@studio-piot/native-ui 0.1.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 (76) 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 +81 -19
  5. package/dist/components/Select/Select.js.map +1 -1
  6. package/dist/components/Select/SelectCloseButton.d.ts +9 -2
  7. package/dist/components/Select/SelectCloseButton.d.ts.map +1 -1
  8. package/dist/components/Select/SelectCloseButton.js +14 -6
  9. package/dist/components/Select/SelectCloseButton.js.map +1 -1
  10. package/dist/components/Select/index.d.ts +3 -3
  11. package/dist/components/Select/index.d.ts.map +1 -1
  12. package/dist/components/Select/index.js +2 -1
  13. package/dist/components/Select/index.js.map +1 -1
  14. package/dist/components/Select/presentSelectSheet.d.ts +7 -0
  15. package/dist/components/Select/presentSelectSheet.d.ts.map +1 -0
  16. package/dist/components/Select/presentSelectSheet.js +14 -0
  17. package/dist/components/Select/presentSelectSheet.js.map +1 -0
  18. package/dist/components/Select/resolveSelectDetentIndex.d.ts +2 -0
  19. package/dist/components/Select/resolveSelectDetentIndex.d.ts.map +1 -1
  20. package/dist/components/Select/resolveSelectDetentIndex.js +5 -0
  21. package/dist/components/Select/resolveSelectDetentIndex.js.map +1 -1
  22. package/dist/components/Select/selectThemeRoles.d.ts +1 -1
  23. package/dist/components/Select/selectThemeRoles.js +1 -1
  24. package/dist/components/Select/selectThemeRoles.js.map +1 -1
  25. package/dist/components/Select/trueSheetReanimated.d.ts +13 -0
  26. package/dist/components/Select/trueSheetReanimated.d.ts.map +1 -0
  27. package/dist/components/Select/trueSheetReanimated.js +14 -0
  28. package/dist/components/Select/trueSheetReanimated.js.map +1 -0
  29. package/dist/components/Select/types.d.ts +3 -1
  30. package/dist/components/Select/types.d.ts.map +1 -1
  31. package/dist/components/Slider/Slider.d.ts +3 -0
  32. package/dist/components/Slider/Slider.d.ts.map +1 -0
  33. package/dist/components/Slider/Slider.js +46 -0
  34. package/dist/components/Slider/Slider.js.map +1 -0
  35. package/dist/components/Slider/index.d.ts +4 -0
  36. package/dist/components/Slider/index.d.ts.map +1 -0
  37. package/dist/components/Slider/index.js +8 -0
  38. package/dist/components/Slider/index.js.map +1 -0
  39. package/dist/components/Slider/sliderThemeRoles.d.ts +8 -0
  40. package/dist/components/Slider/sliderThemeRoles.d.ts.map +1 -0
  41. package/dist/components/Slider/sliderThemeRoles.js +10 -0
  42. package/dist/components/Slider/sliderThemeRoles.js.map +1 -0
  43. package/dist/components/Slider/types.d.ts +18 -0
  44. package/dist/components/Slider/types.d.ts.map +1 -0
  45. package/dist/components/Slider/types.js +3 -0
  46. package/dist/components/Slider/types.js.map +1 -0
  47. package/dist/components/index.d.ts +2 -1
  48. package/dist/components/index.d.ts.map +1 -1
  49. package/dist/components/index.js +5 -1
  50. package/dist/components/index.js.map +1 -1
  51. package/dist/index.d.ts +3 -2
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +6 -2
  54. package/dist/index.js.map +1 -1
  55. package/docs/components/select.md +32 -10
  56. package/docs/components/slider.md +65 -0
  57. package/docs/superpowers/plans/2026-09-25-select.md +26 -8
  58. package/docs/superpowers/plans/2026-09-25-slider.md +35 -0
  59. package/docs/superpowers/specs/2026-09-25-select-design.md +39 -67
  60. package/docs/superpowers/specs/2026-09-25-slider-design.md +117 -0
  61. package/package.json +12 -4
  62. package/src/components/Select/Select.tsx +188 -75
  63. package/src/components/Select/SelectCloseButton.tsx +32 -7
  64. package/src/components/Select/index.ts +6 -2
  65. package/src/components/Select/resolveSelectDetentIndex.test.ts +15 -1
  66. package/src/components/Select/resolveSelectDetentIndex.ts +5 -0
  67. package/src/components/Select/selectThemeRoles.test.ts +16 -0
  68. package/src/components/Select/selectThemeRoles.ts +1 -1
  69. package/src/components/Select/types.ts +3 -1
  70. package/src/components/Slider/Slider.tsx +95 -0
  71. package/src/components/Slider/index.ts +6 -0
  72. package/src/components/Slider/sliderThemeRoles.test.ts +12 -0
  73. package/src/components/Slider/sliderThemeRoles.ts +8 -0
  74. package/src/components/Slider/types.ts +18 -0
  75. package/src/components/index.ts +8 -1
  76. package/src/index.ts +9 -2
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SLIDER_THEME_ROLES = exports.Slider = void 0;
4
+ var Slider_1 = require("./Slider");
5
+ Object.defineProperty(exports, "Slider", { enumerable: true, get: function () { return Slider_1.Slider; } });
6
+ var sliderThemeRoles_1 = require("./sliderThemeRoles");
7
+ Object.defineProperty(exports, "SLIDER_THEME_ROLES", { enumerable: true, get: function () { return sliderThemeRoles_1.SLIDER_THEME_ROLES; } });
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/Slider/index.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AACf,uDAG4B;AAF1B,sHAAA,kBAAkB,OAAA"}
@@ -0,0 +1,8 @@
1
+ export declare const SLIDER_THEME_ROLES: {
2
+ readonly labelColor: "fieldLabel";
3
+ readonly valueColor: "fieldText";
4
+ readonly highlightColor: "fieldSliderHighlight";
5
+ readonly trackColor: "fieldBorder";
6
+ };
7
+ export type SliderThemeColorProp = keyof typeof SLIDER_THEME_ROLES;
8
+ //# sourceMappingURL=sliderThemeRoles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sliderThemeRoles.d.ts","sourceRoot":"","sources":["../../../src/components/Slider/sliderThemeRoles.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB;;;;;CAKrB,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SLIDER_THEME_ROLES = void 0;
4
+ exports.SLIDER_THEME_ROLES = {
5
+ labelColor: "fieldLabel",
6
+ valueColor: "fieldText",
7
+ highlightColor: "fieldSliderHighlight",
8
+ trackColor: "fieldBorder",
9
+ };
10
+ //# sourceMappingURL=sliderThemeRoles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sliderThemeRoles.js","sourceRoot":"","sources":["../../../src/components/Slider/sliderThemeRoles.ts"],"names":[],"mappings":";;;AAAa,QAAA,kBAAkB,GAAG;IAChC,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,WAAW;IACvB,cAAc,EAAE,sBAAsB;IACtC,UAAU,EAAE,aAAa;CACjB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { StyleProp, ViewStyle } from "react-native";
2
+ export type SliderProps = {
3
+ value: number;
4
+ onChange: (value: number) => void;
5
+ minimumValue: number;
6
+ maximumValue: number;
7
+ step?: number;
8
+ label?: string;
9
+ /** Defaults to `String(value)`. */
10
+ formatValue?: (value: number) => string;
11
+ disabled?: boolean;
12
+ labelColor?: string;
13
+ valueColor?: string;
14
+ highlightColor?: string;
15
+ trackColor?: string;
16
+ style?: StyleProp<ViewStyle>;
17
+ };
18
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/Slider/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/components/Slider/types.ts"],"names":[],"mappings":""}
@@ -3,5 +3,6 @@ export { LinkBlocks, filterLinkBlockGroups, type LinkBlockGroup, type LinkBlockI
3
3
  export { FieldText, FieldTextarea, FIELD_THEME_ROLES, type FieldThemeColorProp, type FieldSharedProps, type FieldTextProps, type FieldTextareaProps, } from "./Field";
4
4
  export { Form, FormCloseButton, type FormProps } from "./Form";
5
5
  export { RadioField, RADIO_THEME_ROLES, type RadioThemeColorProp, type RadioOption, type RadioFieldProps, } from "./RadioField";
6
- export { Select, SelectCloseButton, SELECT_THEME_ROLES, DEFAULT_SELECT_DETENTS, DEFAULT_SELECT_INITIAL_DETENT, resolveSelectDetentIndex, type SelectThemeColorProp, type SelectOption, type SelectProps, type SelectCloseButtonProp, } from "./Select";
6
+ export { Select, SelectCloseButton, SELECT_THEME_ROLES, DEFAULT_SELECT_DETENTS, DEFAULT_SELECT_INITIAL_DETENT, detentsToSnapPoints, resolveSelectDetentIndex, type SelectThemeColorProp, type SelectCloseButtonSlot, type SelectOption, type SelectProps, } from "./Select";
7
+ export { Slider, SLIDER_THEME_ROLES, type SliderThemeColorProp, type SliderProps, } from "./Slider";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC/D,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,qBAAqB,GAC3B,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC/D,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,MAAM,EACN,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,WAAW,GACjB,MAAM,UAAU,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveSelectDetentIndex = exports.DEFAULT_SELECT_INITIAL_DETENT = exports.DEFAULT_SELECT_DETENTS = exports.SELECT_THEME_ROLES = exports.SelectCloseButton = exports.Select = exports.RADIO_THEME_ROLES = exports.RadioField = exports.FormCloseButton = exports.Form = exports.FIELD_THEME_ROLES = exports.FieldTextarea = exports.FieldText = exports.filterLinkBlockGroups = exports.LinkBlocks = exports.buttonRadiusValue = exports.Button = void 0;
3
+ exports.SLIDER_THEME_ROLES = exports.Slider = exports.resolveSelectDetentIndex = exports.detentsToSnapPoints = exports.DEFAULT_SELECT_INITIAL_DETENT = exports.DEFAULT_SELECT_DETENTS = exports.SELECT_THEME_ROLES = exports.SelectCloseButton = exports.Select = exports.RADIO_THEME_ROLES = exports.RadioField = exports.FormCloseButton = exports.Form = exports.FIELD_THEME_ROLES = exports.FieldTextarea = exports.FieldText = exports.filterLinkBlockGroups = exports.LinkBlocks = exports.buttonRadiusValue = exports.Button = void 0;
4
4
  var Button_1 = require("./Button");
5
5
  Object.defineProperty(exports, "Button", { enumerable: true, get: function () { return Button_1.Button; } });
6
6
  Object.defineProperty(exports, "buttonRadiusValue", { enumerable: true, get: function () { return Button_1.buttonRadiusValue; } });
@@ -23,5 +23,9 @@ Object.defineProperty(exports, "SelectCloseButton", { enumerable: true, get: fun
23
23
  Object.defineProperty(exports, "SELECT_THEME_ROLES", { enumerable: true, get: function () { return Select_1.SELECT_THEME_ROLES; } });
24
24
  Object.defineProperty(exports, "DEFAULT_SELECT_DETENTS", { enumerable: true, get: function () { return Select_1.DEFAULT_SELECT_DETENTS; } });
25
25
  Object.defineProperty(exports, "DEFAULT_SELECT_INITIAL_DETENT", { enumerable: true, get: function () { return Select_1.DEFAULT_SELECT_INITIAL_DETENT; } });
26
+ Object.defineProperty(exports, "detentsToSnapPoints", { enumerable: true, get: function () { return Select_1.detentsToSnapPoints; } });
26
27
  Object.defineProperty(exports, "resolveSelectDetentIndex", { enumerable: true, get: function () { return Select_1.resolveSelectDetentIndex; } });
28
+ var Slider_1 = require("./Slider");
29
+ Object.defineProperty(exports, "Slider", { enumerable: true, get: function () { return Slider_1.Slider; } });
30
+ Object.defineProperty(exports, "SLIDER_THEME_ROLES", { enumerable: true, get: function () { return Slider_1.SLIDER_THEME_ROLES; } });
27
31
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":";;;AAAA,mCAKkB;AAJhB,gGAAA,MAAM,OAAA;AAEN,2GAAA,iBAAiB,OAAA;AAGnB,2CAMsB;AALpB,wGAAA,UAAU,OAAA;AACV,mHAAA,qBAAqB,OAAA;AAKvB,iCAQiB;AAPf,kGAAA,SAAS,OAAA;AACT,sGAAA,aAAa,OAAA;AACb,0GAAA,iBAAiB,OAAA;AAMnB,+BAA+D;AAAtD,4FAAA,IAAI,OAAA;AAAE,uGAAA,eAAe,OAAA;AAC9B,2CAMsB;AALpB,wGAAA,UAAU,OAAA;AACV,+GAAA,iBAAiB,OAAA;AAKnB,mCAWkB;AAVhB,gGAAA,MAAM,OAAA;AACN,2GAAA,iBAAiB,OAAA;AACjB,4GAAA,kBAAkB,OAAA;AAClB,gHAAA,sBAAsB,OAAA;AACtB,uHAAA,6BAA6B,OAAA;AAC7B,kHAAA,wBAAwB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":";;;AAAA,mCAKkB;AAJhB,gGAAA,MAAM,OAAA;AAEN,2GAAA,iBAAiB,OAAA;AAGnB,2CAMsB;AALpB,wGAAA,UAAU,OAAA;AACV,mHAAA,qBAAqB,OAAA;AAKvB,iCAQiB;AAPf,kGAAA,SAAS,OAAA;AACT,sGAAA,aAAa,OAAA;AACb,0GAAA,iBAAiB,OAAA;AAMnB,+BAA+D;AAAtD,4FAAA,IAAI,OAAA;AAAE,uGAAA,eAAe,OAAA;AAC9B,2CAMsB;AALpB,wGAAA,UAAU,OAAA;AACV,+GAAA,iBAAiB,OAAA;AAKnB,mCAYkB;AAXhB,gGAAA,MAAM,OAAA;AACN,2GAAA,iBAAiB,OAAA;AACjB,4GAAA,kBAAkB,OAAA;AAClB,gHAAA,sBAAsB,OAAA;AACtB,uHAAA,6BAA6B,OAAA;AAC7B,6GAAA,mBAAmB,OAAA;AACnB,kHAAA,wBAAwB,OAAA;AAM1B,mCAKkB;AAJhB,gGAAA,MAAM,OAAA;AACN,4GAAA,kBAAkB,OAAA"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- export declare const version = "0.1.0";
1
+ export declare const version = "0.2.2";
2
2
  export * from "./theme";
3
3
  export { Button, type ButtonProps, buttonRadiusValue, type ButtonRadius, } from "./components/Button";
4
4
  export { LinkBlocks, filterLinkBlockGroups, type LinkBlockGroup, type LinkBlockItem, type LinkBlocksProps, } from "./components/LinkBlocks";
5
5
  export { FieldText, FieldTextarea, FIELD_THEME_ROLES, type FieldThemeColorProp, type FieldSharedProps, type FieldTextProps, type FieldTextareaProps, } from "./components/Field";
6
6
  export { Form, FormCloseButton, type FormProps, } from "./components/Form";
7
7
  export { RadioField, RADIO_THEME_ROLES, type RadioThemeColorProp, type RadioOption, type RadioFieldProps, } from "./components/RadioField";
8
- export { Select, SelectCloseButton, SELECT_THEME_ROLES, DEFAULT_SELECT_DETENTS, DEFAULT_SELECT_INITIAL_DETENT, resolveSelectDetentIndex, type SelectThemeColorProp, type SelectOption, type SelectProps, type SelectCloseButtonProp, } from "./components/Select";
8
+ export { Select, SelectCloseButton, SELECT_THEME_ROLES, DEFAULT_SELECT_DETENTS, DEFAULT_SELECT_INITIAL_DETENT, detentsToSnapPoints, resolveSelectDetentIndex, type SelectThemeColorProp, type SelectCloseButtonSlot, type SelectOption, type SelectProps, } from "./components/Select";
9
+ export { Slider, SLIDER_THEME_ROLES, type SliderThemeColorProp, type SliderProps, } from "./components/Slider";
9
10
  export * as components from "./components";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,MAAM,EACN,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,YAAY,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,IAAI,EACJ,eAAe,EACf,KAAK,SAAS,GACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,MAAM,EACN,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,YAAY,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,IAAI,EACJ,eAAe,EACf,KAAK,SAAS,GACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,MAAM,EACN,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,WAAW,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -36,8 +36,8 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.components = exports.resolveSelectDetentIndex = exports.DEFAULT_SELECT_INITIAL_DETENT = exports.DEFAULT_SELECT_DETENTS = exports.SELECT_THEME_ROLES = exports.SelectCloseButton = exports.Select = exports.RADIO_THEME_ROLES = exports.RadioField = exports.FormCloseButton = exports.Form = exports.FIELD_THEME_ROLES = exports.FieldTextarea = exports.FieldText = exports.filterLinkBlockGroups = exports.LinkBlocks = exports.buttonRadiusValue = exports.Button = exports.version = void 0;
40
- exports.version = "0.1.0";
39
+ exports.components = exports.SLIDER_THEME_ROLES = exports.Slider = exports.resolveSelectDetentIndex = exports.detentsToSnapPoints = exports.DEFAULT_SELECT_INITIAL_DETENT = exports.DEFAULT_SELECT_DETENTS = exports.SELECT_THEME_ROLES = exports.SelectCloseButton = exports.Select = exports.RADIO_THEME_ROLES = exports.RadioField = exports.FormCloseButton = exports.Form = exports.FIELD_THEME_ROLES = exports.FieldTextarea = exports.FieldText = exports.filterLinkBlockGroups = exports.LinkBlocks = exports.buttonRadiusValue = exports.Button = exports.version = void 0;
40
+ exports.version = "0.2.2";
41
41
  __exportStar(require("./theme"), exports);
42
42
  var Button_1 = require("./components/Button");
43
43
  Object.defineProperty(exports, "Button", { enumerable: true, get: function () { return Button_1.Button; } });
@@ -61,6 +61,10 @@ Object.defineProperty(exports, "SelectCloseButton", { enumerable: true, get: fun
61
61
  Object.defineProperty(exports, "SELECT_THEME_ROLES", { enumerable: true, get: function () { return Select_1.SELECT_THEME_ROLES; } });
62
62
  Object.defineProperty(exports, "DEFAULT_SELECT_DETENTS", { enumerable: true, get: function () { return Select_1.DEFAULT_SELECT_DETENTS; } });
63
63
  Object.defineProperty(exports, "DEFAULT_SELECT_INITIAL_DETENT", { enumerable: true, get: function () { return Select_1.DEFAULT_SELECT_INITIAL_DETENT; } });
64
+ Object.defineProperty(exports, "detentsToSnapPoints", { enumerable: true, get: function () { return Select_1.detentsToSnapPoints; } });
64
65
  Object.defineProperty(exports, "resolveSelectDetentIndex", { enumerable: true, get: function () { return Select_1.resolveSelectDetentIndex; } });
66
+ var Slider_1 = require("./components/Slider");
67
+ Object.defineProperty(exports, "Slider", { enumerable: true, get: function () { return Slider_1.Slider; } });
68
+ Object.defineProperty(exports, "SLIDER_THEME_ROLES", { enumerable: true, get: function () { return Slider_1.SLIDER_THEME_ROLES; } });
65
69
  exports.components = __importStar(require("./components"));
66
70
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAa,QAAA,OAAO,GAAG,OAAO,CAAC;AAE/B,0CAAwB;AACxB,8CAK6B;AAJ3B,gGAAA,MAAM,OAAA;AAEN,2GAAA,iBAAiB,OAAA;AAGnB,sDAMiC;AAL/B,wGAAA,UAAU,OAAA;AACV,mHAAA,qBAAqB,OAAA;AAKvB,4CAQ4B;AAP1B,kGAAA,SAAS,OAAA;AACT,sGAAA,aAAa,OAAA;AACb,0GAAA,iBAAiB,OAAA;AAMnB,0CAI2B;AAHzB,4FAAA,IAAI,OAAA;AACJ,uGAAA,eAAe,OAAA;AAGjB,sDAMiC;AAL/B,wGAAA,UAAU,OAAA;AACV,+GAAA,iBAAiB,OAAA;AAKnB,8CAW6B;AAV3B,gGAAA,MAAM,OAAA;AACN,2GAAA,iBAAiB,OAAA;AACjB,4GAAA,kBAAkB,OAAA;AAClB,gHAAA,sBAAsB,OAAA;AACtB,uHAAA,6BAA6B,OAAA;AAC7B,kHAAA,wBAAwB,OAAA;AAM1B,2DAA2C"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAa,QAAA,OAAO,GAAG,OAAO,CAAC;AAE/B,0CAAwB;AACxB,8CAK6B;AAJ3B,gGAAA,MAAM,OAAA;AAEN,2GAAA,iBAAiB,OAAA;AAGnB,sDAMiC;AAL/B,wGAAA,UAAU,OAAA;AACV,mHAAA,qBAAqB,OAAA;AAKvB,4CAQ4B;AAP1B,kGAAA,SAAS,OAAA;AACT,sGAAA,aAAa,OAAA;AACb,0GAAA,iBAAiB,OAAA;AAMnB,0CAI2B;AAHzB,4FAAA,IAAI,OAAA;AACJ,uGAAA,eAAe,OAAA;AAGjB,sDAMiC;AAL/B,wGAAA,UAAU,OAAA;AACV,+GAAA,iBAAiB,OAAA;AAKnB,8CAY6B;AAX3B,gGAAA,MAAM,OAAA;AACN,2GAAA,iBAAiB,OAAA;AACjB,4GAAA,kBAAkB,OAAA;AAClB,gHAAA,sBAAsB,OAAA;AACtB,uHAAA,6BAA6B,OAAA;AAC7B,6GAAA,mBAAmB,OAAA;AACnB,kHAAA,wBAAwB,OAAA;AAM1B,8CAK6B;AAJ3B,gGAAA,MAAM,OAAA;AACN,4GAAA,kBAAkB,OAAA;AAIpB,2DAA2C"}
@@ -1,22 +1,42 @@
1
1
  # Select — usage
2
2
 
3
- Field-like select trigger that opens a native bottom sheet (`@lodev09/react-native-true-sheet`) of options. Selecting an option updates the value and dismisses the sheet. Grabber, dimmed backdrop, and detents are native.
3
+ Field-like select trigger that opens a Gorhom bottom sheet (`@gorhom/bottom-sheet`) of options. Selecting an option updates the value and dismisses the sheet.
4
4
 
5
5
  For `ThemeProvider`, see [theme provider and Button usage](./button.md). Design: [Select design](../superpowers/specs/2026-09-25-select-design.md).
6
6
 
7
- ## Peer dependency
7
+ ## Peer dependencies
8
8
 
9
- Install in the app:
9
+ Install in the app (Expo example):
10
10
 
11
11
  ```bash
12
- npx expo install @lodev09/react-native-true-sheet
12
+ npx expo install @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-screens
13
13
  ```
14
14
 
15
- Requires New Architecture (Expo SDK 54+ / TrueSheet 3.7+).
15
+ On iOS, Select uses `FullWindowOverlay` so the sheet appears above React Navigation `fullScreenModal` screens.
16
+
17
+ Wrap the app root with `GestureHandlerRootView` and `BottomSheetModalProvider`. Put **`BottomSheetModalProvider` inside `ThemeProvider`** so sheet content (portaled) still resolves theme colours:
18
+
19
+ ```tsx
20
+ import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
21
+ import { ThemeProvider } from "@studio-piot/native-ui";
22
+ import { GestureHandlerRootView } from "react-native-gesture-handler";
23
+
24
+ export default function RootLayout() {
25
+ return (
26
+ <GestureHandlerRootView style={{ flex: 1 }}>
27
+ <ThemeProvider colors={Colors}>
28
+ <BottomSheetModalProvider>{/* … */}</BottomSheetModalProvider>
29
+ </ThemeProvider>
30
+ </GestureHandlerRootView>
31
+ );
32
+ }
33
+ ```
34
+
35
+ Import `react-native-gesture-handler` at the top of the entry layout (before other imports) if your template requires it.
16
36
 
17
37
  ## Theme roles
18
38
 
19
- Uses Field roles: `fieldBackground`, `fieldText`, `fieldPlaceholder`, `fieldLabel`, `fieldDescription`, `fieldBorder`, `fieldError`, plus `fieldRadioSelected` for the selected check / emphasis.
39
+ Uses Field roles: `fieldBackground`, `fieldText`, `fieldPlaceholder`, `fieldLabel`, `fieldDescription`, `fieldBorder`, `fieldError`, plus **`fieldSelectSelected`** for the selected option check / label tint.
20
40
 
21
41
  ## Basic usage
22
42
 
@@ -36,15 +56,17 @@ import { Select } from "@studio-piot/native-ui";
36
56
  />
37
57
  ```
38
58
 
39
- ## Glass close (app slot)
59
+ ## Close control
60
+
61
+ Default is a dark disc with a white × (`SelectCloseButton`). Override:
40
62
 
41
63
  ```tsx
42
- closeButton={(dismiss) => <FormCloseButton onPress={dismiss} />}
64
+ closeButton={(dismiss) => <YourClose onPress={dismiss} />}
43
65
  ```
44
66
 
45
- ## Detents
67
+ ## Detents / height
46
68
 
47
- Default `detents={[0.25, 0.5, 0.75]}`, opens at `initialDetent={0.5}`. Override via props.
69
+ Sheet height follows content (`enableDynamicSizing`), capped by the largest `detents` fraction (default `0.75` of the window). Dragging cannot expand past content (or that cap when the list is long).
48
70
 
49
71
  ## Imports
50
72
 
@@ -0,0 +1,65 @@
1
+ # Slider — usage
2
+
3
+ Controlled slider with label on the left and a live formatted value on the top right. Track uses `@react-native-community/slider`. Your app owns form state.
4
+
5
+ Design: [Slider design](../superpowers/specs/2026-09-25-slider-design.md).
6
+
7
+ ## Peer dependency
8
+
9
+ ```bash
10
+ npx expo install @react-native-community/slider
11
+ ```
12
+
13
+ ## Theme roles
14
+
15
+ | Usage | Role |
16
+ |---|---|
17
+ | Label | `fieldLabel` |
18
+ | Value | `fieldText` |
19
+ | Filled track + thumb | **`fieldSliderHighlight`** (apps must add) |
20
+ | Unfilled track | `fieldBorder` |
21
+
22
+ ## Basic usage
23
+
24
+ ```tsx
25
+ import { Slider } from "@studio-piot/native-ui";
26
+ import { useState } from "react";
27
+
28
+ export function EffortSlider() {
29
+ const [effort, setEffort] = useState(5);
30
+
31
+ return (
32
+ <Slider
33
+ label="Effort"
34
+ value={effort}
35
+ onChange={setEffort}
36
+ minimumValue={1}
37
+ maximumValue={10}
38
+ step={1}
39
+ />
40
+ );
41
+ }
42
+ ```
43
+
44
+ ### Optional colour overrides
45
+
46
+ | Prop | Default theme role |
47
+ |---|---|
48
+ | `labelColor` | `fieldLabel` |
49
+ | `valueColor` | `fieldText` |
50
+ | `highlightColor` | `fieldSliderHighlight` |
51
+ | `trackColor` | `fieldBorder` |
52
+
53
+ ### Props
54
+
55
+ | Prop | Type | Default | Notes |
56
+ |---|---|---|---|
57
+ | `value` | `number` | — | Required |
58
+ | `onChange` | `(value: number) => void` | — | Required |
59
+ | `minimumValue` | `number` | — | Required |
60
+ | `maximumValue` | `number` | — | Required |
61
+ | `step` | `number` | — | Optional; omit for continuous |
62
+ | `label` | `string` | — | Optional |
63
+ | `formatValue` | `(value: number) => string` | `String` | Value display |
64
+ | `disabled` | `boolean` | `false` | |
65
+ | `style` | view style | — | Outer column |
@@ -2,21 +2,39 @@
2
2
 
3
3
  > **For agentic workers:** Inline execution in session; checkboxes track progress.
4
4
 
5
- **Goal:** Ship `Select` (Field-like trigger + TrueSheet options) and wire Ribe create-activity Type.
5
+ **Goal:** Ship `Select` (Field-like trigger + Gorhom `BottomSheetModal`) and wire Ribe create-activity Type; remove TrueSheet from Ribe.
6
6
 
7
- **Architecture:** Controlled `Select` presents `@lodev09/react-native-true-sheet` at detents `[0.25, 0.5, 0.75]` (open index for `0.5`). `closeButton` accepts `ReactNode | ((dismiss) => ReactNode)`. Field theme roles + `fieldRadioSelected` for selected check.
7
+ **Architecture:** Controlled `Select` presents `@gorhom/bottom-sheet` at snap points from detents `[0.25, 0.5, 0.75]` (open index for `0.5`). Default close is DiscClose. Field theme roles + `fieldRadioSelected` for selected check. App provides `BottomSheetModalProvider` (+ existing `GestureHandlerRootView`).
8
8
 
9
- **Tech Stack:** TypeScript, RN, TrueSheet peer, Expo Ribe.
9
+ **Tech Stack:** TypeScript, RN, Gorhom peer (+ reanimated, gesture-handler), Expo Ribe.
10
10
 
11
11
  ## Global Constraints
12
12
 
13
13
  - Spec: `docs/superpowers/specs/2026-09-25-select-design.md`
14
- - Peer `@lodev09/react-native-true-sheet` (v3.x for Expo 57)
14
+ - Peers: `@gorhom/bottom-sheet`, `react-native-reanimated`, `react-native-gesture-handler`
15
+ - No TrueSheet in package or Ribe
15
16
  - No brand in package; no search/icons/grids
17
+ - Commit only when the user asks
18
+
19
+ ## File map
20
+
21
+ | File | Responsibility |
22
+ |---|---|
23
+ | `src/components/Select/resolveSelectDetentIndex.ts` | Detent → snap index + % snap points |
24
+ | `src/components/Select/resolveSelectDetentIndex.test.ts` | Unit tests |
25
+ | `src/components/Select/selectThemeRoles.ts` | Role map |
26
+ | `src/components/Select/selectThemeRoles.test.ts` | Role map assertions |
27
+ | `src/components/Select/types.ts` | Props |
28
+ | `src/components/Select/SelectCloseButton.tsx` | DiscClose default |
29
+ | `src/components/Select/Select.tsx` | Trigger + BottomSheetModal |
30
+ | `src/components/Select/index.ts` | Barrel |
31
+ | `docs/components/select.md` | Usage |
32
+ | Exports / README | Public surface |
33
+ | Ribe create-activity, root layout | Wire Select + BottomSheetModalProvider |
34
+ | Ribe package / sheet-lab / activities | Remove TrueSheet + lab |
16
35
 
17
36
  ## Tasks
18
37
 
19
- 1. [x] Package Select + peers + docs + exports + unit test for detent index helper
20
- 2. [x] Ribe: install TrueSheet, create-activity Type → Select + glass close
21
-
22
- ---
38
+ - [x] Spec (Gorhom)
39
+ - [x] Package Select + peers + docs + exports + unit tests
40
+ - [x] Ribe: BottomSheetModalProvider, create-activity Select, remove TrueSheet + sheet-lab
@@ -0,0 +1,35 @@
1
+ # Slider Implementation Plan
2
+
3
+ > **For agentic workers:** Inline execution in session; checkboxes track progress.
4
+
5
+ **Goal:** Ship controlled `Slider` (label left, formatted value top-right, community track) with `fieldSliderHighlight`, and wire Ribe create-activity Effort (1–10).
6
+
7
+ **Architecture:** Presentational wrapper around `@react-native-community/slider`. Theme colours resolve like Field via `useResolvedFieldColor`. New role `fieldSliderHighlight` for filled track + thumb; unfilled track uses `fieldBorder`.
8
+
9
+ **Tech Stack:** TypeScript, RN, `@react-native-community/slider` peer, Expo Ribe.
10
+
11
+ ## Global Constraints
12
+
13
+ - Spec: `docs/superpowers/specs/2026-09-25-slider-design.md`
14
+ - Peer `@react-native-community/slider`
15
+ - No brand in package; no custom-drawn track; no Optimum migration
16
+ - Commit only when the user asks
17
+
18
+ ## File map
19
+
20
+ | File | Responsibility |
21
+ |---|---|
22
+ | `src/components/Slider/sliderThemeRoles.ts` | Role map |
23
+ | `src/components/Slider/sliderThemeRoles.test.ts` | Role assertions |
24
+ | `src/components/Slider/types.ts` | `SliderProps` |
25
+ | `src/components/Slider/Slider.tsx` | Header + community Slider |
26
+ | `src/components/Slider/index.ts` | Barrel |
27
+ | Exports / README / `docs/components/slider.md` | Public surface |
28
+ | Ribe `colors.ts` (+ test) | `fieldSliderHighlight` |
29
+ | Ribe create-activity | Effort Slider |
30
+ | Ribe `package.json` | Install community slider |
31
+
32
+ ## Tasks
33
+
34
+ - [x] Package Slider + peer + docs + exports + unit test
35
+ - [x] Ribe: theme role + Effort on create-activity + install peer
@@ -7,22 +7,24 @@ Apps: Ribe create-activity Type field in the same pass
7
7
 
8
8
  ## Goal
9
9
 
10
- Ship a presentational **Select**: Field-like trigger that opens a native bottom sheet of options (TrueSheet). Highlight the selected option; choosing one updates value and dismisses the sheet. Drag handle, dimmed backdrop, and snap heights feel like system UI (Strava-style grabber). Ribe wires Type of activity to Select with a glass close control.
10
+ Ship a presentational **Select**: Field-like trigger that opens a Gorhom bottom sheet of options. Highlight the selected option; choosing one updates value and dismisses the sheet. Grabber, dimmed backdrop, and snap heights match the spike UX. Ribe wires Type of activity to Select; default close is a dark disc with white cross (not glass Cancel).
11
11
 
12
12
  ## Decisions
13
13
 
14
14
  | Topic | Choice |
15
15
  |---|---|
16
16
  | API shape | Single `Select`: trigger + internal sheet |
17
- | Sheet library | `@lodev09/react-native-true-sheet` (native UIKit / Material) |
18
- | Detents | Default `[0.25, 0.5, 0.75]`; present at `0.5` |
19
- | Close control | `closeButton` slot; package default plain Cancel; Ribe passes glass |
20
- | Colours | Field `field*` roles + `fieldRadioSelected` for selected check / emphasis |
17
+ | Sheet library | `@gorhom/bottom-sheet` (`BottomSheetModal`) |
18
+ | Sheet height | Content-sized (`enableDynamicSizing`); max = largest detent (default 75% of window) |
19
+ | Close control | `closeButton` slot; package default **DiscClose** (dark disc + white ×); Ribe uses default |
20
+ | Colours | Field `field*` roles + `fieldSelectSelected` for selected check / emphasis |
21
21
  | Trigger | Field-like chrome (label, description, error, chevron, radius) |
22
22
  | Options | `{ value, label, disabled? }[]`; scrollable when long |
23
23
  | Select behaviour | Tap option → `onChange(value)` + dismiss |
24
+ | App provider | App must wrap with `GestureHandlerRootView` + `BottomSheetModalProvider` |
24
25
  | Icons / search / grids | Out of scope |
25
- | Optimum migration | Out of scope this pass (CustomPicker remains until a later PR) |
26
+ | TrueSheet | Not used; no TrueSheet peer or native bits |
27
+ | Optimum migration | Out of scope this pass |
26
28
  | Brand coupling | None in the package |
27
29
 
28
30
  ## Architecture
@@ -30,29 +32,30 @@ Ship a presentational **Select**: Field-like trigger that opens a native bottom
30
32
  ```
31
33
  Select
32
34
  ├── trigger (Field-like Pressable)
33
- └── TrueSheet
34
- ├── grabber (system)
35
+ └── BottomSheetModal
36
+ ├── BottomSheetBackdrop (opacity 0.72)
37
+ ├── handle / grabber
35
38
  ├── header (title + closeButton)
36
- └── scrollable options
39
+ └── BottomSheetScrollView options
37
40
  └── row (label + check when selected)
38
41
  ```
39
42
 
40
- - Public export: `Select` (+ types). Default close control may be a small internal component (plain Cancel), not required to be public.
43
+ - Public export: `Select` (+ types). Default close is `SelectCloseButton` (DiscClose).
41
44
  - Controlled only: `value` + `onChange` + `options`.
42
- - Peer dependency: `@lodev09/react-native-true-sheet` (document in usage docs; matching `devDependency` for types/build).
45
+ - Peer dependencies: `@gorhom/bottom-sheet`, `react-native-reanimated`, `react-native-gesture-handler` (plus existing RN peers).
43
46
 
44
47
  ## Theme roles
45
48
 
46
49
  | Usage | Theme role |
47
50
  |---|---|
48
- | Trigger / sheet list ground | `fieldBackground` |
51
+ | Trigger / sheet ground | `fieldBackground` |
49
52
  | Trigger value / option label | `fieldText` |
50
- | Placeholder / chevron | `fieldPlaceholder` |
53
+ | Placeholder / chevron / handle | `fieldPlaceholder` |
51
54
  | Label above trigger | `fieldLabel` |
52
55
  | Description under trigger | `fieldDescription` |
53
56
  | Trigger border / dividers | `fieldBorder` |
54
57
  | Error border + message | `fieldError` |
55
- | Selected option emphasis / check | `fieldRadioSelected` |
58
+ | Selected option emphasis / check | `fieldSelectSelected` |
56
59
 
57
60
  Optional colour override props follow the Field pattern (explicit prop wins over theme role).
58
61
 
@@ -65,6 +68,10 @@ type SelectOption = {
65
68
  disabled?: boolean;
66
69
  };
67
70
 
71
+ type SelectCloseButton =
72
+ | ReactNode
73
+ | ((dismiss: () => void) => ReactNode);
74
+
68
75
  type SelectProps = {
69
76
  value: string | null;
70
77
  onChange: (value: string) => void;
@@ -74,7 +81,7 @@ type SelectProps = {
74
81
  description?: string;
75
82
  error?: string;
76
83
  sheetTitle?: string;
77
- closeButton?: ReactNode;
84
+ closeButton?: SelectCloseButton;
78
85
  detents?: number[];
79
86
  initialDetent?: number;
80
87
  radius?: ButtonRadius;
@@ -88,11 +95,11 @@ type SelectProps = {
88
95
  |---|---|
89
96
  | `value` | Controlled; `null` / unmatched → show placeholder |
90
97
  | `onChange` | Fired with option `value` on row press |
91
- | `options` | Empty list: trigger still opens an empty sheet (or no-op open — prefer open with empty state text “No options”) |
98
+ | `options` | Empty list: open sheet with “No options” empty state |
92
99
  | `sheetTitle` | Header title; default `label` or `"Select"` |
93
- | `closeButton` | Render prop / node for header trailing close; default plain Cancel that dismisses |
94
- | `detents` | Default `[0.25, 0.5, 0.75]` |
95
- | `initialDetent` | Default `0.5` (must be one of `detents`) |
100
+ | `closeButton` | Header trailing close; default DiscClose that dismisses |
101
+ | `detents` | Fractions `0–1`; default `[0.25, 0.5, 0.75]` → Gorhom snap `%` |
102
+ | `initialDetent` | Default `0.5` (must be one of `detents` or falls back to index 0) |
96
103
  | `radius` | Trigger radius; default `"xl"` |
97
104
  | `editable` | `false` → reduced opacity, no open |
98
105
 
@@ -107,58 +114,23 @@ Aligned with Field:
107
114
 
108
115
  ## Sheet behaviour
109
116
 
110
- - Present TrueSheet on trigger press (when `editable`)
111
- - System grabber; dimmed / fading backdrop (library default)
112
- - Header: title + `closeButton` (or default Cancel)
113
- - Scrollable option list; row min height ~52; selected row uses `fieldRadioSelected` for check (and optional label tint)
117
+ - Present `BottomSheetModal` on trigger press (when `editable`)
118
+ - Backdrop opacity `0.72`, press closes
119
+ - Handle indicator; `enablePanDownToClose`
120
+ - Header: title + `closeButton` (or default DiscClose)
121
+ - Scrollable option list; row min height ~52; selected row uses `fieldRadioSelected` for check (and label tint)
114
122
  - Row press: `onChange` + dismiss
115
- - Dismiss also via grabber drag / backdrop / close control
116
- - No search field, no category grids, no leading sport icons in this pass
123
+ - Dismiss also via pan-down / backdrop / close control
117
124
 
118
125
  ## Accessibility
119
126
 
120
- - Trigger: `accessibilityRole="button"`, label from `label` / placeholder; announce current value when set
121
- - Options: `accessibilityRole="button"` (or `menuitem` if we use a menu pattern — prefer button + `accessibilityState.selected`)
122
- - Close control: button with accessible name “Cancel” / “Close”
123
-
124
- ## App work (Ribe)
125
-
126
- 1. `npx expo install @lodev09/react-native-true-sheet` (or pnpm equivalent)
127
- 2. Create-activity: replace Type `FieldText` with `Select`
128
- - Options: Ride, Run, Swim, Walk, Hike (values lowercase slug or same as label — pick stable string values e.g. `ride`, `run`, …)
129
- - `error` when empty (same validation copy intent as today)
130
- - `closeButton` that dismisses the sheet using glass `FormCloseButton` (needs a dismiss callback from Select — either `closeButton` as render prop `(dismiss) => ReactNode` **or** Select clones/passes dismiss via slot convention)
131
-
132
- ### Close slot + dismiss
133
-
134
- Prefer **render prop** so the app can wire glass Cancel correctly:
135
-
136
- ```ts
137
- closeButton?: ReactNode | ((dismiss: () => void) => ReactNode);
138
- ```
139
-
140
- If `ReactNode` only, the default Cancel works but glass Cancel cannot call dismiss unless Select exposes an imperative API. **Decision: support function form** `(dismiss) => ReactNode`, and also accept a static `ReactNode` for simple cases (static node cannot dismiss unless it uses an internal context — for static nodes, wrap with Pressable only in default Cancel).
141
-
142
- Ribe usage:
143
-
144
- ```tsx
145
- closeButton={(dismiss) => <FormCloseButton onPress={dismiss} />}
146
- ```
147
-
148
- 3. Keep Name / Notes as Field; form submit still gated on name (and optionally require type — keep current “type empty → error” UX).
127
+ - Trigger: `accessibilityRole="button"`, label includes field label + current display, hint that it opens options
128
+ - Option rows: `accessibilityRole="button"`, `accessibilityState.selected`
129
+ - Close: `accessibilityLabel="Close"`
149
130
 
150
131
  ## Out of scope
151
132
 
152
- - Strava “Top Sports” horizontal chips / search
153
- - Per-option icons
154
- - Migrating Optimum `CustomPicker` call sites
155
- - Multi-select
156
- - Liquid glass Cancel inside the package
157
- - New `select*` theme roles
158
-
159
- ## Success criteria
160
-
161
- - Package exports `Select` with usage docs and TrueSheet peer documented
162
- - Sheet: grabber, fade backdrop, detents ¼ / ½ / ¾, open at ½, scrollable options, select-to-dismiss
163
- - Ribe create-activity Type uses Select + glass close
164
- - `npm test` / `npm run build` (package) and Ribe `pnpm typecheck` pass
133
+ - Multi-select, search, icons, section headers
134
+ - TrueSheet / RN Modal sheet implementations
135
+ - Migrating Optimum CustomPicker
136
+ - Publishing / EAS workflow changes beyond peers + Ribe wire