@oxyhq/bloom 0.30.3 → 0.30.5

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 (80) hide show
  1. package/lib/commonjs/activity-heatmap/ActivityHeatmap.js +2 -37
  2. package/lib/commonjs/activity-heatmap/ActivityHeatmap.js.map +1 -1
  3. package/lib/commonjs/bottom-sheet/BottomSheetBase.js +731 -0
  4. package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -0
  5. package/lib/commonjs/bottom-sheet/index.js +24 -694
  6. package/lib/commonjs/bottom-sheet/index.js.map +1 -1
  7. package/lib/commonjs/bottom-sheet/index.web.js +64 -0
  8. package/lib/commonjs/bottom-sheet/index.web.js.map +1 -0
  9. package/lib/commonjs/dialog/Dialog.js +2 -2
  10. package/lib/commonjs/dialog/Dialog.js.map +1 -1
  11. package/lib/commonjs/dialog/DialogBottomSheet.js +2 -2
  12. package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -1
  13. package/lib/commonjs/dialog/SheetShell.js +2 -2
  14. package/lib/commonjs/dialog/SheetShell.js.map +1 -1
  15. package/lib/commonjs/index.js +44 -44
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/commonjs/index.web.js +66 -66
  18. package/lib/commonjs/index.web.js.map +1 -1
  19. package/lib/commonjs/theme/color-utils.js +65 -0
  20. package/lib/commonjs/theme/color-utils.js.map +1 -0
  21. package/lib/commonjs/theme/index.js +13 -0
  22. package/lib/commonjs/theme/index.js.map +1 -1
  23. package/lib/module/activity-heatmap/ActivityHeatmap.js +1 -36
  24. package/lib/module/activity-heatmap/ActivityHeatmap.js.map +1 -1
  25. package/lib/module/bottom-sheet/BottomSheetBase.js +728 -0
  26. package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -0
  27. package/lib/module/bottom-sheet/index.js +27 -696
  28. package/lib/module/bottom-sheet/index.js.map +1 -1
  29. package/lib/module/bottom-sheet/index.web.js +63 -0
  30. package/lib/module/bottom-sheet/index.web.js.map +1 -0
  31. package/lib/module/dialog/Dialog.js +1 -1
  32. package/lib/module/dialog/Dialog.js.map +1 -1
  33. package/lib/module/dialog/DialogBottomSheet.js +1 -1
  34. package/lib/module/dialog/DialogBottomSheet.js.map +1 -1
  35. package/lib/module/dialog/SheetShell.js +1 -1
  36. package/lib/module/dialog/SheetShell.js.map +1 -1
  37. package/lib/module/index.js +1 -1
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/index.web.js +1 -1
  40. package/lib/module/index.web.js.map +1 -1
  41. package/lib/module/theme/color-utils.js +60 -0
  42. package/lib/module/theme/color-utils.js.map +1 -0
  43. package/lib/module/theme/index.js +1 -0
  44. package/lib/module/theme/index.js.map +1 -1
  45. package/lib/typescript/commonjs/activity-heatmap/ActivityHeatmap.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts +127 -0
  47. package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts.map +1 -0
  48. package/lib/typescript/commonjs/bottom-sheet/index.d.ts +2 -90
  49. package/lib/typescript/commonjs/bottom-sheet/index.d.ts.map +1 -1
  50. package/lib/typescript/commonjs/bottom-sheet/index.web.d.ts +7 -0
  51. package/lib/typescript/commonjs/bottom-sheet/index.web.d.ts.map +1 -0
  52. package/lib/typescript/commonjs/index.web.d.ts +2 -2
  53. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  54. package/lib/typescript/commonjs/theme/color-utils.d.ts +28 -0
  55. package/lib/typescript/commonjs/theme/color-utils.d.ts.map +1 -0
  56. package/lib/typescript/commonjs/theme/index.d.ts +2 -0
  57. package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
  58. package/lib/typescript/module/activity-heatmap/ActivityHeatmap.d.ts.map +1 -1
  59. package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts +127 -0
  60. package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts.map +1 -0
  61. package/lib/typescript/module/bottom-sheet/index.d.ts +2 -90
  62. package/lib/typescript/module/bottom-sheet/index.d.ts.map +1 -1
  63. package/lib/typescript/module/bottom-sheet/index.web.d.ts +7 -0
  64. package/lib/typescript/module/bottom-sheet/index.web.d.ts.map +1 -0
  65. package/lib/typescript/module/index.web.d.ts +2 -2
  66. package/lib/typescript/module/index.web.d.ts.map +1 -1
  67. package/lib/typescript/module/theme/color-utils.d.ts +28 -0
  68. package/lib/typescript/module/theme/color-utils.d.ts.map +1 -0
  69. package/lib/typescript/module/theme/index.d.ts +2 -0
  70. package/lib/typescript/module/theme/index.d.ts.map +1 -1
  71. package/package.json +6 -1
  72. package/src/__tests__/BottomSheetWebFork.test.ts +66 -0
  73. package/src/activity-heatmap/ActivityHeatmap.tsx +1 -29
  74. package/src/bottom-sheet/BottomSheetBase.tsx +853 -0
  75. package/src/bottom-sheet/index.tsx +26 -821
  76. package/src/bottom-sheet/index.web.tsx +66 -0
  77. package/src/index.web.ts +2 -2
  78. package/src/theme/__tests__/color-utils.test.ts +40 -0
  79. package/src/theme/color-utils.ts +54 -0
  80. package/src/theme/index.ts +2 -0
@@ -0,0 +1,66 @@
1
+ import type React from 'react';
2
+ import { forwardRef } from 'react';
3
+ import { StyleSheet, type ViewStyle } from 'react-native';
4
+ import { GestureHandlerRootView } from 'react-native-gesture-handler';
5
+ import { Portal } from '../portal/index.web';
6
+ import { Z_INDEX } from '../styles/z-index';
7
+ import {
8
+ BottomSheetBase,
9
+ type BottomSheetProps,
10
+ type BottomSheetRef,
11
+ type BottomSheetShellProps,
12
+ } from './BottomSheetBase';
13
+
14
+ /**
15
+ * Web shell: bloom's stable DOM `<Portal>` (a single, reused
16
+ * `#bloom-portal-root` in `document.body`, created in a layout effect and never
17
+ * torn down) + a fixed, full-viewport `<GestureHandlerRootView>`.
18
+ *
19
+ * Why NOT RN-Web's `<Modal>` (which native uses): RN-Web's `Modal` mounts
20
+ * through `ModalPortal`, which appends its host `<div>` DURING RENDER and
21
+ * removes it in an unmount effect that also nulls its ref. Under React 19
22
+ * concurrent rendering / StrictMode, that host node is orphaned — the sheet
23
+ * mounts but its DOM lands in a detached node and never paints. Bloom's Portal
24
+ * has no render-phase side effect and a stable host, so the sheet paints
25
+ * reliably. This is the SAME portal the `Dialog` `center`/side paths already
26
+ * use on web.
27
+ *
28
+ * `keyboardHeight` / `onRequestClose` are native-shell concerns (the browser
29
+ * owns keyboard layout and back-button semantics) and are intentionally unused
30
+ * here.
31
+ */
32
+ function WebShell({ children }: BottomSheetShellProps) {
33
+ return (
34
+ <Portal>
35
+ <GestureHandlerRootView style={webStyles.rootView} pointerEvents="box-none">
36
+ {children}
37
+ </GestureHandlerRootView>
38
+ </Portal>
39
+ );
40
+ }
41
+
42
+ const BottomSheet = forwardRef((props: BottomSheetProps, ref: React.ForwardedRef<BottomSheetRef>) => (
43
+ <BottomSheetBase {...props} ref={ref} Shell={WebShell} />
44
+ ));
45
+
46
+ BottomSheet.displayName = 'BottomSheet';
47
+
48
+ const webStyles = StyleSheet.create({
49
+ rootView: {
50
+ // The bloom Portal root is `position: fixed; inset: 0; pointer-events:
51
+ // none`; this fixed, full-viewport root re-enables pointer events for
52
+ // the sheet's own interactive descendants (backdrop, sheet) while empty
53
+ // gaps stay click-through (`box-none`). `StyleSheet.absoluteFill` from
54
+ // the sheet body then anchors to this box.
55
+ position: 'fixed' as ViewStyle['position'],
56
+ top: 0,
57
+ left: 0,
58
+ right: 0,
59
+ bottom: 0,
60
+ zIndex: Z_INDEX.portalRoot,
61
+ },
62
+ });
63
+
64
+ export type { BottomSheetProps, BottomSheetRef };
65
+ export default BottomSheet;
66
+ export { BottomSheet };
package/src/index.web.ts CHANGED
@@ -144,8 +144,8 @@ export { Combobox } from './combobox/index.web';
144
144
  export type { ComboboxProps, ComboboxOption } from './combobox/index.web';
145
145
 
146
146
  // Bottom sheet
147
- export { BottomSheet } from './bottom-sheet';
148
- export type { BottomSheetRef, BottomSheetProps } from './bottom-sheet';
147
+ export { BottomSheet } from './bottom-sheet/index.web';
148
+ export type { BottomSheetRef, BottomSheetProps } from './bottom-sheet/index.web';
149
149
 
150
150
  // Data display
151
151
  export * from './card';
@@ -0,0 +1,40 @@
1
+ import { parseRgb, withAlpha } from '../color-utils';
2
+
3
+ describe('color-utils', () => {
4
+ describe('parseRgb', () => {
5
+ it('parses canonical space-separated rgb', () => {
6
+ expect(parseRgb('rgb(31 153 239)')).toEqual({ r: 31, g: 153, b: 239 });
7
+ });
8
+ it('parses legacy comma-separated rgb / rgba', () => {
9
+ expect(parseRgb('rgb(31, 153, 239)')).toEqual({ r: 31, g: 153, b: 239 });
10
+ expect(parseRgb('rgba(31, 153, 239, 0.5)')).toEqual({ r: 31, g: 153, b: 239 });
11
+ });
12
+ it('parses #rrggbb and #rgb hex', () => {
13
+ expect(parseRgb('#1f99ef')).toEqual({ r: 31, g: 153, b: 239 });
14
+ expect(parseRgb('#abc')).toEqual({ r: 170, g: 187, b: 204 });
15
+ });
16
+ it('returns null on unparseable input', () => {
17
+ expect(parseRgb('hsl(277 50% 5%)')).toBeNull();
18
+ expect(parseRgb('transparent')).toBeNull();
19
+ expect(parseRgb('#12')).toBeNull();
20
+ expect(parseRgb('')).toBeNull();
21
+ });
22
+ });
23
+
24
+ describe('withAlpha', () => {
25
+ it('composes canonical space-separated rgb at the given alpha', () => {
26
+ expect(withAlpha('rgb(31 153 239)', 0.5)).toBe('rgba(31, 153, 239, 0.5)');
27
+ });
28
+ it('composes at alpha 0 (fully transparent, not the transparent keyword)', () => {
29
+ expect(withAlpha('rgb(31 153 239)', 0)).toBe('rgba(31, 153, 239, 0)');
30
+ });
31
+ it('handles legacy comma rgb and hex input', () => {
32
+ expect(withAlpha('rgb(31, 153, 239)', 0.5)).toBe('rgba(31, 153, 239, 0.5)');
33
+ expect(withAlpha('#1f99ef', 1)).toBe('rgba(31, 153, 239, 1)');
34
+ });
35
+ it('returns the input unchanged when it cannot be parsed', () => {
36
+ expect(withAlpha('hsl(277 50% 5%)', 0.5)).toBe('hsl(277 50% 5%)');
37
+ expect(withAlpha('transparent', 0)).toBe('transparent');
38
+ });
39
+ });
40
+ });
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Graceful color-utility primitives for consumer-facing composition.
3
+ *
4
+ * Unlike `parseRgbString` in `./color-space` (which THROWS on non-rgb input —
5
+ * it is a canonical-pipeline primitive where a bad value signals a real bug),
6
+ * these helpers fall back gracefully: they return `null` / the untouched input
7
+ * when a color string can't be parsed. That makes them safe to point at
8
+ * arbitrary consumer color values (theme tokens, user-supplied colors) — e.g.
9
+ * building `[bg, withAlpha(bg, 0)]` gradient stops for a scroll fade, where the
10
+ * `transparent` keyword would incorrectly fade through black.
11
+ */
12
+
13
+ /** RGB channels, each 0..255. */
14
+ export interface RgbChannels {
15
+ r: number;
16
+ g: number;
17
+ b: number;
18
+ }
19
+
20
+ /**
21
+ * Parse a color string into its RGB channels, or `null` if it can't be parsed.
22
+ *
23
+ * Accepts Bloom's canonical space-separated `rgb(r g b)` output, legacy
24
+ * comma-separated `rgb(r, g, b)` / `rgba(...)`, and `#rgb` / `#rrggbb` hex.
25
+ * Anything else yields `null` so callers can fall back to the original value.
26
+ */
27
+ export function parseRgb(color: string): RgbChannels | null {
28
+ const trimmed = color.trim();
29
+ if (trimmed.startsWith('#')) {
30
+ let hex = trimmed.slice(1);
31
+ if (hex.length === 3) hex = hex.split('').map((c) => c + c).join('');
32
+ if (hex.length !== 6) return null;
33
+ const r = parseInt(hex.slice(0, 2), 16);
34
+ const g = parseInt(hex.slice(2, 4), 16);
35
+ const b = parseInt(hex.slice(4, 6), 16);
36
+ if ([r, g, b].some((v) => Number.isNaN(v))) return null;
37
+ return { r, g, b };
38
+ }
39
+ const match = /rgba?\(([^)]+)\)/i.exec(trimmed);
40
+ if (!match || match[1] === undefined) return null;
41
+ const parts = match[1].split(/[\s,/]+/).filter(Boolean).map(Number);
42
+ if (parts.length < 3) return null;
43
+ const [r, g, b] = parts;
44
+ if (r === undefined || g === undefined || b === undefined) return null;
45
+ if ([r, g, b].some((v) => Number.isNaN(v))) return null;
46
+ return { r, g, b };
47
+ }
48
+
49
+ /** Compose `color` at `alpha`; returns the input unchanged if it can't parse. */
50
+ export function withAlpha(color: string, alpha: number): string {
51
+ const rgb = parseRgb(color);
52
+ if (!rgb) return color;
53
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
54
+ }
@@ -19,6 +19,8 @@ export {
19
19
  HEX_TO_APP_COLOR,
20
20
  hexToAppColorName,
21
21
  } from './color-presets';
22
+ export { parseRgb, withAlpha } from './color-utils';
23
+ export type { RgbChannels } from './color-utils';
22
24
  export { getPresetVars, applyPresetVarsToDocument } from './preset-vars';
23
25
  export { applyDarkClass } from './apply-dark-class';
24
26
  export { setColorSchemeSafe } from './set-color-scheme-safe';