@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.
- package/lib/commonjs/activity-heatmap/ActivityHeatmap.js +2 -37
- package/lib/commonjs/activity-heatmap/ActivityHeatmap.js.map +1 -1
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js +731 -0
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -0
- package/lib/commonjs/bottom-sheet/index.js +24 -694
- package/lib/commonjs/bottom-sheet/index.js.map +1 -1
- package/lib/commonjs/bottom-sheet/index.web.js +64 -0
- package/lib/commonjs/bottom-sheet/index.web.js.map +1 -0
- package/lib/commonjs/dialog/Dialog.js +2 -2
- package/lib/commonjs/dialog/Dialog.js.map +1 -1
- package/lib/commonjs/dialog/DialogBottomSheet.js +2 -2
- package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -1
- package/lib/commonjs/dialog/SheetShell.js +2 -2
- package/lib/commonjs/dialog/SheetShell.js.map +1 -1
- package/lib/commonjs/index.js +44 -44
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +66 -66
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/theme/color-utils.js +65 -0
- package/lib/commonjs/theme/color-utils.js.map +1 -0
- package/lib/commonjs/theme/index.js +13 -0
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/module/activity-heatmap/ActivityHeatmap.js +1 -36
- package/lib/module/activity-heatmap/ActivityHeatmap.js.map +1 -1
- package/lib/module/bottom-sheet/BottomSheetBase.js +728 -0
- package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -0
- package/lib/module/bottom-sheet/index.js +27 -696
- package/lib/module/bottom-sheet/index.js.map +1 -1
- package/lib/module/bottom-sheet/index.web.js +63 -0
- package/lib/module/bottom-sheet/index.web.js.map +1 -0
- package/lib/module/dialog/Dialog.js +1 -1
- package/lib/module/dialog/Dialog.js.map +1 -1
- package/lib/module/dialog/DialogBottomSheet.js +1 -1
- package/lib/module/dialog/DialogBottomSheet.js.map +1 -1
- package/lib/module/dialog/SheetShell.js +1 -1
- package/lib/module/dialog/SheetShell.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +1 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/theme/color-utils.js +60 -0
- package/lib/module/theme/color-utils.js.map +1 -0
- package/lib/module/theme/index.js +1 -0
- package/lib/module/theme/index.js.map +1 -1
- package/lib/typescript/commonjs/activity-heatmap/ActivityHeatmap.d.ts.map +1 -1
- package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts +127 -0
- package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts.map +1 -0
- package/lib/typescript/commonjs/bottom-sheet/index.d.ts +2 -90
- package/lib/typescript/commonjs/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/bottom-sheet/index.web.d.ts +7 -0
- package/lib/typescript/commonjs/bottom-sheet/index.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.web.d.ts +2 -2
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-utils.d.ts +28 -0
- package/lib/typescript/commonjs/theme/color-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/index.d.ts +2 -0
- package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
- package/lib/typescript/module/activity-heatmap/ActivityHeatmap.d.ts.map +1 -1
- package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts +127 -0
- package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts.map +1 -0
- package/lib/typescript/module/bottom-sheet/index.d.ts +2 -90
- package/lib/typescript/module/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/module/bottom-sheet/index.web.d.ts +7 -0
- package/lib/typescript/module/bottom-sheet/index.web.d.ts.map +1 -0
- package/lib/typescript/module/index.web.d.ts +2 -2
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-utils.d.ts +28 -0
- package/lib/typescript/module/theme/color-utils.d.ts.map +1 -0
- package/lib/typescript/module/theme/index.d.ts +2 -0
- package/lib/typescript/module/theme/index.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/__tests__/BottomSheetWebFork.test.ts +66 -0
- package/src/activity-heatmap/ActivityHeatmap.tsx +1 -29
- package/src/bottom-sheet/BottomSheetBase.tsx +853 -0
- package/src/bottom-sheet/index.tsx +26 -821
- package/src/bottom-sheet/index.web.tsx +66 -0
- package/src/index.web.ts +2 -2
- package/src/theme/__tests__/color-utils.test.ts +40 -0
- package/src/theme/color-utils.ts +54 -0
- 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
|
+
}
|
package/src/theme/index.ts
CHANGED
|
@@ -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';
|