react-native-platform-components 0.0.2

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.
Files changed (63) hide show
  1. package/LICENSE +20 -0
  2. package/PlatformComponents.podspec +20 -0
  3. package/README.md +233 -0
  4. package/android/build.gradle +78 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/com/platformcomponents/DateConstraints.kt +83 -0
  8. package/android/src/main/java/com/platformcomponents/Helper.kt +27 -0
  9. package/android/src/main/java/com/platformcomponents/PCDatePickerView.kt +684 -0
  10. package/android/src/main/java/com/platformcomponents/PCDatePickerViewManager.kt +149 -0
  11. package/android/src/main/java/com/platformcomponents/PCMaterialMode.kt +16 -0
  12. package/android/src/main/java/com/platformcomponents/PCSelectionMenuView.kt +410 -0
  13. package/android/src/main/java/com/platformcomponents/PCSelectionMenuViewManager.kt +114 -0
  14. package/android/src/main/java/com/platformcomponents/PlatformComponentsPackage.kt +22 -0
  15. package/ios/PCDatePicker.h +11 -0
  16. package/ios/PCDatePicker.mm +248 -0
  17. package/ios/PCDatePickerView.swift +405 -0
  18. package/ios/PCSelectionMenu.h +10 -0
  19. package/ios/PCSelectionMenu.mm +182 -0
  20. package/ios/PCSelectionMenu.swift +434 -0
  21. package/lib/module/DatePicker.js +74 -0
  22. package/lib/module/DatePicker.js.map +1 -0
  23. package/lib/module/DatePickerNativeComponent.ts +68 -0
  24. package/lib/module/SelectionMenu.js +79 -0
  25. package/lib/module/SelectionMenu.js.map +1 -0
  26. package/lib/module/SelectionMenu.web.js +57 -0
  27. package/lib/module/SelectionMenu.web.js.map +1 -0
  28. package/lib/module/SelectionMenuNativeComponent.ts +106 -0
  29. package/lib/module/index.js +6 -0
  30. package/lib/module/index.js.map +1 -0
  31. package/lib/module/package.json +1 -0
  32. package/lib/module/sharedTypes.js +4 -0
  33. package/lib/module/sharedTypes.js.map +1 -0
  34. package/lib/typescript/package.json +1 -0
  35. package/lib/typescript/src/DatePicker.d.ts +38 -0
  36. package/lib/typescript/src/DatePicker.d.ts.map +1 -0
  37. package/lib/typescript/src/DatePickerNativeComponent.d.ts +53 -0
  38. package/lib/typescript/src/DatePickerNativeComponent.d.ts.map +1 -0
  39. package/lib/typescript/src/SelectionMenu.d.ts +50 -0
  40. package/lib/typescript/src/SelectionMenu.d.ts.map +1 -0
  41. package/lib/typescript/src/SelectionMenu.web.d.ts +19 -0
  42. package/lib/typescript/src/SelectionMenu.web.d.ts.map +1 -0
  43. package/lib/typescript/src/SelectionMenuNativeComponent.d.ts +85 -0
  44. package/lib/typescript/src/SelectionMenuNativeComponent.d.ts.map +1 -0
  45. package/lib/typescript/src/index.d.ts +4 -0
  46. package/lib/typescript/src/index.d.ts.map +1 -0
  47. package/lib/typescript/src/sharedTypes.d.ts +10 -0
  48. package/lib/typescript/src/sharedTypes.d.ts.map +1 -0
  49. package/package.json +178 -0
  50. package/shared/PCDatePickerComponentDescriptors-custom.h +52 -0
  51. package/shared/PCDatePickerShadowNode-custom.cpp +1 -0
  52. package/shared/PCDatePickerShadowNode-custom.h +27 -0
  53. package/shared/PCDatePickerState-custom.h +13 -0
  54. package/shared/PCSelectionMenuComponentDescriptors-custom.h +25 -0
  55. package/shared/PCSelectionMenuShadowNode-custom.cpp +36 -0
  56. package/shared/PCSelectionMenuShadowNode-custom.h +46 -0
  57. package/src/DatePicker.tsx +146 -0
  58. package/src/DatePickerNativeComponent.ts +68 -0
  59. package/src/SelectionMenu.tsx +170 -0
  60. package/src/SelectionMenu.web.tsx +93 -0
  61. package/src/SelectionMenuNativeComponent.ts +106 -0
  62. package/src/index.tsx +3 -0
  63. package/src/sharedTypes.ts +14 -0
@@ -0,0 +1,170 @@
1
+ // SelectionMenu.tsx
2
+ import React, { useCallback, useMemo } from 'react';
3
+ import {
4
+ Platform,
5
+ StyleSheet,
6
+ type StyleProp,
7
+ type ViewStyle,
8
+ } from 'react-native';
9
+
10
+ import NativeSelectionMenu, {
11
+ type SelectionMenuOption,
12
+ type SelectionMenuSelectEvent,
13
+ type SelectionMenuPresentation,
14
+ } from './SelectionMenuNativeComponent';
15
+
16
+ import type { AndroidMaterialMode } from './sharedTypes';
17
+
18
+ export type SelectionMenuProps = {
19
+ style?: StyleProp<ViewStyle>;
20
+
21
+ /** Options are label + data (payload) */
22
+ options: readonly SelectionMenuOption[];
23
+
24
+ /**
25
+ * Controlled selection by the option's `data`.
26
+ * Use `null` for "no selection".
27
+ */
28
+ selected: string | null;
29
+
30
+ disabled?: boolean;
31
+ placeholder?: string;
32
+
33
+ /**
34
+ * If true, native renders its own inline anchor and manages open/close internally.
35
+ * If false (default), component is headless and controlled by `visible`.
36
+ */
37
+ inlineMode?: boolean;
38
+
39
+ /**
40
+ * Headless mode only (inlineMode === false):
41
+ * controls whether the native menu UI is presented.
42
+ */
43
+ visible?: boolean;
44
+
45
+ /**
46
+ * Headless mode only (inlineMode === false):
47
+ * presentation hint ("auto" | "popover" | "sheet").
48
+ */
49
+ presentation?: SelectionMenuPresentation;
50
+
51
+ /**
52
+ * Called when the user selects an option.
53
+ * Receives the selected `data` payload, plus label/index for convenience.
54
+ */
55
+ onSelect?: (data: string, label: string, index: number) => void;
56
+
57
+ /**
58
+ * Called when the user dismisses without selecting.
59
+ */
60
+ onRequestClose?: () => void;
61
+
62
+ /**
63
+ * Pass-through platform props.
64
+ */
65
+ ios?: {};
66
+
67
+ android?: {
68
+ /** Material preference ("auto" | "m2" | "m3"). */
69
+ material?: AndroidMaterialMode;
70
+ };
71
+ };
72
+
73
+ function normalizeSelectedData(selected: string | null): string {
74
+ return selected ?? '';
75
+ }
76
+
77
+ function normalizeNativeVisible(
78
+ inlineMode: boolean | undefined,
79
+ visible: boolean | undefined
80
+ ): 'open' | 'closed' | undefined {
81
+ // Inline mode ignores visible; keep it undefined so native isn't spammed.
82
+ if (inlineMode) return undefined;
83
+ return visible ? 'open' : 'closed';
84
+ }
85
+
86
+ function normalizeNativePresentation(
87
+ inlineMode: boolean | undefined,
88
+ presentation: SelectionMenuPresentation | undefined
89
+ ): SelectionMenuPresentation | undefined {
90
+ // Inline mode ignores presentation.
91
+ if (inlineMode) return undefined;
92
+ return presentation ?? 'auto';
93
+ }
94
+
95
+ export function SelectionMenu(props: SelectionMenuProps): React.ReactElement {
96
+ const {
97
+ style,
98
+ options,
99
+ selected,
100
+ disabled,
101
+ placeholder,
102
+ inlineMode,
103
+ visible,
104
+ presentation,
105
+ onSelect,
106
+ onRequestClose,
107
+ ios,
108
+ android,
109
+ } = props;
110
+
111
+ const selectedData = useMemo(
112
+ () => normalizeSelectedData(selected),
113
+ [selected]
114
+ );
115
+
116
+ const nativeVisible = useMemo(
117
+ () => normalizeNativeVisible(inlineMode, visible),
118
+ [inlineMode, visible]
119
+ );
120
+
121
+ const nativePresentation = useMemo(
122
+ () => normalizeNativePresentation(inlineMode, presentation),
123
+ [inlineMode, presentation]
124
+ );
125
+
126
+ const handleSelect = useCallback(
127
+ (e: { nativeEvent: SelectionMenuSelectEvent }) => {
128
+ const { index, label, data } = e.nativeEvent;
129
+ onSelect?.(data, label, index);
130
+ },
131
+ [onSelect]
132
+ );
133
+
134
+ const handleRequestClose = useCallback(() => {
135
+ onRequestClose?.();
136
+ }, [onRequestClose]);
137
+
138
+ // Keep android prop stable and codegen-friendly (string unions live in native spec).
139
+ const nativeAndroid = useMemo(() => {
140
+ if (!android) return undefined;
141
+ return { material: android.material };
142
+ }, [android]);
143
+
144
+ const isAndroidM3Inline =
145
+ android?.material &&
146
+ inlineMode &&
147
+ android.material === 'm3' &&
148
+ Platform.OS === 'android';
149
+
150
+ return (
151
+ <NativeSelectionMenu
152
+ style={[style, isAndroidM3Inline && styles.androidInline]}
153
+ options={options}
154
+ selectedData={selectedData}
155
+ interactivity={disabled ? 'disabled' : 'enabled'}
156
+ placeholder={placeholder}
157
+ anchorMode={inlineMode ? 'inline' : 'headless'}
158
+ visible={nativeVisible}
159
+ presentation={nativePresentation}
160
+ onSelect={onSelect ? handleSelect : undefined}
161
+ onRequestClose={onRequestClose ? handleRequestClose : undefined}
162
+ ios={ios}
163
+ android={nativeAndroid}
164
+ />
165
+ );
166
+ }
167
+
168
+ const styles = StyleSheet.create({
169
+ androidInline: { minHeight: 60 },
170
+ });
@@ -0,0 +1,93 @@
1
+ // SelectionMenu.web.tsx
2
+ import React, { useMemo } from 'react';
3
+ import type { StyleProp, ViewStyle } from 'react-native';
4
+
5
+ import type {
6
+ SelectionMenuOption,
7
+ SelectionMenuPresentation,
8
+ } from './SelectionMenuNativeComponent';
9
+
10
+ export type SelectionMenuProps = {
11
+ style?: StyleProp<ViewStyle>;
12
+
13
+ options: readonly SelectionMenuOption[];
14
+ selected: string | null;
15
+
16
+ disabled?: boolean;
17
+ placeholder?: string;
18
+
19
+ // kept for parity; ignored by <select>
20
+ inlineMode?: boolean;
21
+ visible?: boolean;
22
+ presentation?: SelectionMenuPresentation;
23
+
24
+ onSelect?: (data: string, label: string, index: number) => void;
25
+ onRequestClose?: () => void;
26
+
27
+ ios?: {};
28
+ android?: {};
29
+ };
30
+
31
+ function toCssStyle(
32
+ style: StyleProp<ViewStyle>
33
+ ): React.CSSProperties | undefined {
34
+ if (!style) return undefined;
35
+ if (Array.isArray(style)) {
36
+ const merged: any = {};
37
+ for (const s of style) {
38
+ if (s && typeof s === 'object') Object.assign(merged, s as any);
39
+ }
40
+ return merged as React.CSSProperties;
41
+ }
42
+ if (typeof style === 'object') return style as any;
43
+ return undefined;
44
+ }
45
+
46
+ export function SelectionMenu(props: SelectionMenuProps): React.ReactElement {
47
+ const {
48
+ style,
49
+ options,
50
+ selected,
51
+ disabled,
52
+ placeholder,
53
+ onSelect,
54
+ onRequestClose,
55
+ } = props;
56
+
57
+ const cssStyle = useMemo(() => toCssStyle(style), [style]);
58
+ const value = selected ?? '';
59
+
60
+ return (
61
+ <select
62
+ style={cssStyle}
63
+ value={value}
64
+ disabled={!!disabled}
65
+ onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
66
+ const data = (e.currentTarget as any).value as string;
67
+
68
+ // placeholder chosen (or no selection)
69
+ if (data === '') return;
70
+
71
+ const index = options.findIndex((o) => o.data === data);
72
+ const opt = index >= 0 ? options[index] : undefined;
73
+
74
+ onSelect?.(data, opt?.label ?? '', index);
75
+ }}
76
+ onBlur={() => {
77
+ onRequestClose?.();
78
+ }}
79
+ >
80
+ {placeholder ? (
81
+ <option value="" disabled hidden>
82
+ {placeholder}
83
+ </option>
84
+ ) : null}
85
+
86
+ {options.map((opt, idx) => (
87
+ <option key={`${opt.data}-${idx}`} value={opt.data}>
88
+ {opt.label}
89
+ </option>
90
+ ))}
91
+ </select>
92
+ );
93
+ }
@@ -0,0 +1,106 @@
1
+ // SelectionMenuNativeComponent.ts
2
+ import type { CodegenTypes, ViewProps } from 'react-native';
3
+ import { codegenNativeComponent } from 'react-native';
4
+
5
+ /**
6
+ * A single option in the menu.
7
+ */
8
+ export type SelectionMenuOption = Readonly<{
9
+ label: string;
10
+ data: string;
11
+ }>;
12
+
13
+ /**
14
+ * Event emitted when the user selects an option.
15
+ */
16
+ export type SelectionMenuSelectEvent = Readonly<{
17
+ /** Selected option index (implementation detail; stable for this render) */
18
+ index: CodegenTypes.Int32;
19
+
20
+ /** Selected option label */
21
+ label: string;
22
+
23
+ /** Selected option data payload (source of truth) */
24
+ data: string;
25
+ }>;
26
+
27
+ /** Visibility state (headless mode only). */
28
+ export type SelectionMenuVisible = 'open' | 'closed';
29
+
30
+ /** Presentation hint (headless mode only). */
31
+ export type SelectionMenuPresentation = 'auto' | 'popover' | 'sheet';
32
+
33
+ /** Interactivity state (no booleans). */
34
+ export type SelectionMenuInteractivity = 'enabled' | 'disabled';
35
+
36
+ /** Anchor behavior (no booleans). */
37
+ export type SelectionMenuAnchorMode = 'inline' | 'headless';
38
+
39
+ /**
40
+ * iOS-specific configuration (reserved).
41
+ */
42
+ export type IOSProps = Readonly<{}>;
43
+
44
+ /**
45
+ * Android-specific configuration.
46
+ */
47
+ export type AndroidProps = Readonly<{
48
+ material?: string; // AndroidMaterialMode
49
+ }>;
50
+
51
+ export interface SelectionMenuProps extends ViewProps {
52
+ /**
53
+ * Menu options.
54
+ */
55
+ options: ReadonlyArray<SelectionMenuOption>;
56
+
57
+ /**
58
+ * Controlled selection by `data`.
59
+ *
60
+ * - Empty string means "no selection".
61
+ * - Native should treat this as the single source of truth.
62
+ */
63
+ selectedData?: CodegenTypes.WithDefault<string, ''>;
64
+
65
+ /**
66
+ * Enabled / disabled state.
67
+ */
68
+ interactivity?: string; // SelectionMenuInteractivity
69
+
70
+ /**
71
+ * Placeholder text shown when selectedData === "".
72
+ */
73
+ placeholder?: string;
74
+
75
+ /**
76
+ * Inline vs headless behavior.
77
+ */
78
+ anchorMode?: string; // SelectionMenuAnchorMode
79
+
80
+ /**
81
+ * Headless mode only:
82
+ * controls visibility.
83
+ */
84
+ visible?: string; // SelectionMenuVisible
85
+
86
+ /**
87
+ * Headless mode only:
88
+ * presentation hint.
89
+ */
90
+ presentation?: string; // SelectionMenuPresentation
91
+
92
+ /**
93
+ * Fired when the user selects an option.
94
+ */
95
+ onSelect?: CodegenTypes.BubblingEventHandler<SelectionMenuSelectEvent>;
96
+
97
+ /**
98
+ * Fired when dismissed without selection.
99
+ */
100
+ onRequestClose?: CodegenTypes.BubblingEventHandler<Readonly<{}>>;
101
+
102
+ ios?: IOSProps;
103
+ android?: AndroidProps;
104
+ }
105
+
106
+ export default codegenNativeComponent<SelectionMenuProps>('PCSelectionMenu');
package/src/index.tsx ADDED
@@ -0,0 +1,3 @@
1
+ export * from './DatePicker';
2
+ export * from './SelectionMenu';
3
+ export * from './sharedTypes';
@@ -0,0 +1,14 @@
1
+ // SharedTypes.ts
2
+ import type { CodegenTypes } from 'react-native';
3
+
4
+ /** Shared “open/closed” control state. */
5
+ export type Visible = 'open' | 'closed';
6
+
7
+ /** Shared Material preference (Android). */
8
+ export type AndroidMaterialMode = 'system' | 'm3';
9
+
10
+ /** Common event empty payload type. */
11
+ export type EmptyEvent = Readonly<{}>;
12
+
13
+ /** Convenience alias (optional). */
14
+ export type Bubbling<T> = CodegenTypes.BubblingEventHandler<T>;