@rnzeus/atlas 0.1.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 (110) hide show
  1. package/dist/README.md +21 -0
  2. package/dist/config/internal.d.ts +1 -0
  3. package/dist/config/internal.js +1 -0
  4. package/dist/config/navigation/index.d.ts +1 -0
  5. package/dist/config/navigation/index.js +1 -0
  6. package/dist/config/navigation/types.d.ts +55 -0
  7. package/dist/config/navigation/types.js +1 -0
  8. package/dist/config/navigation.types.d.ts +25 -0
  9. package/dist/config/navigation.types.js +1 -0
  10. package/dist/hooks/README.md +11 -0
  11. package/dist/hooks/index.d.ts +1 -0
  12. package/dist/hooks/index.js +1 -0
  13. package/dist/hooks/internal.d.ts +1 -0
  14. package/dist/hooks/internal.js +1 -0
  15. package/dist/hooks/use-path-navigation/README.md +66 -0
  16. package/dist/hooks/use-path-navigation/hook.d.ts +119 -0
  17. package/dist/hooks/use-path-navigation/hook.js +52 -0
  18. package/dist/hooks/use-path-navigation/index.d.ts +1 -0
  19. package/dist/hooks/use-path-navigation/index.js +1 -0
  20. package/dist/services/README.md +17 -0
  21. package/dist/services/index.d.ts +4 -0
  22. package/dist/services/index.js +3 -0
  23. package/dist/services/internal.d.ts +1 -0
  24. package/dist/services/internal.js +1 -0
  25. package/dist/services/navigation/README.md +131 -0
  26. package/dist/services/navigation/api.d.ts +5 -0
  27. package/dist/services/navigation/api.js +11 -0
  28. package/dist/services/navigation/chain-builder.d.ts +16 -0
  29. package/dist/services/navigation/chain-builder.js +31 -0
  30. package/dist/services/navigation/index.d.ts +3 -0
  31. package/dist/services/navigation/index.js +3 -0
  32. package/dist/services/navigation/service.d.ts +55 -0
  33. package/dist/services/navigation/service.js +536 -0
  34. package/dist/services/navigation/types.d.ts +13 -0
  35. package/dist/services/navigation/types.js +1 -0
  36. package/dist/services/navigation/zod-adapter.d.ts +22 -0
  37. package/dist/services/navigation/zod-adapter.js +36 -0
  38. package/dist/services/transport/README.md +79 -0
  39. package/dist/services/transport/build-query.d.ts +2 -0
  40. package/dist/services/transport/build-query.js +3 -0
  41. package/dist/services/transport/create-transport-methods.d.ts +8 -0
  42. package/dist/services/transport/create-transport-methods.js +34 -0
  43. package/dist/services/transport/error-exception.d.ts +8 -0
  44. package/dist/services/transport/error-exception.js +16 -0
  45. package/dist/services/transport/index.d.ts +3 -0
  46. package/dist/services/transport/index.js +2 -0
  47. package/dist/services/transport/read-response-body.d.ts +3 -0
  48. package/dist/services/transport/read-response-body.js +27 -0
  49. package/dist/services/transport/service.d.ts +22 -0
  50. package/dist/services/transport/service.js +102 -0
  51. package/dist/services/transport/types.d.ts +4 -0
  52. package/dist/services/transport/types.js +1 -0
  53. package/dist/ui/README.md +17 -0
  54. package/dist/ui/debug-dock/README.md +58 -0
  55. package/dist/ui/debug-dock/debug-dock.d.ts +13 -0
  56. package/dist/ui/debug-dock/debug-dock.js +20 -0
  57. package/dist/ui/debug-dock/index.d.ts +2 -0
  58. package/dist/ui/debug-dock/index.js +1 -0
  59. package/dist/ui/debug-dock/styles.d.ts +8 -0
  60. package/dist/ui/debug-dock/styles.js +25 -0
  61. package/dist/ui/debug-dock/use-debug-dock.d.ts +20 -0
  62. package/dist/ui/debug-dock/use-debug-dock.js +71 -0
  63. package/dist/ui/index.d.ts +2 -0
  64. package/dist/ui/index.js +2 -0
  65. package/dist/ui/toast/README.md +62 -0
  66. package/dist/ui/toast/context.d.ts +7 -0
  67. package/dist/ui/toast/context.js +6 -0
  68. package/dist/ui/toast/index.d.ts +5 -0
  69. package/dist/ui/toast/index.js +2 -0
  70. package/dist/ui/toast/toast-plate/README.md +38 -0
  71. package/dist/ui/toast/toast-plate/index.d.ts +2 -0
  72. package/dist/ui/toast/toast-plate/index.js +1 -0
  73. package/dist/ui/toast/toast-plate/toast-plate.d.ts +9 -0
  74. package/dist/ui/toast/toast-plate/toast-plate.js +11 -0
  75. package/dist/ui/toast/toast-plate/use-toast-plate.d.ts +4 -0
  76. package/dist/ui/toast/toast-plate/use-toast-plate.js +33 -0
  77. package/dist/ui/toast/toast-provider.d.ts +10 -0
  78. package/dist/ui/toast/toast-provider.js +53 -0
  79. package/dist/ui/toast/toast-viewport/README.md +38 -0
  80. package/dist/ui/toast/toast-viewport/index.d.ts +2 -0
  81. package/dist/ui/toast/toast-viewport/index.js +1 -0
  82. package/dist/ui/toast/toast-viewport/styles.d.ts +5 -0
  83. package/dist/ui/toast/toast-viewport/styles.js +10 -0
  84. package/dist/ui/toast/toast-viewport/toast-viewport.d.ts +10 -0
  85. package/dist/ui/toast/toast-viewport/toast-viewport.js +13 -0
  86. package/dist/ui/toast/types.d.ts +25 -0
  87. package/dist/ui/toast/types.js +1 -0
  88. package/dist/ui/toast/use-toast.d.ts +1 -0
  89. package/dist/ui/toast/use-toast.js +5 -0
  90. package/dist/utils/README.md +23 -0
  91. package/dist/utils/create-typography/README.md +71 -0
  92. package/dist/utils/create-typography/helper.d.ts +23 -0
  93. package/dist/utils/create-typography/helper.js +72 -0
  94. package/dist/utils/create-typography/index.d.ts +1 -0
  95. package/dist/utils/create-typography/index.js +1 -0
  96. package/dist/utils/define-route/README.md +50 -0
  97. package/dist/utils/define-route/helper.d.ts +2 -0
  98. package/dist/utils/define-route/helper.js +1 -0
  99. package/dist/utils/define-route/index.d.ts +1 -0
  100. package/dist/utils/define-route/index.js +1 -0
  101. package/dist/utils/display-name/README.md +46 -0
  102. package/dist/utils/display-name/helper.d.ts +2 -0
  103. package/dist/utils/display-name/helper.js +91 -0
  104. package/dist/utils/display-name/index.d.ts +1 -0
  105. package/dist/utils/display-name/index.js +1 -0
  106. package/dist/utils/index.d.ts +3 -0
  107. package/dist/utils/index.js +3 -0
  108. package/dist/utils/internal.d.ts +3 -0
  109. package/dist/utils/internal.js +3 -0
  110. package/package.json +74 -0
@@ -0,0 +1,79 @@
1
+ # `@rnzeus/atlas/services/transport`
2
+
3
+ ← [`@rnzeus/atlas/services`](../README.md)
4
+
5
+ ## Purpose
6
+
7
+ Base `fetch` transport with query params, default headers, response parsing, and normalized errors. Provides `get/post/put/delete` helpers on top of a single request pipeline.
8
+
9
+ ## Import
10
+
11
+ ```ts
12
+ import {Transport} from '@rnzeus/atlas/services';
13
+ ```
14
+
15
+ ## API (public surface)
16
+
17
+ - `class Transport` (abstract)
18
+ - **required**: `protected get baseURL(): string`
19
+ - **required**: `protected formatErrorMessage(data: unknown, status: number): string`
20
+ - **optional**: `protected notify(message: string): void` (default: no-op)
21
+ - `headers: HeadersInit_` (get/set, merges with defaults)
22
+ - `get<T>(url, params?, options?)`
23
+ - `post<T>(url, body, options?)`
24
+ - `put<T>(url, body, options?)`
25
+ - `delete<T>(url, params?, options?)`
26
+ - `class TransportErrorException extends Error`
27
+ - `status: number`
28
+ - `data: unknown`
29
+ - `isServerError()`
30
+ - `isClientError()`
31
+ - `type TransportOptions`
32
+ - `RequestInit` + `headers?: HeadersInit_` + `responseType?: 'arrayBuffer' | 'json'`
33
+
34
+ ## Usage (minimal)
35
+
36
+ ```ts
37
+ import {Transport} from '@rnzeus/atlas/services';
38
+
39
+ class ApiTransport extends Transport {
40
+ protected get baseURL() {
41
+ return 'https://api.example.com/';
42
+ }
43
+
44
+ protected formatErrorMessage(data: unknown, status: number) {
45
+ const msg = typeof data === 'object' && data && (data as any).message;
46
+ return msg ? String(msg) : `HTTP ${status}`;
47
+ }
48
+ }
49
+
50
+ const api = new ApiTransport();
51
+ api.headers = {Authorization: 'Bearer token'};
52
+
53
+ const user = await api.get<User>('/v1/user', {id: '42'});
54
+ ```
55
+
56
+ ## Notes
57
+
58
+ - **Relative vs absolute URLs**: relative paths are resolved against `baseURL`; absolute (`http(s)://`) are used as-is.
59
+ - **Trailing slash**: the final resolved URL has a trailing `/` removed.
60
+ - **Query params**: `params` are serialized with `qs` using `arrayFormat: 'brackets'`.
61
+ - **Headers**: default headers are merged with per-request headers; `Content-Type` is removed automatically for `FormData`.
62
+ - **Response parsing**:
63
+ - `options.responseType === 'arrayBuffer'` forces `arrayBuffer()`
64
+ - otherwise parses by `content-type` (`json`, `formData`, `text`), with a JSON→text fallback
65
+ - **Errors**:
66
+ - non-OK responses throw `TransportErrorException(status, message, data)`
67
+ - 5xx triggers `notify(message)` before throwing
68
+ - `AbortError` is rethrown as-is
69
+ - unknown errors are wrapped into `TransportErrorException(0, message)`
70
+
71
+ ## Sources
72
+
73
+ - [`service`](./service.js)
74
+ - [`types`](./types.js)
75
+ - [`error-exception`](./error-exception.js)
76
+ - [`create-transport-methods`](./create-transport-methods.js)
77
+ - [`read-response-body`](./read-response-body.js)
78
+ - [`build-query`](./build-query.js)
79
+ - [`index`](./index.js)
@@ -0,0 +1,2 @@
1
+ declare const buildTransportQuery: (params?: Record<string, unknown> | null) => string;
2
+ export default buildTransportQuery;
@@ -0,0 +1,3 @@
1
+ import qs from 'qs';
2
+ const buildTransportQuery = (params) => params ? qs.stringify(params, { arrayFormat: 'brackets', encode: true }) : '';
3
+ export default buildTransportQuery;
@@ -0,0 +1,8 @@
1
+ import type { TransportOptions } from './types';
2
+ declare const createTransportMethods: (requestFn: <T>(url: string, options: TransportOptions, paramsOrBody?: Record<string, unknown> | null) => Promise<T>) => {
3
+ get<T>(url: string, params?: Record<string, unknown> | null, options?: TransportOptions): Promise<T>;
4
+ post<T>(url: string, body: unknown, options?: TransportOptions): Promise<T>;
5
+ put<T>(url: string, body: unknown, options?: TransportOptions): Promise<T>;
6
+ delete<T>(url: string, params?: Record<string, unknown> | null, options?: TransportOptions): Promise<T>;
7
+ };
8
+ export default createTransportMethods;
@@ -0,0 +1,34 @@
1
+ function isBodyFormData(v) {
2
+ return typeof FormData !== 'undefined' && v instanceof FormData;
3
+ }
4
+ function buildRequestInitWithBody(options, method, body) {
5
+ const init = { ...options, method };
6
+ if (body === undefined || body === null)
7
+ return init;
8
+ if (isBodyFormData(body)) {
9
+ init.body = body;
10
+ }
11
+ else {
12
+ init.body = JSON.stringify(body);
13
+ }
14
+ return init;
15
+ }
16
+ const createTransportMethods = (requestFn) => {
17
+ return {
18
+ get(url, params = null, options = {}) {
19
+ return requestFn(url, { ...options, method: 'GET' }, params);
20
+ },
21
+ post(url, body, options = {}) {
22
+ const init = buildRequestInitWithBody(options, 'POST', body);
23
+ return requestFn(url, init);
24
+ },
25
+ put(url, body, options = {}) {
26
+ const init = buildRequestInitWithBody(options, 'PUT', body);
27
+ return requestFn(url, init);
28
+ },
29
+ delete(url, params = null, options = {}) {
30
+ return requestFn(url, { ...options, method: 'DELETE' }, params);
31
+ },
32
+ };
33
+ };
34
+ export default createTransportMethods;
@@ -0,0 +1,8 @@
1
+ declare class TransportErrorException extends Error {
2
+ readonly status: number;
3
+ readonly data: unknown;
4
+ constructor(status: number, message: string, data?: unknown);
5
+ isServerError(): boolean;
6
+ isClientError(): boolean;
7
+ }
8
+ export default TransportErrorException;
@@ -0,0 +1,16 @@
1
+ class TransportErrorException extends Error {
2
+ constructor(status, message, data) {
3
+ super(message);
4
+ this.name = 'TransportErrorException';
5
+ this.status = status;
6
+ this.data = data;
7
+ Object.setPrototypeOf(this, TransportErrorException.prototype);
8
+ }
9
+ isServerError() {
10
+ return this.status >= 500;
11
+ }
12
+ isClientError() {
13
+ return this.status >= 400 && this.status < 500;
14
+ }
15
+ }
16
+ export default TransportErrorException;
@@ -0,0 +1,3 @@
1
+ export { default as TransportErrorException } from './error-exception';
2
+ export { default as Transport } from './service';
3
+ export type { TransportOptions } from './types';
@@ -0,0 +1,2 @@
1
+ export { default as TransportErrorException } from './error-exception';
2
+ export { default as Transport } from './service';
@@ -0,0 +1,3 @@
1
+ import type { TransportOptions } from './types';
2
+ declare const readTransportResponseBody: (response: Response, options?: TransportOptions, contentType?: string) => Promise<unknown>;
3
+ export default readTransportResponseBody;
@@ -0,0 +1,27 @@
1
+ const readTransportResponseBody = async (response, options = {}, contentType) => {
2
+ try {
3
+ if (options.responseType === 'arrayBuffer') {
4
+ return await response.arrayBuffer();
5
+ }
6
+ if (contentType?.includes('application/json')) {
7
+ return await response.json();
8
+ }
9
+ if (contentType?.includes('multipart/form-data')) {
10
+ return await response.formData();
11
+ }
12
+ if (contentType?.includes('text/')) {
13
+ return await response.text();
14
+ }
15
+ try {
16
+ return await response.json();
17
+ }
18
+ catch {
19
+ return await response.text();
20
+ }
21
+ }
22
+ catch (error) {
23
+ console.error('Error reading response body:', error);
24
+ return await response.text();
25
+ }
26
+ };
27
+ export default readTransportResponseBody;
@@ -0,0 +1,22 @@
1
+ import type { TransportOptions } from './types';
2
+ export default abstract class Transport {
3
+ private headersDefault;
4
+ protected abstract get baseURL(): string;
5
+ protected abstract formatErrorMessage(data: unknown, status: number): string;
6
+ protected notify(_message: string): void;
7
+ get headers(): HeadersInit_;
8
+ set headers(next: HeadersInit_);
9
+ protected request<T>(url: string, options?: TransportOptions, params?: Record<string, unknown> | null): Promise<T>;
10
+ protected transportMethods: {
11
+ get<T>(url: string, params?: Record<string, unknown> | null, options?: TransportOptions): Promise<T>;
12
+ post<T>(url: string, body: unknown, options?: TransportOptions): Promise<T>;
13
+ put<T>(url: string, body: unknown, options?: TransportOptions): Promise<T>;
14
+ delete<T>(url: string, params?: Record<string, unknown> | null, options?: TransportOptions): Promise<T>;
15
+ };
16
+ get: <T>(url: string, params?: Record<string, unknown> | null, options?: TransportOptions) => Promise<T>;
17
+ post: <T>(url: string, body: unknown, options?: TransportOptions) => Promise<T>;
18
+ put: <T>(url: string, body: unknown, options?: TransportOptions) => Promise<T>;
19
+ delete: <T>(url: string, params?: Record<string, unknown> | null, options?: TransportOptions) => Promise<T>;
20
+ private headersToRecord;
21
+ private withoutContentType;
22
+ }
@@ -0,0 +1,102 @@
1
+ import buildTransportQuery from './build-query';
2
+ import createTransportMethods from './create-transport-methods';
3
+ import TransportErrorException from './error-exception';
4
+ import readTransportResponseBody from './read-response-body';
5
+ export default class Transport {
6
+ constructor() {
7
+ this.headersDefault = {
8
+ 'Content-Type': 'application/json',
9
+ Accept: 'application/json',
10
+ };
11
+ this.transportMethods = createTransportMethods(this.request.bind(this));
12
+ this.get = this.transportMethods.get.bind(this.transportMethods);
13
+ this.post = this.transportMethods.post.bind(this.transportMethods);
14
+ this.put = this.transportMethods.put.bind(this.transportMethods);
15
+ this.delete = this.transportMethods.delete.bind(this.transportMethods);
16
+ }
17
+ notify(_message) {
18
+ /* no-op */
19
+ }
20
+ get headers() {
21
+ return this.headersDefault;
22
+ }
23
+ set headers(next) {
24
+ this.headersDefault = {
25
+ ...this.headersDefault,
26
+ ...this.headersToRecord(next),
27
+ };
28
+ }
29
+ async request(url, options = {}, params) {
30
+ const search = buildTransportQuery(params);
31
+ const urlWithQuery = `${url}${search ? `?${search}` : ''}`;
32
+ const optionHeaders = this.headersToRecord(options.headers);
33
+ let mergedHeaders = {
34
+ ...this.headersDefault,
35
+ ...optionHeaders,
36
+ };
37
+ if (options.body instanceof FormData) {
38
+ mergedHeaders = this.withoutContentType(mergedHeaders);
39
+ }
40
+ const isAbsolute = urlWithQuery.startsWith('http://') || urlWithQuery.startsWith('https://');
41
+ const base = this.baseURL;
42
+ let finalUrl = isAbsolute ? urlWithQuery : new URL(urlWithQuery, base).toString();
43
+ if (finalUrl.endsWith('/'))
44
+ finalUrl = finalUrl.slice(0, -1);
45
+ try {
46
+ const response = await fetch(finalUrl, { ...options, headers: mergedHeaders });
47
+ const contentType = response.headers.get('content-type') ?? undefined;
48
+ const responseData = await readTransportResponseBody(response, options, contentType);
49
+ if (!response.ok) {
50
+ const message = this.formatErrorMessage(responseData, response.status);
51
+ const transportErrorException = new TransportErrorException(response.status, message, responseData);
52
+ if (transportErrorException.isServerError()) {
53
+ this.notify(message);
54
+ }
55
+ throw transportErrorException;
56
+ }
57
+ return responseData;
58
+ }
59
+ catch (error) {
60
+ if (error instanceof Error && error.name === 'AbortError') {
61
+ console.warn('⚠️ [Transport] Fetch aborted:', url);
62
+ throw error;
63
+ }
64
+ if (error instanceof TransportErrorException) {
65
+ throw error;
66
+ }
67
+ console.error('❌ [Transport] Fetch error:', error);
68
+ throw new TransportErrorException(0, error instanceof Error ? error.message : 'Unknown error');
69
+ }
70
+ }
71
+ headersToRecord(input) {
72
+ if (!input)
73
+ return {};
74
+ if (typeof Headers !== 'undefined' && input instanceof Headers) {
75
+ const out = {};
76
+ input.forEach((v, k) => (out[k] = v));
77
+ return out;
78
+ }
79
+ if (Array.isArray(input)) {
80
+ const out = {};
81
+ for (const pair of input) {
82
+ if (Array.isArray(pair) && typeof pair[0] === 'string' && typeof pair[1] === 'string') {
83
+ out[pair[0]] = pair[1];
84
+ }
85
+ }
86
+ return out;
87
+ }
88
+ if (typeof input === 'object') {
89
+ const out = {};
90
+ for (const [k, v] of Object.entries(input)) {
91
+ if (typeof k === 'string' && typeof v === 'string')
92
+ out[k] = v;
93
+ }
94
+ return out;
95
+ }
96
+ return {};
97
+ }
98
+ withoutContentType(h) {
99
+ const { ['Content-Type']: _omit, ...rest } = h;
100
+ return rest;
101
+ }
102
+ }
@@ -0,0 +1,4 @@
1
+ export type TransportOptions = Omit<RequestInit, 'headers'> & {
2
+ headers?: HeadersInit_;
3
+ responseType?: 'arrayBuffer' | 'json';
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ # `@rnzeus/atlas/ui`
2
+
3
+ ← [`@rnzeus/atlas`](../README.md)
4
+
5
+ ## Documentation
6
+
7
+ - **[`toast`](./toast/README.md)** — toast notifications: mount `ToastProvider` once near the app root, then call `useToast().show()` from screens or hooks. Provider owns the queue, viewport, and plate animations.
8
+
9
+ ```ts
10
+ import {ToastProvider, useToast} from '@rnzeus/atlas/ui';
11
+ ```
12
+
13
+ - **[`debug-dock`](./debug-dock/README.md)** — a dev-oriented floating strip docked to the right edge with a draggable peek drawer; wrap debug tools or secondary actions and disable in production with `enabled={false}` or conditional render.
14
+
15
+ ```ts
16
+ import {DebugDock} from '@rnzeus/atlas/ui';
17
+ ```
@@ -0,0 +1,58 @@
1
+ # `@rnzeus/atlas/ui/debug-dock`
2
+
3
+ ← [`@rnzeus/atlas/ui`](../README.md)
4
+
5
+ ## Purpose
6
+
7
+ Floating debug UI anchored to the top-right: a slim vertical drawer handle and a horizontal strip for shortcuts or dev-only controls. Uses Reanimated and Gesture Handler for vertical drag (reposition) and tap-to-toggle expand/peek along the X axis.
8
+
9
+ ## Import
10
+
11
+ ```ts
12
+ import {DebugDock} from '@rnzeus/atlas/ui';
13
+ ```
14
+
15
+ ## API (public surface)
16
+
17
+ - `DebugDock` — layout wrapper component.
18
+ - `DebugDockProps`:
19
+ - `children` — main strip content (laid out in a row).
20
+ - `enabled` — default `true`; when `false`, the component renders nothing.
21
+ - `height` — strip height in px (default `60`).
22
+ - `drawerWidth` — width of the left handle area in px (default `20`).
23
+ - `drawerPeek` — how many px of the dock stay visible when “closed” on the X axis; defaults to `drawerWidth + 3`.
24
+ - `drawerContent` — optional node for the handle (default is a chevron `Text`).
25
+
26
+ ## Usage (minimal)
27
+
28
+ ```tsx
29
+ import type {ReactNode} from 'react';
30
+ import {DebugDock} from '@rnzeus/atlas/ui';
31
+ import {Text} from 'react-native';
32
+
33
+ export function AppWithDebugShell({children}: {children: ReactNode}) {
34
+ const showDock = __DEV__;
35
+
36
+ return (
37
+ <>
38
+ {children}
39
+ <DebugDock enabled={showDock} drawerContent={<Text style={{color: '#fff'}}>⋮</Text>}>
40
+ <Text style={{color: '#fff'}}>Open menu</Text>
41
+ </DebugDock>
42
+ </>
43
+ );
44
+ }
45
+ ```
46
+
47
+ ## Notes
48
+
49
+ - Positioning is `absolute` at the top; vertical position respects device height and safe-area insets via `useDebugDock` (internal).
50
+ - Depends on `react-native-reanimated`, `react-native-gesture-handler`, and `react-native-safe-area-context` (peer expectations match a typical RN app with gesture + safe area already set up).
51
+ - Intended for **development / internal builds**; avoid shipping `enabled={true}` in production or gate with `__DEV__` / build flags.
52
+
53
+ ## Sources
54
+
55
+ - [`index`](./index.js)
56
+ - [`debug-dock`](./debug-dock.js)
57
+ - [`use-debug-dock`](./use-debug-dock.js)
58
+ - [`styles`](./styles.js)
@@ -0,0 +1,13 @@
1
+ import type { JSX, ReactNode } from 'react';
2
+ export type DebugDockProps = {
3
+ children: ReactNode;
4
+ enabled?: boolean;
5
+ height?: number;
6
+ drawerWidth?: number;
7
+ drawerPeek?: number;
8
+ drawerContent?: ReactNode;
9
+ };
10
+ export declare function DebugDock({ children, enabled, height, drawerWidth, drawerPeek, drawerContent, }: DebugDockProps): JSX.Element | null;
11
+ export declare namespace DebugDock {
12
+ var displayName: string;
13
+ }
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { GestureDetector } from 'react-native-gesture-handler';
4
+ import Animated from 'react-native-reanimated';
5
+ import { Text, View } from 'react-native';
6
+ import { displayName } from '../../utils';
7
+ import { styles } from './styles';
8
+ import { useDebugDock } from './use-debug-dock';
9
+ export function DebugDock({ children, enabled = true, height = 60, drawerWidth = 20, drawerPeek, drawerContent, }) {
10
+ const resolvedDrawerPeek = useMemo(() => drawerPeek ?? drawerWidth + 3, [drawerPeek, drawerWidth]);
11
+ const { containerAnimatedStyle, panGesture, onContainerLayout } = useDebugDock({
12
+ height,
13
+ drawerWidth,
14
+ drawerPeek: resolvedDrawerPeek,
15
+ });
16
+ if (!enabled)
17
+ return null;
18
+ return (_jsxs(Animated.View, { onLayout: onContainerLayout, style: [styles.container, { height }, containerAnimatedStyle], children: [_jsx(GestureDetector, { gesture: panGesture, children: _jsx(View, { style: [styles.drawer, { width: drawerWidth }], children: drawerContent ?? _jsx(Text, { style: styles.drawerIcon, children: "\u276E" }) }) }), _jsx(View, { style: styles.content, children: children })] }));
19
+ }
20
+ DebugDock.displayName = displayName('ui', 'debug-dock');
@@ -0,0 +1,2 @@
1
+ export type { DebugDockProps } from './debug-dock';
2
+ export { DebugDock } from './debug-dock';
@@ -0,0 +1 @@
1
+ export { DebugDock } from './debug-dock';
@@ -0,0 +1,8 @@
1
+ import type { TextStyle, ViewStyle } from 'react-native';
2
+ export type DebugDockStyles = {
3
+ readonly container: ViewStyle;
4
+ readonly drawer: ViewStyle;
5
+ readonly drawerIcon: TextStyle;
6
+ readonly content: ViewStyle;
7
+ };
8
+ export declare const styles: DebugDockStyles;
@@ -0,0 +1,25 @@
1
+ import { StyleSheet } from 'react-native';
2
+ export const styles = StyleSheet.create({
3
+ container: {
4
+ alignItems: 'center',
5
+ backgroundColor: '#444444',
6
+ borderBottomLeftRadius: 10,
7
+ borderTopLeftRadius: 10,
8
+ flexDirection: 'row',
9
+ height: 60,
10
+ justifyContent: 'center',
11
+ paddingRight: 20,
12
+ position: 'absolute',
13
+ top: 0,
14
+ },
15
+ content: { flexDirection: 'row', gap: 20 },
16
+ drawer: {
17
+ alignItems: 'center',
18
+ borderBottomLeftRadius: 10,
19
+ borderTopLeftRadius: 10,
20
+ height: '100%',
21
+ justifyContent: 'center',
22
+ width: 20,
23
+ },
24
+ drawerIcon: { color: 'white', fontSize: 30 },
25
+ });
@@ -0,0 +1,20 @@
1
+ import type { LayoutChangeEvent } from 'react-native';
2
+ export type UseDebugDockOptions = {
3
+ height: number;
4
+ drawerWidth: number;
5
+ drawerPeek: number;
6
+ };
7
+ export declare function useDebugDock({ height, drawerWidth, drawerPeek }: UseDebugDockOptions): {
8
+ containerAnimatedStyle: {
9
+ left: number;
10
+ transform: ({
11
+ translateY: number;
12
+ translateX?: undefined;
13
+ } | {
14
+ translateX: number;
15
+ translateY?: undefined;
16
+ })[];
17
+ };
18
+ panGesture: import("react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture").PanGesture;
19
+ onContainerLayout: (event: LayoutChangeEvent) => void;
20
+ };
@@ -0,0 +1,71 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { Gesture } from 'react-native-gesture-handler';
3
+ import { cancelAnimation, interpolate, ReduceMotion, useAnimatedStyle, useSharedValue, withDecay, withTiming, } from 'react-native-reanimated';
4
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
+ import { useWindowDimensions } from 'react-native';
6
+ export function useDebugDock({ height, drawerWidth, drawerPeek }) {
7
+ const insets = useSafeAreaInsets();
8
+ const { height: windowHeight, width: windowWidth } = useWindowDimensions();
9
+ const containerWidth = useSharedValue(0);
10
+ const dockLevel = useSharedValue(0);
11
+ const isDragging = useSharedValue(false);
12
+ const translateY = useSharedValue(Math.max(insets.top, windowHeight - height - insets.bottom));
13
+ const clampY = useMemo(() => {
14
+ const min = insets.top;
15
+ const max = Math.max(insets.top, windowHeight - height - insets.bottom);
16
+ return { min, max };
17
+ }, [height, insets.bottom, insets.top, windowHeight]);
18
+ const onContainerLayout = useCallback((event) => {
19
+ const width = event.nativeEvent.layout.width;
20
+ if (width <= 0)
21
+ return;
22
+ containerWidth.value = width;
23
+ if (dockLevel.value === 0) {
24
+ dockLevel.value = 1;
25
+ }
26
+ }, [containerWidth, dockLevel]);
27
+ const panGesture = useMemo(() => {
28
+ return Gesture.Pan()
29
+ .onTouchesUp(() => {
30
+ 'worklet';
31
+ if (isDragging.value)
32
+ return;
33
+ if (dockLevel.value === 0)
34
+ return;
35
+ dockLevel.value = withTiming(dockLevel.value === 1 ? 2 : 1, { duration: 300 });
36
+ })
37
+ .onStart(() => {
38
+ isDragging.value = true;
39
+ cancelAnimation(translateY);
40
+ })
41
+ .onChange((event) => {
42
+ translateY.value = Math.min(Math.max(Math.abs(translateY.value) + event.changeY, clampY.min), clampY.max);
43
+ })
44
+ .onFinalize((event) => {
45
+ translateY.value = withDecay({
46
+ velocity: event.velocityY,
47
+ velocityFactor: 0.25,
48
+ reduceMotion: ReduceMotion.System,
49
+ clamp: [clampY.min, clampY.max],
50
+ }, () => {
51
+ isDragging.value = false;
52
+ });
53
+ });
54
+ }, [clampY.max, clampY.min]);
55
+ const containerAnimatedStyle = useAnimatedStyle(() => {
56
+ const openX = -containerWidth.value;
57
+ const peekX = -(drawerPeek > 0 ? drawerPeek : drawerWidth);
58
+ return {
59
+ left: windowWidth,
60
+ transform: [
61
+ { translateY: translateY.value },
62
+ { translateX: interpolate(dockLevel.value, [0, 1, 2], [0, peekX, openX]) },
63
+ ],
64
+ };
65
+ }, [drawerPeek, drawerWidth, windowWidth]);
66
+ return {
67
+ containerAnimatedStyle,
68
+ panGesture,
69
+ onContainerLayout,
70
+ };
71
+ }
@@ -0,0 +1,2 @@
1
+ export * from './debug-dock';
2
+ export * from './toast';
@@ -0,0 +1,2 @@
1
+ export * from './debug-dock';
2
+ export * from './toast';
@@ -0,0 +1,62 @@
1
+ # `@rnzeus/atlas/ui/toast`
2
+
3
+ ← [`@rnzeus/atlas/ui`](../README.md)
4
+
5
+ ## Purpose
6
+
7
+ Toast system for React Native: global toast API (`useToast`), provider-managed queue, and animated viewport/plates.
8
+
9
+ ## Import
10
+
11
+ ```ts
12
+ import {ToastProvider, useToast} from '@rnzeus/atlas/ui';
13
+ ```
14
+
15
+ ## API (public surface)
16
+
17
+ - `ToastProvider`
18
+ - `useToast() -> { show, dismiss, clear }`
19
+
20
+ ## Usage (minimal)
21
+
22
+ ```tsx
23
+ import {ToastProvider, useToast} from '@rnzeus/atlas/ui';
24
+
25
+ function SaveButton() {
26
+ const toast = useToast();
27
+ return (
28
+ <Button
29
+ title="Save"
30
+ onPress={() =>
31
+ toast.show({
32
+ render: () => <Text>Saved</Text>,
33
+ })
34
+ }
35
+ />
36
+ );
37
+ }
38
+
39
+ export function App() {
40
+ return (
41
+ <ToastProvider>
42
+ <SaveButton />
43
+ </ToastProvider>
44
+ );
45
+ }
46
+ ```
47
+
48
+ ## Notes
49
+
50
+ - `ToastProvider` renders `ToastViewport` internally; usually you only mount the provider once near app root.
51
+ - `show()` supports dedupe via `uuid` and limits concurrent toasts by `maxToasts`.
52
+ - `autoHide` defaults to `true`; default `visibilityTime` is `3000`.
53
+
54
+ ## Sources
55
+
56
+ - [`index`](./index.js)
57
+ - [`toast-provider`](./toast-provider.js)
58
+ - [`context`](./context.js)
59
+ - [`use-toast`](./use-toast.js)
60
+ - [`types`](./types.js)
61
+ - [`toast-viewport`](./toast-viewport/toast-viewport.js)
62
+ - [`toast-plate`](./toast-plate/toast-plate.js)
@@ -0,0 +1,7 @@
1
+ import type { ToastId, ToastOptions } from './types';
2
+ export type ToastApi = {
3
+ show: (options: ToastOptions) => ToastId;
4
+ dismiss: (id: ToastId) => void;
5
+ clear: () => void;
6
+ };
7
+ export declare const ToastContext: import("react").Context<ToastApi>;