@oxyhq/bloom 0.63.0 → 0.65.0

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 (53) hide show
  1. package/lib/commonjs/bottom-sheet/BottomSheetBase.js +2 -4
  2. package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
  3. package/lib/commonjs/connection-status/index.js +40 -0
  4. package/lib/commonjs/connection-status/index.js.map +1 -0
  5. package/lib/commonjs/connection-status/index.web.js +36 -0
  6. package/lib/commonjs/connection-status/index.web.js.map +1 -0
  7. package/lib/commonjs/connection-status/shared.js +68 -0
  8. package/lib/commonjs/connection-status/shared.js.map +1 -0
  9. package/lib/commonjs/dialog/Dialog.js +1 -2
  10. package/lib/commonjs/dialog/Dialog.js.map +1 -1
  11. package/lib/commonjs/dialog/Dialog.web.js +2 -2
  12. package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
  13. package/lib/commonjs/overlay/index.js +11 -3
  14. package/lib/commonjs/overlay/index.js.map +1 -1
  15. package/lib/module/bottom-sheet/BottomSheetBase.js +2 -4
  16. package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
  17. package/lib/module/connection-status/index.js +34 -0
  18. package/lib/module/connection-status/index.js.map +1 -0
  19. package/lib/module/connection-status/index.web.js +31 -0
  20. package/lib/module/connection-status/index.web.js.map +1 -0
  21. package/lib/module/connection-status/shared.js +65 -0
  22. package/lib/module/connection-status/shared.js.map +1 -0
  23. package/lib/module/dialog/Dialog.js +1 -2
  24. package/lib/module/dialog/Dialog.js.map +1 -1
  25. package/lib/module/dialog/Dialog.web.js +2 -2
  26. package/lib/module/dialog/Dialog.web.js.map +1 -1
  27. package/lib/module/overlay/index.js +11 -3
  28. package/lib/module/overlay/index.js.map +1 -1
  29. package/lib/typescript/commonjs/connection-status/index.d.ts +8 -0
  30. package/lib/typescript/commonjs/connection-status/index.d.ts.map +1 -0
  31. package/lib/typescript/commonjs/connection-status/index.web.d.ts +8 -0
  32. package/lib/typescript/commonjs/connection-status/index.web.d.ts.map +1 -0
  33. package/lib/typescript/commonjs/connection-status/shared.d.ts +24 -0
  34. package/lib/typescript/commonjs/connection-status/shared.d.ts.map +1 -0
  35. package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/overlay/index.d.ts.map +1 -1
  37. package/lib/typescript/module/connection-status/index.d.ts +8 -0
  38. package/lib/typescript/module/connection-status/index.d.ts.map +1 -0
  39. package/lib/typescript/module/connection-status/index.web.d.ts +8 -0
  40. package/lib/typescript/module/connection-status/index.web.d.ts.map +1 -0
  41. package/lib/typescript/module/connection-status/shared.d.ts +24 -0
  42. package/lib/typescript/module/connection-status/shared.d.ts.map +1 -0
  43. package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
  44. package/lib/typescript/module/overlay/index.d.ts.map +1 -1
  45. package/package.json +24 -3
  46. package/src/__tests__/overlay-pointer-events.test.tsx +32 -5
  47. package/src/bottom-sheet/BottomSheetBase.tsx +1 -3
  48. package/src/connection-status/index.tsx +38 -0
  49. package/src/connection-status/index.web.tsx +36 -0
  50. package/src/connection-status/shared.ts +83 -0
  51. package/src/dialog/Dialog.tsx +0 -1
  52. package/src/dialog/Dialog.web.tsx +1 -1
  53. package/src/overlay/index.tsx +11 -4
@@ -0,0 +1,24 @@
1
+ export interface ConnectionStatusToastsProps {
2
+ /** Shown as soon as the device reports no connection. */
3
+ offlineMessage?: string;
4
+ /** Replaces `offlineMessage` once the outage has lasted `reconnectingDelayMs`. */
5
+ reconnectingMessage?: string;
6
+ /** Shown briefly when the connection comes back. */
7
+ restoredMessage?: string;
8
+ /** How long an outage must last before it reads as "reconnecting". */
9
+ reconnectingDelayMs?: number;
10
+ /** How long the restored toast stays up. */
11
+ restoredDurationMs?: number;
12
+ }
13
+ /**
14
+ * Drive the connection toast from an `isOnline` signal.
15
+ *
16
+ * Platform-agnostic half of `ConnectionStatusToasts`: the forks differ only in
17
+ * where the signal comes from (NetInfo on native, `navigator.onLine` on web),
18
+ * never in what the user sees.
19
+ *
20
+ * `isOnline === null` means "not determined yet" and shows nothing — a first
21
+ * paint must not accuse the user of being offline.
22
+ */
23
+ export declare function useConnectionStatusToasts(isOnline: boolean | null, { offlineMessage, reconnectingMessage, restoredMessage, reconnectingDelayMs, restoredDurationMs, }?: ConnectionStatusToastsProps): void;
24
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/shared.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,2BAA2B;IAC1C,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAUD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,OAAO,GAAG,IAAI,EACxB,EACE,cAAwC,EACxC,mBAAkD,EAClD,eAA0C,EAC1C,mBAAkD,EAClD,kBAAgD,GACjD,GAAE,2BAAgC,GAClC,IAAI,CAmCN"}
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.tsx"],"names":[],"mappings":"AA2CA,OAAO,EAIL,4BAA4B,EAI7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,WAAW,2CAab;AAufD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAoBA"}
1
+ {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.tsx"],"names":[],"mappings":"AA2CA,OAAO,EAIL,4BAA4B,EAI7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,WAAW,2CAab;AAsfD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAoBA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/overlay/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAiB,EAAoB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAMvF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAMxE;yBANe,WAAW;;;AAU3B,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACxC,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,sFAAsF;IACtF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,qDAgEnB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/overlay/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAiB,EAAoB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAMvF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAMxE;yBANe,WAAW;;;AAU3B,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACxC,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,sFAAsF;IACtF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,qDAuEnB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type ConnectionStatusToastsProps } from './shared';
2
+ export type { ConnectionStatusToastsProps } from './shared';
3
+ /** Mount once at the app root. Renders nothing. */
4
+ export declare function ConnectionStatusToasts(props?: ConnectionStatusToastsProps): null;
5
+ export declare namespace ConnectionStatusToasts {
6
+ var displayName: string;
7
+ }
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/index.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAA6B,KAAK,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAEvF,YAAY,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAE5D,mDAAmD;AACnD,wBAAgB,sBAAsB,CAAC,KAAK,GAAE,2BAAgC,GAAG,IAAI,CAcpF;yBAde,sBAAsB"}
@@ -0,0 +1,8 @@
1
+ import { type ConnectionStatusToastsProps } from './shared';
2
+ export type { ConnectionStatusToastsProps } from './shared';
3
+ /** Mount once at the app root. Renders nothing. */
4
+ export declare function ConnectionStatusToasts(props?: ConnectionStatusToastsProps): null;
5
+ export declare namespace ConnectionStatusToasts {
6
+ var displayName: string;
7
+ }
8
+ //# sourceMappingURL=index.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/index.web.tsx"],"names":[],"mappings":"AAUA,OAAO,EAA6B,KAAK,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAEvF,YAAY,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAE5D,mDAAmD;AACnD,wBAAgB,sBAAsB,CAAC,KAAK,GAAE,2BAAgC,GAAG,IAAI,CAkBpF;yBAlBe,sBAAsB"}
@@ -0,0 +1,24 @@
1
+ export interface ConnectionStatusToastsProps {
2
+ /** Shown as soon as the device reports no connection. */
3
+ offlineMessage?: string;
4
+ /** Replaces `offlineMessage` once the outage has lasted `reconnectingDelayMs`. */
5
+ reconnectingMessage?: string;
6
+ /** Shown briefly when the connection comes back. */
7
+ restoredMessage?: string;
8
+ /** How long an outage must last before it reads as "reconnecting". */
9
+ reconnectingDelayMs?: number;
10
+ /** How long the restored toast stays up. */
11
+ restoredDurationMs?: number;
12
+ }
13
+ /**
14
+ * Drive the connection toast from an `isOnline` signal.
15
+ *
16
+ * Platform-agnostic half of `ConnectionStatusToasts`: the forks differ only in
17
+ * where the signal comes from (NetInfo on native, `navigator.onLine` on web),
18
+ * never in what the user sees.
19
+ *
20
+ * `isOnline === null` means "not determined yet" and shows nothing — a first
21
+ * paint must not accuse the user of being offline.
22
+ */
23
+ export declare function useConnectionStatusToasts(isOnline: boolean | null, { offlineMessage, reconnectingMessage, restoredMessage, reconnectingDelayMs, restoredDurationMs, }?: ConnectionStatusToastsProps): void;
24
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/connection-status/shared.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,2BAA2B;IAC1C,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAUD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,OAAO,GAAG,IAAI,EACxB,EACE,cAAwC,EACxC,mBAAkD,EAClD,eAA0C,EAC1C,mBAAkD,EAClD,kBAAgD,GACjD,GAAE,2BAAgC,GAClC,IAAI,CAmCN"}
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.tsx"],"names":[],"mappings":"AA2CA,OAAO,EAIL,4BAA4B,EAI7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,WAAW,2CAab;AAufD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAoBA"}
1
+ {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.tsx"],"names":[],"mappings":"AA2CA,OAAO,EAIL,4BAA4B,EAI7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,WAAW,2CAab;AAsfD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAoBA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/overlay/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAiB,EAAoB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAMvF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAMxE;yBANe,WAAW;;;AAU3B,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACxC,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,sFAAsF;IACtF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,qDAgEnB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/overlay/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAiB,EAAoB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAMvF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAMxE;yBANe,WAAW;;;AAU3B,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACxC,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,sFAAsF;IACtF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,qDAuEnB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.63.0",
3
+ "version": "0.65.0",
4
4
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -148,6 +148,22 @@
148
148
  "default": "./lib/commonjs/portal/index.js"
149
149
  }
150
150
  },
151
+ "./connection-status": {
152
+ "react-native": "./src/connection-status/index.tsx",
153
+ "browser": {
154
+ "types": "./lib/typescript/module/connection-status/index.web.d.ts",
155
+ "import": "./lib/module/connection-status/index.web.js",
156
+ "require": "./lib/commonjs/connection-status/index.web.js"
157
+ },
158
+ "import": {
159
+ "types": "./lib/typescript/module/connection-status/index.d.ts",
160
+ "default": "./lib/module/connection-status/index.js"
161
+ },
162
+ "require": {
163
+ "types": "./lib/typescript/commonjs/connection-status/index.d.ts",
164
+ "default": "./lib/commonjs/connection-status/index.js"
165
+ }
166
+ },
151
167
  "./overlay": {
152
168
  "react-native": "./src/overlay/index.tsx",
153
169
  "import": {
@@ -1210,7 +1226,8 @@
1210
1226
  "storybook": "^10",
1211
1227
  "ts-jest": "^29.4.6",
1212
1228
  "typescript": "~5.9.2",
1213
- "vite": "^7"
1229
+ "vite": "^7",
1230
+ "@react-native-community/netinfo": "^12.0.1"
1214
1231
  },
1215
1232
  "peerDependencies": {
1216
1233
  "expo": "*",
@@ -1229,7 +1246,8 @@
1229
1246
  "react-native-safe-area-context": ">=5.0.0",
1230
1247
  "react-native-screens": ">=3.16.0",
1231
1248
  "react-native-svg": ">=13.0.0",
1232
- "nativewind": ">=5.0.0"
1249
+ "nativewind": ">=5.0.0",
1250
+ "@react-native-community/netinfo": ""
1233
1251
  },
1234
1252
  "peerDependenciesMeta": {
1235
1253
  "expo": {
@@ -1249,6 +1267,9 @@
1249
1267
  },
1250
1268
  "nativewind": {
1251
1269
  "optional": true
1270
+ },
1271
+ "@react-native-community/netinfo": {
1272
+ "optional": true
1252
1273
  }
1253
1274
  },
1254
1275
  "react-native-builder-bob": {
@@ -28,8 +28,8 @@ import { Backdrop, OverlayRoot } from '../overlay';
28
28
  (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT =
29
29
  true;
30
30
 
31
- /** Every `pointer-events` declaration react-native-web injected for `className`. */
32
- function pointerEventsRulesFor(className: string): string[] {
31
+ /** Every injected rule that targets one of `className`'s classes. */
32
+ function rulesFor(className: string): string[] {
33
33
  const classes = className.split(/\s+/).filter(Boolean);
34
34
  const out: string[] = [];
35
35
  for (const sheet of Array.from(document.styleSheets)) {
@@ -41,7 +41,6 @@ function pointerEventsRulesFor(className: string): string[] {
41
41
  }
42
42
  for (const rule of Array.from(rules)) {
43
43
  const text = rule.cssText ?? '';
44
- if (!text.includes('pointer-events')) continue;
45
44
  if (classes.some((c) => text.includes(`.${c}`))) out.push(text);
46
45
  }
47
46
  }
@@ -69,7 +68,7 @@ describe('overlay pointer-events contract (web)', () => {
69
68
 
70
69
  const el = container.querySelector('[data-testid="root"]');
71
70
  expect(el).not.toBeNull();
72
- const rules = pointerEventsRulesFor(el?.className ?? '');
71
+ const rules = rulesFor(el?.className ?? '');
73
72
 
74
73
  // The two halves of react-native-web's `box-none`: the box itself does not
75
74
  // take events (empty gaps stay click-through), direct children do.
@@ -92,7 +91,7 @@ describe('overlay pointer-events contract (web)', () => {
92
91
  // Without this the element inherits the portal root's `none` and the press
93
92
  // below can never happen in a real browser.
94
93
  expect(
95
- pointerEventsRulesFor(el?.className ?? '').some((r) =>
94
+ rulesFor(el?.className ?? '').some((r) =>
96
95
  /pointer-events:\s*auto/.test(r),
97
96
  ),
98
97
  ).toBe(true);
@@ -148,6 +147,34 @@ describe('overlay pointer-events contract (web)', () => {
148
147
  container.remove();
149
148
  });
150
149
 
150
+ // The other half of the same rule: a background on the press target paints an
151
+ // opaque sheet UNDER the blur — a second backdrop. Menus and sheets shipped
152
+ // exactly that (their `styles.backdrop` still carried `#000`) and rendered
153
+ // solid black while the image viewer, which passed no background, looked
154
+ // right. A caller's colour is the DIM's colour, so it moves to the layer.
155
+ it('never paints the press target — a background becomes the dim colour', () => {
156
+ const { root, container } = render(
157
+ createElement(Backdrop, {
158
+ onPress: () => {},
159
+ testID: 'backdrop',
160
+ style: { backgroundColor: 'rgb(1, 2, 3)' },
161
+ }),
162
+ );
163
+
164
+ const el = container.querySelector('[data-testid="backdrop"]') as HTMLElement;
165
+ const rootBg = el.style.backgroundColor || getComputedStyle(el).backgroundColor;
166
+ expect(rootBg === '' || rootBg === 'rgba(0, 0, 0, 0)' || rootBg === 'transparent').toBe(true);
167
+
168
+ // Where the colour LANDS (the dim layer) is asserted in a browser: the dim
169
+ // is a reanimated `Animated.View`, which this suite mocks, so it never
170
+ // reaches the DOM here. What matters and is observable is that the press
171
+ // target above the blur stays transparent.
172
+ expect(el.children.length).toBeGreaterThanOrEqual(2);
173
+
174
+ act(() => root.unmount());
175
+ container.remove();
176
+ });
177
+
151
178
  it('a disabled Backdrop dims without dismissing', () => {
152
179
  const onPress = jest.fn();
153
180
  const { root, container } = render(
@@ -844,10 +844,8 @@ BottomSheetBase.displayName = 'BottomSheetBase';
844
844
 
845
845
  const styles = StyleSheet.create({
846
846
  backdrop: {
847
- // Color is solid black; the dim level is driven by `backdropOpacity` via
848
- // the animated `opacity` style on the wrapping <Animated.View>.
847
+ // Geometry only: the colour and the dim level belong to `Backdrop`.
849
848
  flex: 1,
850
- backgroundColor: '#000',
851
849
  },
852
850
  backdropTouchable: {
853
851
  flex: 1,
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Connection loss as a toast, not a layout-shifting banner.
3
+ *
4
+ * Every Oxy app needs this and none of them should hand-roll it: a coloured bar
5
+ * sliding in above the header pushes the whole screen down, has to be themed
6
+ * again in each app, and competes with the app's own chrome. The toast host is
7
+ * already mounted, already themed, already stacks.
8
+ *
9
+ * NATIVE variant: `@react-native-community/netinfo` (an Expo-managed dependency
10
+ * every Oxy app already ships) reports both link state and reachability. The
11
+ * web fork uses `navigator.onLine` instead so Vite/SPA consumers need no extra
12
+ * dependency at all.
13
+ */
14
+ import { useEffect, useState } from 'react';
15
+ import NetInfo from '@react-native-community/netinfo';
16
+
17
+ import { useConnectionStatusToasts, type ConnectionStatusToastsProps } from './shared';
18
+
19
+ export type { ConnectionStatusToastsProps } from './shared';
20
+
21
+ /** Mount once at the app root. Renders nothing. */
22
+ export function ConnectionStatusToasts(props: ConnectionStatusToastsProps = {}): null {
23
+ const [isOnline, setIsOnline] = useState<boolean | null>(null);
24
+
25
+ useEffect(() => {
26
+ // `isInternetReachable` is null until the first probe resolves; treat that
27
+ // as "connected as far as we know" so a captive-portal check never flashes
28
+ // an outage on launch.
29
+ return NetInfo.addEventListener((state) => {
30
+ setIsOnline(Boolean(state.isConnected && state.isInternetReachable !== false));
31
+ });
32
+ }, []);
33
+
34
+ useConnectionStatusToasts(isOnline, props);
35
+ return null;
36
+ }
37
+
38
+ ConnectionStatusToasts.displayName = 'ConnectionStatusToasts';
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Web variant — see `./index.tsx` for what this is and why it is a toast.
3
+ *
4
+ * `navigator.onLine` + the `online`/`offline` events are the browser's own
5
+ * signal, so a Vite/SPA consumer needs no native dependency to get the same
6
+ * behaviour. (The browser only knows about the link, not reachability; that is
7
+ * the honest limit of what a web app can detect without polling.)
8
+ */
9
+ import { useEffect, useState } from 'react';
10
+
11
+ import { useConnectionStatusToasts, type ConnectionStatusToastsProps } from './shared';
12
+
13
+ export type { ConnectionStatusToastsProps } from './shared';
14
+
15
+ /** Mount once at the app root. Renders nothing. */
16
+ export function ConnectionStatusToasts(props: ConnectionStatusToastsProps = {}): null {
17
+ const [isOnline, setIsOnline] = useState<boolean | null>(null);
18
+
19
+ useEffect(() => {
20
+ if (typeof navigator === 'undefined' || typeof window === 'undefined') return undefined;
21
+
22
+ setIsOnline(navigator.onLine);
23
+ const update = () => setIsOnline(navigator.onLine);
24
+ window.addEventListener('online', update);
25
+ window.addEventListener('offline', update);
26
+ return () => {
27
+ window.removeEventListener('online', update);
28
+ window.removeEventListener('offline', update);
29
+ };
30
+ }, []);
31
+
32
+ useConnectionStatusToasts(isOnline, props);
33
+ return null;
34
+ }
35
+
36
+ ConnectionStatusToasts.displayName = 'ConnectionStatusToasts';
@@ -0,0 +1,83 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ import { toast } from '../toast';
4
+
5
+ /** One id for the whole lifecycle, so the toast updates in place. */
6
+ const TOAST_ID = 'bloom-connection-status';
7
+
8
+ export interface ConnectionStatusToastsProps {
9
+ /** Shown as soon as the device reports no connection. */
10
+ offlineMessage?: string;
11
+ /** Replaces `offlineMessage` once the outage has lasted `reconnectingDelayMs`. */
12
+ reconnectingMessage?: string;
13
+ /** Shown briefly when the connection comes back. */
14
+ restoredMessage?: string;
15
+ /** How long an outage must last before it reads as "reconnecting". */
16
+ reconnectingDelayMs?: number;
17
+ /** How long the restored toast stays up. */
18
+ restoredDurationMs?: number;
19
+ }
20
+
21
+ const DEFAULTS = {
22
+ offlineMessage: 'No internet connection',
23
+ reconnectingMessage: 'Reconnecting…',
24
+ restoredMessage: 'Back online',
25
+ reconnectingDelayMs: 3000,
26
+ restoredDurationMs: 2500,
27
+ } as const;
28
+
29
+ /**
30
+ * Drive the connection toast from an `isOnline` signal.
31
+ *
32
+ * Platform-agnostic half of `ConnectionStatusToasts`: the forks differ only in
33
+ * where the signal comes from (NetInfo on native, `navigator.onLine` on web),
34
+ * never in what the user sees.
35
+ *
36
+ * `isOnline === null` means "not determined yet" and shows nothing — a first
37
+ * paint must not accuse the user of being offline.
38
+ */
39
+ export function useConnectionStatusToasts(
40
+ isOnline: boolean | null,
41
+ {
42
+ offlineMessage = DEFAULTS.offlineMessage,
43
+ reconnectingMessage = DEFAULTS.reconnectingMessage,
44
+ restoredMessage = DEFAULTS.restoredMessage,
45
+ reconnectingDelayMs = DEFAULTS.reconnectingDelayMs,
46
+ restoredDurationMs = DEFAULTS.restoredDurationMs,
47
+ }: ConnectionStatusToastsProps = {},
48
+ ): void {
49
+ // Whether an outage toast is currently up, so coming back online only
50
+ // announces itself to someone who saw the outage.
51
+ const wasOffline = useRef(false);
52
+
53
+ useEffect(() => {
54
+ if (isOnline === null) return undefined;
55
+
56
+ if (!isOnline) {
57
+ wasOffline.current = true;
58
+ // Persistent: an outage ends when the network says so, not on a timer.
59
+ toast.error(offlineMessage, { id: TOAST_ID, duration: Infinity, dismissible: false });
60
+ const escalate = setTimeout(() => {
61
+ toast.warning(reconnectingMessage, {
62
+ id: TOAST_ID,
63
+ duration: Infinity,
64
+ dismissible: false,
65
+ });
66
+ }, reconnectingDelayMs);
67
+ return () => clearTimeout(escalate);
68
+ }
69
+
70
+ if (wasOffline.current) {
71
+ wasOffline.current = false;
72
+ toast.success(restoredMessage, { id: TOAST_ID, duration: restoredDurationMs });
73
+ }
74
+ return undefined;
75
+ }, [
76
+ isOnline,
77
+ offlineMessage,
78
+ reconnectingMessage,
79
+ restoredMessage,
80
+ reconnectingDelayMs,
81
+ restoredDurationMs,
82
+ ]);
83
+ }
@@ -587,7 +587,6 @@ const sideStyles = StyleSheet.create({
587
587
  },
588
588
  backdrop: {
589
589
  ...StyleSheet.absoluteFill,
590
- backgroundColor: '#000',
591
590
  },
592
591
  panel: {
593
592
  position: 'absolute',
@@ -851,12 +851,12 @@ const sheetStyles: Record<'root' | 'backdrop' | 'panel', ViewStyle> = {
851
851
  bottom: 0,
852
852
  },
853
853
  backdrop: {
854
+ // Geometry only: the colour and the dim level belong to `Backdrop`.
854
855
  position: 'absolute',
855
856
  top: 0,
856
857
  left: 0,
857
858
  right: 0,
858
859
  bottom: 0,
859
- backgroundColor: '#000',
860
860
  },
861
861
  panel: {
862
862
  position: 'absolute',
@@ -151,11 +151,18 @@ export const Backdrop = memo(function Backdrop({
151
151
  }: BackdropProps) {
152
152
  const inert = disabled || !onPress;
153
153
 
154
- // An `opacity` handed in through `style` would sit on the ancestor of the
155
- // blur and kill it; hoist it onto the layers instead of honouring it there.
154
+ // The press target must stay a pure hit box. Two things a caller's `style`
155
+ // can carry would break the visuals if honoured there:
156
+ // - `opacity`, because `backdrop-filter` samples nothing under an ancestor
157
+ // that composites in isolation, so the fade would erase the blur;
158
+ // - `backgroundColor`, which would paint an opaque sheet UNDER the blur —
159
+ // a second backdrop, and the reason menus and sheets rendered solid black
160
+ // while the image viewer (which passed no background) looked right.
161
+ // Both are hoisted onto the layers, where they mean what the caller intended.
156
162
  const flat: ViewStyle = StyleSheet.flatten(style) ?? {};
157
- const { opacity: styleOpacity, ...rootStyle } = flat;
163
+ const { opacity: styleOpacity, backgroundColor: styleBackground, ...rootStyle } = flat;
158
164
  const staticOpacity = typeof styleOpacity === 'number' ? styleOpacity : 1;
165
+ const resolvedDimColor = typeof styleBackground === 'string' ? styleBackground : dimColor;
159
166
 
160
167
  // The fade lives on each LAYER, never on their shared ancestor.
161
168
  const blurFade = useAnimatedStyle(
@@ -194,7 +201,7 @@ export const Backdrop = memo(function Backdrop({
194
201
  ) : null}
195
202
  <Animated.View
196
203
  pointerEvents="none"
197
- style={[StyleSheet.absoluteFill, { backgroundColor: dimColor }, layerStyle, dimFade]}
204
+ style={[StyleSheet.absoluteFill, { backgroundColor: resolvedDimColor }, layerStyle, dimFade]}
198
205
  />
199
206
  {children}
200
207
  </Pressable>