@studio-piot/native-ui 0.2.0 → 0.2.3

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 (88) hide show
  1. package/README.md +4 -1
  2. package/dist/components/SegmentedControl/SegmentedControl.d.ts +3 -0
  3. package/dist/components/SegmentedControl/SegmentedControl.d.ts.map +1 -0
  4. package/dist/components/SegmentedControl/SegmentedControl.js +181 -0
  5. package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -0
  6. package/dist/components/SegmentedControl/index.d.ts +4 -0
  7. package/dist/components/SegmentedControl/index.d.ts.map +1 -0
  8. package/dist/components/SegmentedControl/index.js +8 -0
  9. package/dist/components/SegmentedControl/index.js.map +1 -0
  10. package/dist/components/SegmentedControl/segmentIndicatorFrame.d.ts +16 -0
  11. package/dist/components/SegmentedControl/segmentIndicatorFrame.d.ts.map +1 -0
  12. package/dist/components/SegmentedControl/segmentIndicatorFrame.js +27 -0
  13. package/dist/components/SegmentedControl/segmentIndicatorFrame.js.map +1 -0
  14. package/dist/components/SegmentedControl/segmentThemeRoles.d.ts +10 -0
  15. package/dist/components/SegmentedControl/segmentThemeRoles.d.ts.map +1 -0
  16. package/dist/components/SegmentedControl/segmentThemeRoles.js +12 -0
  17. package/dist/components/SegmentedControl/segmentThemeRoles.js.map +1 -0
  18. package/dist/components/SegmentedControl/types.d.ts +23 -0
  19. package/dist/components/SegmentedControl/types.d.ts.map +1 -0
  20. package/dist/components/SegmentedControl/types.js +3 -0
  21. package/dist/components/SegmentedControl/types.js.map +1 -0
  22. package/dist/components/Select/Select.d.ts +3 -1
  23. package/dist/components/Select/Select.d.ts.map +1 -1
  24. package/dist/components/Select/Select.js +72 -28
  25. package/dist/components/Select/Select.js.map +1 -1
  26. package/dist/components/Select/SelectCloseButton.d.ts +9 -3
  27. package/dist/components/Select/SelectCloseButton.d.ts.map +1 -1
  28. package/dist/components/Select/SelectCloseButton.js +2 -5
  29. package/dist/components/Select/SelectCloseButton.js.map +1 -1
  30. package/dist/components/Select/presentSelectSheet.d.ts +7 -0
  31. package/dist/components/Select/presentSelectSheet.d.ts.map +1 -0
  32. package/dist/components/Select/presentSelectSheet.js +14 -0
  33. package/dist/components/Select/presentSelectSheet.js.map +1 -0
  34. package/dist/components/Select/selectThemeRoles.d.ts +1 -1
  35. package/dist/components/Select/selectThemeRoles.js +1 -1
  36. package/dist/components/Select/selectThemeRoles.js.map +1 -1
  37. package/dist/components/Select/types.d.ts +2 -2
  38. package/dist/components/Select/types.d.ts.map +1 -1
  39. package/dist/components/Slider/Slider.d.ts +3 -0
  40. package/dist/components/Slider/Slider.d.ts.map +1 -0
  41. package/dist/components/Slider/Slider.js +46 -0
  42. package/dist/components/Slider/Slider.js.map +1 -0
  43. package/dist/components/Slider/index.d.ts +4 -0
  44. package/dist/components/Slider/index.d.ts.map +1 -0
  45. package/dist/components/Slider/index.js +8 -0
  46. package/dist/components/Slider/index.js.map +1 -0
  47. package/dist/components/Slider/sliderThemeRoles.d.ts +8 -0
  48. package/dist/components/Slider/sliderThemeRoles.d.ts.map +1 -0
  49. package/dist/components/Slider/sliderThemeRoles.js +10 -0
  50. package/dist/components/Slider/sliderThemeRoles.js.map +1 -0
  51. package/dist/components/Slider/types.d.ts +18 -0
  52. package/dist/components/Slider/types.d.ts.map +1 -0
  53. package/dist/components/Slider/types.js +3 -0
  54. package/dist/components/Slider/types.js.map +1 -0
  55. package/dist/components/index.d.ts +2 -0
  56. package/dist/components/index.d.ts.map +1 -1
  57. package/dist/components/index.js +7 -1
  58. package/dist/components/index.js.map +1 -1
  59. package/dist/index.d.ts +3 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +8 -2
  62. package/dist/index.js.map +1 -1
  63. package/docs/components/segmented-control.md +95 -0
  64. package/docs/components/select.md +11 -6
  65. package/docs/components/slider.md +65 -0
  66. package/docs/superpowers/plans/2026-09-25-slider.md +35 -0
  67. package/docs/superpowers/specs/2026-09-25-select-design.md +3 -3
  68. package/docs/superpowers/specs/2026-09-25-slider-design.md +117 -0
  69. package/package.json +6 -2
  70. package/src/components/SegmentedControl/SegmentedControl.tsx +234 -0
  71. package/src/components/SegmentedControl/index.ts +6 -0
  72. package/src/components/SegmentedControl/segmentIndicatorFrame.test.ts +39 -0
  73. package/src/components/SegmentedControl/segmentIndicatorFrame.ts +31 -0
  74. package/src/components/SegmentedControl/segmentThemeRoles.test.ts +17 -0
  75. package/src/components/SegmentedControl/segmentThemeRoles.ts +10 -0
  76. package/src/components/SegmentedControl/types.ts +24 -0
  77. package/src/components/Select/Select.tsx +133 -64
  78. package/src/components/Select/SelectCloseButton.tsx +15 -7
  79. package/src/components/Select/selectThemeRoles.test.ts +2 -2
  80. package/src/components/Select/selectThemeRoles.ts +1 -1
  81. package/src/components/Select/types.ts +2 -2
  82. package/src/components/Slider/Slider.tsx +95 -0
  83. package/src/components/Slider/index.ts +6 -0
  84. package/src/components/Slider/sliderThemeRoles.test.ts +12 -0
  85. package/src/components/Slider/sliderThemeRoles.ts +8 -0
  86. package/src/components/Slider/types.ts +18 -0
  87. package/src/components/index.ts +13 -0
  88. package/src/index.ts +14 -1
@@ -5,26 +5,41 @@ import {
5
5
  type ElementRef,
6
6
  type ReactNode,
7
7
  } from "react";
8
- import { Pressable, StyleSheet, Text, View } from "react-native";
8
+ import {
9
+ Platform,
10
+ Pressable,
11
+ StyleSheet,
12
+ Text,
13
+ useWindowDimensions,
14
+ View,
15
+ } from "react-native";
9
16
  import {
10
17
  BottomSheetBackdrop,
18
+ BottomSheetHandle,
11
19
  BottomSheetModal,
12
20
  BottomSheetScrollView,
13
21
  type BottomSheetBackdropProps,
22
+ type BottomSheetHandleProps,
14
23
  } from "@gorhom/bottom-sheet";
24
+ import { FullWindowOverlay } from "react-native-screens";
15
25
  import { buttonRadiusValue } from "../Button/buttonRadius";
16
26
  import { useResolvedFieldColor } from "../Field/useResolvedFieldColor";
17
- import {
18
- DEFAULT_SELECT_DETENTS,
19
- DEFAULT_SELECT_INITIAL_DETENT,
20
- detentsToSnapPoints,
21
- resolveSelectDetentIndex,
22
- } from "./resolveSelectDetentIndex";
27
+ import { useThemeColor } from "../../theme/useThemeColor";
28
+ import { DEFAULT_SELECT_DETENTS } from "./resolveSelectDetentIndex";
23
29
  import { SelectCloseButton } from "./SelectCloseButton";
24
30
  import { SELECT_THEME_ROLES } from "./selectThemeRoles";
25
31
  import type { SelectOption, SelectProps } from "./types";
26
32
 
27
33
  const BACKDROP_OPACITY = 0.72;
34
+ const SHEET_TOP_RADIUS = 20;
35
+
36
+ /** Renders the sheet above React Navigation native modals (iOS). */
37
+ function SheetContainer({ children }: { children?: ReactNode }) {
38
+ if (Platform.OS === "ios") {
39
+ return <FullWindowOverlay>{children}</FullWindowOverlay>;
40
+ }
41
+ return <>{children}</>;
42
+ }
28
43
 
29
44
  export function Select({
30
45
  value,
@@ -36,8 +51,9 @@ export function Select({
36
51
  error,
37
52
  sheetTitle,
38
53
  closeButton,
54
+ /** Max sheet height as a fraction of the window (largest value wins). Default `[0.25, 0.5, 0.75]` → 75%. */
39
55
  detents = [...DEFAULT_SELECT_DETENTS],
40
- initialDetent = DEFAULT_SELECT_INITIAL_DETENT,
56
+ initialDetent: _initialDetent,
41
57
  radius = "xl",
42
58
  editable = true,
43
59
  backgroundColor: backgroundColorProp,
@@ -51,6 +67,7 @@ export function Select({
51
67
  style,
52
68
  }: SelectProps) {
53
69
  const modalRef = useRef<ElementRef<typeof BottomSheetModal>>(null);
70
+ const { height: windowHeight } = useWindowDimensions();
54
71
 
55
72
  const backgroundColor = useResolvedFieldColor(
56
73
  backgroundColorProp,
@@ -84,6 +101,9 @@ export function Select({
84
101
  selectedColorProp,
85
102
  SELECT_THEME_ROLES.selectedColor,
86
103
  );
104
+ // Resolved on the trigger tree (has ThemeProvider), then passed into the portal.
105
+ const closeDiscColor = useThemeColor("text");
106
+ const closeIconColor = useThemeColor("background");
87
107
 
88
108
  const hasError = Boolean(error);
89
109
  const selected = options.find((option) => option.value === value);
@@ -91,8 +111,11 @@ export function Select({
91
111
  const isPlaceholder = selected == null;
92
112
  const borderRadius = buttonRadiusValue(radius);
93
113
  const title = sheetTitle ?? label ?? "Select";
94
- const presentIndex = resolveSelectDetentIndex(detents, initialDetent);
95
- const snapPoints = useMemo(() => detentsToSnapPoints(detents), [detents]);
114
+
115
+ const maxDynamicContentSize = useMemo(() => {
116
+ const maxDetent = Math.max(...detents, 0.75);
117
+ return Math.round(windowHeight * maxDetent);
118
+ }, [detents, windowHeight]);
96
119
 
97
120
  const dismiss = useCallback(() => {
98
121
  modalRef.current?.dismiss();
@@ -119,7 +142,52 @@ export function Select({
119
142
  const resolvedClose: ReactNode =
120
143
  typeof closeButton === "function"
121
144
  ? closeButton(dismiss)
122
- : (closeButton ?? <SelectCloseButton onPress={dismiss} />);
145
+ : (closeButton ?? (
146
+ <SelectCloseButton
147
+ backgroundColor={closeDiscColor}
148
+ iconColor={closeIconColor}
149
+ onPress={dismiss}
150
+ />
151
+ ));
152
+
153
+ // Header lives in the handle so it stays fixed; only options scroll.
154
+ const renderHandle = useCallback(
155
+ (props: BottomSheetHandleProps) => (
156
+ <View
157
+ style={[
158
+ styles.handleRoot,
159
+ {
160
+ backgroundColor,
161
+ borderTopLeftRadius: SHEET_TOP_RADIUS,
162
+ borderTopRightRadius: SHEET_TOP_RADIUS,
163
+ },
164
+ ]}
165
+ >
166
+ <BottomSheetHandle
167
+ {...props}
168
+ indicatorStyle={{
169
+ backgroundColor: placeholderColor,
170
+ opacity: 0.9,
171
+ }}
172
+ />
173
+ <View style={styles.header}>
174
+ <Text style={[styles.sheetTitle, { color: textColor, flex: 1 }]}>
175
+ {title}
176
+ </Text>
177
+ {resolvedClose}
178
+ </View>
179
+ <View style={[styles.headerDivider, { backgroundColor: borderColor }]} />
180
+ </View>
181
+ ),
182
+ [
183
+ backgroundColor,
184
+ borderColor,
185
+ placeholderColor,
186
+ resolvedClose,
187
+ textColor,
188
+ title,
189
+ ],
190
+ );
123
191
 
124
192
  const onSelect = (option: SelectOption) => {
125
193
  if (option.disabled) return;
@@ -176,67 +244,62 @@ export function Select({
176
244
  <BottomSheetModal
177
245
  ref={modalRef}
178
246
  backdropComponent={renderBackdrop}
179
- backgroundStyle={{ backgroundColor }}
180
- enablePanDownToClose
181
- handleIndicatorStyle={{
182
- backgroundColor: placeholderColor,
183
- opacity: 0.9,
247
+ backgroundStyle={{
248
+ backgroundColor,
249
+ borderTopLeftRadius: SHEET_TOP_RADIUS,
250
+ borderTopRightRadius: SHEET_TOP_RADIUS,
184
251
  }}
185
- index={presentIndex}
186
- snapPoints={snapPoints}
252
+ containerComponent={SheetContainer}
253
+ enableDynamicSizing
254
+ enablePanDownToClose
255
+ handleComponent={renderHandle}
256
+ handleStyle={styles.handleShell}
257
+ maxDynamicContentSize={maxDynamicContentSize}
258
+ stackBehavior="push"
187
259
  >
188
- <BottomSheetScrollView contentContainerStyle={styles.sheetContent}>
189
- <View style={styles.header}>
190
- <Text style={[styles.sheetTitle, { color: textColor, flex: 1 }]}>
191
- {title}
192
- </Text>
193
- {resolvedClose}
194
- </View>
260
+ <BottomSheetScrollView
261
+ contentContainerStyle={styles.sheetContent}
262
+ enableFooterMarginAdjustment={false}
263
+ >
195
264
  {options.length === 0 ? (
196
265
  <Text style={[styles.empty, { color: placeholderColor }]}>
197
266
  No options
198
267
  </Text>
199
268
  ) : (
200
- options.map((option, index) => {
269
+ options.map((option) => {
201
270
  const isSelected = option.value === value;
202
271
  return (
203
- <View key={option.value}>
204
- {index > 0 ? (
205
- <View
206
- style={[styles.divider, { backgroundColor: borderColor }]}
207
- />
208
- ) : null}
209
- <Pressable
210
- accessibilityRole="button"
211
- accessibilityState={{
212
- selected: isSelected,
213
- disabled: !!option.disabled,
214
- }}
215
- disabled={option.disabled}
216
- onPress={() => onSelect(option)}
272
+ <Pressable
273
+ key={option.value}
274
+ accessibilityRole="button"
275
+ accessibilityState={{
276
+ selected: isSelected,
277
+ disabled: !!option.disabled,
278
+ }}
279
+ disabled={option.disabled}
280
+ onPress={() => onSelect(option)}
281
+ style={[
282
+ styles.option,
283
+ option.disabled && styles.optionBlocked,
284
+ ]}
285
+ >
286
+ <Text
217
287
  style={[
218
- styles.option,
219
- option.disabled && styles.optionBlocked,
288
+ styles.optionLabel,
289
+ {
290
+ color: isSelected ? selectedColor : textColor,
291
+ flex: 1,
292
+ },
220
293
  ]}
221
294
  >
222
- <Text
223
- style={[
224
- styles.optionLabel,
225
- {
226
- color: isSelected ? selectedColor : textColor,
227
- flex: 1,
228
- },
229
- ]}
230
- >
231
- {option.label}
295
+ {option.label}
296
+ </Text>
297
+ {isSelected ? (
298
+ <Text style={[styles.check, { color: selectedColor }]}>
299
+ ✓
232
300
  </Text>
233
- {isSelected ? (
234
- <Text style={[styles.check, { color: selectedColor }]}>
235
- ✓
236
- </Text>
237
- ) : null}
238
- </Pressable>
239
- </View>
301
+ ) : null}
302
+ </Pressable>
240
303
  );
241
304
  })
242
305
  )}
@@ -275,10 +338,17 @@ const styles = StyleSheet.create({
275
338
  fontSize: 22,
276
339
  fontWeight: "300",
277
340
  lineHeight: 24,
341
+ transform: [{ rotate: "90deg" }],
278
342
  },
279
343
  helper: {
280
344
  fontSize: 13,
281
345
  },
346
+ handleShell: {
347
+ paddingBottom: 0,
348
+ },
349
+ handleRoot: {
350
+ overflow: "hidden",
351
+ },
282
352
  sheetContent: {
283
353
  paddingBottom: 24,
284
354
  },
@@ -287,9 +357,12 @@ const styles = StyleSheet.create({
287
357
  alignItems: "center",
288
358
  gap: 12,
289
359
  paddingHorizontal: 16,
290
- paddingTop: 8,
360
+ paddingTop: 4,
291
361
  paddingBottom: 12,
292
362
  },
363
+ headerDivider: {
364
+ height: StyleSheet.hairlineWidth,
365
+ },
293
366
  sheetTitle: {
294
367
  fontSize: 17,
295
368
  fontWeight: "600",
@@ -316,8 +389,4 @@ const styles = StyleSheet.create({
316
389
  fontSize: 17,
317
390
  fontWeight: "600",
318
391
  },
319
- divider: {
320
- height: StyleSheet.hairlineWidth,
321
- marginHorizontal: 16,
322
- },
323
392
  });
@@ -1,20 +1,28 @@
1
1
  import { Pressable, StyleSheet, Text } from "react-native";
2
- import { useThemeColor } from "../../theme/useThemeColor";
3
2
 
4
- /** Default Select sheet close: filled disc + contrasting cross. */
5
- export function SelectCloseButton({ onPress }: { onPress: () => void }) {
6
- const disc = useThemeColor("text");
7
- const cross = useThemeColor("background");
3
+ type SelectCloseButtonProps = {
4
+ onPress: () => void;
5
+ /** Disc fill. Select passes theme colours so this stays portal-safe. */
6
+ backgroundColor?: string;
7
+ /** Cross colour. */
8
+ iconColor?: string;
9
+ };
8
10
 
11
+ /** Default Select sheet close: filled disc + contrasting cross. */
12
+ export function SelectCloseButton({
13
+ onPress,
14
+ backgroundColor = "#111111",
15
+ iconColor = "#FFFFFF",
16
+ }: SelectCloseButtonProps) {
9
17
  return (
10
18
  <Pressable
11
19
  accessibilityLabel="Close"
12
20
  accessibilityRole="button"
13
21
  hitSlop={8}
14
22
  onPress={onPress}
15
- style={[styles.disc, { backgroundColor: disc }]}
23
+ style={[styles.disc, { backgroundColor }]}
16
24
  >
17
- <Text style={[styles.discX, { color: cross }]}>×</Text>
25
+ <Text style={[styles.discX, { color: iconColor }]}>×</Text>
18
26
  </Pressable>
19
27
  );
20
28
  }
@@ -3,7 +3,7 @@ import { describe, it } from "node:test";
3
3
  import { SELECT_THEME_ROLES } from "./selectThemeRoles.ts";
4
4
 
5
5
  describe("SELECT_THEME_ROLES", () => {
6
- it("maps colour props to Field roles plus fieldRadioSelected", () => {
6
+ it("maps colour props to Field roles plus fieldSelectSelected", () => {
7
7
  assert.equal(SELECT_THEME_ROLES.backgroundColor, "fieldBackground");
8
8
  assert.equal(SELECT_THEME_ROLES.textColor, "fieldText");
9
9
  assert.equal(SELECT_THEME_ROLES.placeholderColor, "fieldPlaceholder");
@@ -11,6 +11,6 @@ describe("SELECT_THEME_ROLES", () => {
11
11
  assert.equal(SELECT_THEME_ROLES.descriptionColor, "fieldDescription");
12
12
  assert.equal(SELECT_THEME_ROLES.borderColor, "fieldBorder");
13
13
  assert.equal(SELECT_THEME_ROLES.errorColor, "fieldError");
14
- assert.equal(SELECT_THEME_ROLES.selectedColor, "fieldRadioSelected");
14
+ assert.equal(SELECT_THEME_ROLES.selectedColor, "fieldSelectSelected");
15
15
  });
16
16
  });
@@ -6,7 +6,7 @@ export const SELECT_THEME_ROLES = {
6
6
  descriptionColor: "fieldDescription",
7
7
  borderColor: "fieldBorder",
8
8
  errorColor: "fieldError",
9
- selectedColor: "fieldRadioSelected",
9
+ selectedColor: "fieldSelectSelected",
10
10
  } as const;
11
11
 
12
12
  export type SelectThemeColorProp = keyof typeof SELECT_THEME_ROLES;
@@ -23,9 +23,9 @@ export type SelectProps = {
23
23
  sheetTitle?: string;
24
24
  /** DiscClose by default. Pass `(dismiss) => <YourClose onPress={dismiss} />` to override. */
25
25
  closeButton?: SelectCloseButton;
26
- /** Detent fractions 0–1; mapped to Gorhom snap `%`. Default `[0.25, 0.5, 0.75]`. */
26
+ /** Detent fractions 0–1. Largest value caps sheet height (content-sized up to that max). Default `[0.25, 0.5, 0.75]`. */
27
27
  detents?: number[];
28
- /** Which detent to open at. Default `0.5`. */
28
+ /** @deprecated Ignored — sheet opens at content height (capped by `detents`). */
29
29
  initialDetent?: number;
30
30
  radius?: ButtonRadius;
31
31
  editable?: boolean;
@@ -0,0 +1,95 @@
1
+ import { StyleSheet, Text, View } from "react-native";
2
+ import CommunitySlider from "@react-native-community/slider";
3
+ import { useResolvedFieldColor } from "../Field/useResolvedFieldColor";
4
+ import { SLIDER_THEME_ROLES } from "./sliderThemeRoles";
5
+ import type { SliderProps } from "./types";
6
+
7
+ export function Slider({
8
+ value,
9
+ onChange,
10
+ minimumValue,
11
+ maximumValue,
12
+ step,
13
+ label,
14
+ formatValue = String,
15
+ disabled = false,
16
+ labelColor: labelColorProp,
17
+ valueColor: valueColorProp,
18
+ highlightColor: highlightColorProp,
19
+ trackColor: trackColorProp,
20
+ style,
21
+ }: SliderProps) {
22
+ const labelColor = useResolvedFieldColor(
23
+ labelColorProp,
24
+ SLIDER_THEME_ROLES.labelColor,
25
+ );
26
+ const valueColor = useResolvedFieldColor(
27
+ valueColorProp,
28
+ SLIDER_THEME_ROLES.valueColor,
29
+ );
30
+ const highlightColor = useResolvedFieldColor(
31
+ highlightColorProp,
32
+ SLIDER_THEME_ROLES.highlightColor,
33
+ );
34
+ const trackColor = useResolvedFieldColor(
35
+ trackColorProp,
36
+ SLIDER_THEME_ROLES.trackColor,
37
+ );
38
+
39
+ const display = formatValue(value);
40
+ const accessibilityLabel = label ? `${label}. ${display}` : display;
41
+
42
+ return (
43
+ <View style={[styles.column, disabled && styles.blocked, style]}>
44
+ <View style={styles.header}>
45
+ {label ? (
46
+ <Text style={[styles.label, { color: labelColor, flex: 1 }]}>
47
+ {label}
48
+ </Text>
49
+ ) : (
50
+ <View style={{ flex: 1 }} />
51
+ )}
52
+ <Text style={[styles.value, { color: valueColor }]}>{display}</Text>
53
+ </View>
54
+ <CommunitySlider
55
+ accessibilityLabel={accessibilityLabel}
56
+ disabled={disabled}
57
+ maximumTrackTintColor={trackColor}
58
+ maximumValue={maximumValue}
59
+ minimumTrackTintColor={highlightColor}
60
+ minimumValue={minimumValue}
61
+ onValueChange={onChange}
62
+ step={step}
63
+ style={styles.track}
64
+ thumbTintColor={highlightColor}
65
+ value={value}
66
+ />
67
+ </View>
68
+ );
69
+ }
70
+
71
+ const styles = StyleSheet.create({
72
+ column: {
73
+ width: "100%",
74
+ gap: 6,
75
+ },
76
+ blocked: {
77
+ opacity: 0.4,
78
+ },
79
+ header: {
80
+ flexDirection: "row",
81
+ alignItems: "center",
82
+ gap: 12,
83
+ },
84
+ label: {
85
+ fontSize: 13,
86
+ fontWeight: "600",
87
+ },
88
+ value: {
89
+ fontSize: 17,
90
+ },
91
+ track: {
92
+ width: "100%",
93
+ height: 40,
94
+ },
95
+ });
@@ -0,0 +1,6 @@
1
+ export { Slider } from "./Slider";
2
+ export {
3
+ SLIDER_THEME_ROLES,
4
+ type SliderThemeColorProp,
5
+ } from "./sliderThemeRoles";
6
+ export type { SliderProps } from "./types";
@@ -0,0 +1,12 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import { SLIDER_THEME_ROLES } from "./sliderThemeRoles.ts";
4
+
5
+ describe("SLIDER_THEME_ROLES", () => {
6
+ it("maps colour props to Field roles plus fieldSliderHighlight", () => {
7
+ assert.equal(SLIDER_THEME_ROLES.labelColor, "fieldLabel");
8
+ assert.equal(SLIDER_THEME_ROLES.valueColor, "fieldText");
9
+ assert.equal(SLIDER_THEME_ROLES.highlightColor, "fieldSliderHighlight");
10
+ assert.equal(SLIDER_THEME_ROLES.trackColor, "fieldBorder");
11
+ });
12
+ });
@@ -0,0 +1,8 @@
1
+ export const SLIDER_THEME_ROLES = {
2
+ labelColor: "fieldLabel",
3
+ valueColor: "fieldText",
4
+ highlightColor: "fieldSliderHighlight",
5
+ trackColor: "fieldBorder",
6
+ } as const;
7
+
8
+ export type SliderThemeColorProp = keyof typeof SLIDER_THEME_ROLES;
@@ -0,0 +1,18 @@
1
+ import type { StyleProp, ViewStyle } from "react-native";
2
+
3
+ export type SliderProps = {
4
+ value: number;
5
+ onChange: (value: number) => void;
6
+ minimumValue: number;
7
+ maximumValue: number;
8
+ step?: number;
9
+ label?: string;
10
+ /** Defaults to `String(value)`. */
11
+ formatValue?: (value: number) => string;
12
+ disabled?: boolean;
13
+ labelColor?: string;
14
+ valueColor?: string;
15
+ highlightColor?: string;
16
+ trackColor?: string;
17
+ style?: StyleProp<ViewStyle>;
18
+ };
@@ -41,3 +41,16 @@ export {
41
41
  type SelectOption,
42
42
  type SelectProps,
43
43
  } from "./Select";
44
+ export {
45
+ SegmentedControl,
46
+ SEGMENTED_THEME_ROLES,
47
+ type SegmentedThemeColorProp,
48
+ type SegmentedOption,
49
+ type SegmentedControlProps,
50
+ } from "./SegmentedControl";
51
+ export {
52
+ Slider,
53
+ SLIDER_THEME_ROLES,
54
+ type SliderThemeColorProp,
55
+ type SliderProps,
56
+ } from "./Slider";
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export const version = "0.2.0";
1
+ export const version = "0.2.3";
2
2
 
3
3
  export * from "./theme";
4
4
  export {
@@ -48,4 +48,17 @@ export {
48
48
  type SelectOption,
49
49
  type SelectProps,
50
50
  } from "./components/Select";
51
+ export {
52
+ SegmentedControl,
53
+ SEGMENTED_THEME_ROLES,
54
+ type SegmentedThemeColorProp,
55
+ type SegmentedOption,
56
+ type SegmentedControlProps,
57
+ } from "./components/SegmentedControl";
58
+ export {
59
+ Slider,
60
+ SLIDER_THEME_ROLES,
61
+ type SliderThemeColorProp,
62
+ type SliderProps,
63
+ } from "./components/Slider";
51
64
  export * as components from "./components";