@studio-piot/native-ui 0.2.0 → 0.2.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 (60) hide show
  1. package/README.md +3 -1
  2. package/dist/components/Select/Select.d.ts +3 -1
  3. package/dist/components/Select/Select.d.ts.map +1 -1
  4. package/dist/components/Select/Select.js +72 -28
  5. package/dist/components/Select/Select.js.map +1 -1
  6. package/dist/components/Select/SelectCloseButton.d.ts +9 -3
  7. package/dist/components/Select/SelectCloseButton.d.ts.map +1 -1
  8. package/dist/components/Select/SelectCloseButton.js +2 -5
  9. package/dist/components/Select/SelectCloseButton.js.map +1 -1
  10. package/dist/components/Select/presentSelectSheet.d.ts +7 -0
  11. package/dist/components/Select/presentSelectSheet.d.ts.map +1 -0
  12. package/dist/components/Select/presentSelectSheet.js +14 -0
  13. package/dist/components/Select/presentSelectSheet.js.map +1 -0
  14. package/dist/components/Select/selectThemeRoles.d.ts +1 -1
  15. package/dist/components/Select/selectThemeRoles.js +1 -1
  16. package/dist/components/Select/selectThemeRoles.js.map +1 -1
  17. package/dist/components/Select/types.d.ts +2 -2
  18. package/dist/components/Select/types.d.ts.map +1 -1
  19. package/dist/components/Slider/Slider.d.ts +3 -0
  20. package/dist/components/Slider/Slider.d.ts.map +1 -0
  21. package/dist/components/Slider/Slider.js +46 -0
  22. package/dist/components/Slider/Slider.js.map +1 -0
  23. package/dist/components/Slider/index.d.ts +4 -0
  24. package/dist/components/Slider/index.d.ts.map +1 -0
  25. package/dist/components/Slider/index.js +8 -0
  26. package/dist/components/Slider/index.js.map +1 -0
  27. package/dist/components/Slider/sliderThemeRoles.d.ts +8 -0
  28. package/dist/components/Slider/sliderThemeRoles.d.ts.map +1 -0
  29. package/dist/components/Slider/sliderThemeRoles.js +10 -0
  30. package/dist/components/Slider/sliderThemeRoles.js.map +1 -0
  31. package/dist/components/Slider/types.d.ts +18 -0
  32. package/dist/components/Slider/types.d.ts.map +1 -0
  33. package/dist/components/Slider/types.js +3 -0
  34. package/dist/components/Slider/types.js.map +1 -0
  35. package/dist/components/index.d.ts +1 -0
  36. package/dist/components/index.d.ts.map +1 -1
  37. package/dist/components/index.js +4 -1
  38. package/dist/components/index.js.map +1 -1
  39. package/dist/index.d.ts +2 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +5 -2
  42. package/dist/index.js.map +1 -1
  43. package/docs/components/select.md +11 -6
  44. package/docs/components/slider.md +65 -0
  45. package/docs/superpowers/plans/2026-09-25-slider.md +35 -0
  46. package/docs/superpowers/specs/2026-09-25-select-design.md +3 -3
  47. package/docs/superpowers/specs/2026-09-25-slider-design.md +117 -0
  48. package/package.json +6 -2
  49. package/src/components/Select/Select.tsx +133 -64
  50. package/src/components/Select/SelectCloseButton.tsx +15 -7
  51. package/src/components/Select/selectThemeRoles.test.ts +2 -2
  52. package/src/components/Select/selectThemeRoles.ts +1 -1
  53. package/src/components/Select/types.ts +2 -2
  54. package/src/components/Slider/Slider.tsx +95 -0
  55. package/src/components/Slider/index.ts +6 -0
  56. package/src/components/Slider/sliderThemeRoles.test.ts +12 -0
  57. package/src/components/Slider/sliderThemeRoles.ts +8 -0
  58. package/src/components/Slider/types.ts +18 -0
  59. package/src/components/index.ts +6 -0
  60. package/src/index.ts +7 -1
@@ -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,9 @@ export {
41
41
  type SelectOption,
42
42
  type SelectProps,
43
43
  } from "./Select";
44
+ export {
45
+ Slider,
46
+ SLIDER_THEME_ROLES,
47
+ type SliderThemeColorProp,
48
+ type SliderProps,
49
+ } from "./Slider";
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export const version = "0.2.0";
1
+ export const version = "0.2.2";
2
2
 
3
3
  export * from "./theme";
4
4
  export {
@@ -48,4 +48,10 @@ export {
48
48
  type SelectOption,
49
49
  type SelectProps,
50
50
  } from "./components/Select";
51
+ export {
52
+ Slider,
53
+ SLIDER_THEME_ROLES,
54
+ type SliderThemeColorProp,
55
+ type SliderProps,
56
+ } from "./components/Slider";
51
57
  export * as components from "./components";