@oneplatformdev/react-native-uikit 0.0.0-stage → 1.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 (68) hide show
  1. package/README.md +376 -2
  2. package/bin/cli.cjs +372 -0
  3. package/package.json +34 -4
  4. package/registry/manifest.json +113 -0
  5. package/src/components/BottomModal/index.ts +2 -0
  6. package/src/components/BottomModal/presenters/useBottomModal.ts +114 -0
  7. package/src/components/BottomModal/presenters/useBottomModalInsets.ts +19 -0
  8. package/src/components/BottomModal/types/types.ts +23 -0
  9. package/src/components/BottomModal/ui/BottomModal.tsx +110 -0
  10. package/src/components/BottomModal/ui/styles.ts +115 -0
  11. package/src/components/Button/index.ts +8 -0
  12. package/src/components/Button/presenters/useButton.ts +72 -0
  13. package/src/components/Button/types/types.ts +39 -0
  14. package/src/components/Button/ui/Button.tsx +121 -0
  15. package/src/components/Button/ui/styles.ts +82 -0
  16. package/src/components/ConnectionContainer/index.ts +2 -0
  17. package/src/components/ConnectionContainer/presenters/useConnectionContainer.ts +77 -0
  18. package/src/components/ConnectionContainer/types/types.ts +14 -0
  19. package/src/components/ConnectionContainer/ui/ConnectionContainer.tsx +51 -0
  20. package/src/components/ConnectionContainer/ui/styles.ts +35 -0
  21. package/src/components/CustomAlert/index.ts +2 -0
  22. package/src/components/CustomAlert/presenters/useCustomAlert.ts +24 -0
  23. package/src/components/CustomAlert/types/types.ts +20 -0
  24. package/src/components/CustomAlert/ui/CustomAlert.tsx +77 -0
  25. package/src/components/CustomAlert/ui/styles.ts +71 -0
  26. package/src/components/HeaderWithBackButton/index.ts +2 -0
  27. package/src/components/HeaderWithBackButton/presenters/useHeaderWithBackButton.ts +17 -0
  28. package/src/components/HeaderWithBackButton/types/types.ts +21 -0
  29. package/src/components/HeaderWithBackButton/ui/HeaderWithBackButton.tsx +90 -0
  30. package/src/components/HeaderWithBackButton/ui/styles.ts +58 -0
  31. package/src/components/Input/index.ts +8 -0
  32. package/src/components/Input/presenters/useInput.ts +104 -0
  33. package/src/components/Input/types/types.ts +36 -0
  34. package/src/components/Input/ui/Input.tsx +161 -0
  35. package/src/components/Input/ui/styles.ts +80 -0
  36. package/src/components/Loader/index.ts +2 -0
  37. package/src/components/Loader/types/types.ts +12 -0
  38. package/src/components/Loader/ui/Loader.tsx +29 -0
  39. package/src/components/Loader/ui/styles.ts +14 -0
  40. package/src/components/Logger/index.ts +2 -0
  41. package/src/components/Logger/presenters/useLogger.ts +13 -0
  42. package/src/components/Logger/types/types.ts +56 -0
  43. package/src/components/Logger/ui/Logger.tsx +85 -0
  44. package/src/components/Logger/ui/components/LoggerItem.tsx +117 -0
  45. package/src/components/Logger/ui/styles.ts +121 -0
  46. package/src/components/Logger/utils/groupLoggerEntries.ts +40 -0
  47. package/src/components/Logger/utils/loggerPresentation.ts +69 -0
  48. package/src/components/ScreenContainer/index.ts +2 -0
  49. package/src/components/ScreenContainer/presenters/useScreenContainer.ts +12 -0
  50. package/src/components/ScreenContainer/types/types.ts +19 -0
  51. package/src/components/ScreenContainer/ui/ScreenContainer.tsx +83 -0
  52. package/src/components/ScreenContainer/ui/styles.ts +31 -0
  53. package/src/components/Typography/index.ts +2 -0
  54. package/src/components/Typography/types/types.ts +14 -0
  55. package/src/components/Typography/ui/Typography.tsx +37 -0
  56. package/src/components/Typography/ui/styles.ts +26 -0
  57. package/src/index.ts +13 -0
  58. package/src/provider/LocalizationContext.tsx +14 -0
  59. package/src/provider/ModeContext.tsx +9 -0
  60. package/src/provider/index.ts +12 -0
  61. package/src/provider/types.ts +31 -0
  62. package/src/theme/ThemeProvider.tsx +7 -0
  63. package/src/theme/UIProvider.tsx +60 -0
  64. package/src/theme/createTheme.ts +66 -0
  65. package/src/theme/defaultTheme.ts +215 -0
  66. package/src/theme/index.ts +4 -0
  67. package/src/theme/types.ts +265 -0
  68. package/src/utils/index.ts +94 -0
package/README.md CHANGED
@@ -1,3 +1,377 @@
1
- # Temporary Holding Version
1
+ # React Native NLtDev UI Kit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Reusable React Native theme, scaling, and UI components.
4
+
5
+ ## Distribution modes
6
+
7
+ ### Package mode — available now
8
+
9
+ Install with `npm install @oneplatformdev/react-native-uikit` and import ready-made components:
10
+
11
+ ```tsx
12
+ import { Button, Input, Typography, UIProvider } from '@oneplatformdev/react-native-uikit';
13
+
14
+ <UIProvider>
15
+ <Typography text="Hello" />
16
+ <Input label="Name" value={name} onChangeText={setName} />
17
+ <Button text="Continue" onPress={onContinue} />
18
+ </UIProvider>
19
+ ```
20
+
21
+ Choose package mode when theme tokens and public props cover the design and receiving package updates is useful. `UIProvider` is optional when the default theme is sufficient.
22
+
23
+ ### Owned/local source mode — available now
24
+
25
+ Run from the consuming project root after declaring `@oneplatformdev/react-native-uikit`, `react`, and `react-native` in its `package.json`:
26
+
27
+ ```sh
28
+ npx @oneplatformdev/react-native-uikit add typography
29
+ npx @oneplatformdev/react-native-uikit add button
30
+ npx @oneplatformdev/react-native-uikit add input
31
+ npx @oneplatformdev/react-native-uikit add screen-container
32
+ npx @oneplatformdev/react-native-uikit add custom-alert
33
+ npx @oneplatformdev/react-native-uikit add bottom-modal
34
+ npx @oneplatformdev/react-native-uikit add connection-container
35
+ npx @oneplatformdev/react-native-uikit add header-with-back-button
36
+ npx @oneplatformdev/react-native-uikit add loader
37
+ npx @oneplatformdev/react-native-uikit add logger
38
+ npx @oneplatformdev/react-native-uikit add button --dir src/UIKit
39
+ ```
40
+
41
+ The default destination is `src/components/ui/<Component>/`; `--dir` changes its root, so the last command creates `src/UIKit/Typography/` and `src/UIKit/Button/`. No `init` command or configuration file is needed. The CLI refuses existing requested target folders, never overwrites, has no `--force`, and does not install npm dependencies. It reports missing declarations before copying.
42
+
43
+ The application owns and may freely change the copied implementation—structure, API, layout, animation, and behavior—without editing `node_modules` or changing the upstream package. Package upgrades do **not** update copied source. There is no diff/update command yet. Use this mode for substantial project-specific divergence, not ordinary color or spacing changes already supported by the theme.
44
+
45
+ Owned Button, Input, CustomAlert, BottomModal, ConnectionContainer, and HeaderWithBackButton import sibling local Typography. Their `add` commands install Typography automatically when missing; a compatible existing owned Typography is reused untouched. An incomplete or incompatible dependency aborts the command without installing the requested component. Copied components are actual sources, not wrappers around their package-mode counterparts. They still require the package for theme/scaling foundations; no project alias is required.
46
+
47
+ For Button, Typography, Input, future form controls, sheets, and other reusable primitives, documentation should cover: package import, basic usage, main props, theme customization, advanced/custom content, and owned-source availability. Components should serve both modes without becoming unlimited-configuration abstractions; source ownership is the escape hatch for major changes.
48
+
49
+ ## Extending the theme
50
+
51
+ Define project tokens once and derive extension types from those objects instead of repeating every custom key:
52
+
53
+ ```ts
54
+ // theme/tokens.ts
55
+ export const colors = {
56
+ primary: '#123456',
57
+ premium: '#D6A84B',
58
+ } as const;
59
+
60
+ export const spacing = {
61
+ xs: 7,
62
+ screenHorizontal: 20,
63
+ } as const;
64
+
65
+ export const radius = {
66
+ card: 14,
67
+ } as const;
68
+
69
+ export const fonts = {
70
+ regular: {
71
+ fontFamily: 'Inter-Regular',
72
+ fontWeight: 'normal',
73
+ },
74
+ bold: {
75
+ fontFamily: 'Inter-Bold',
76
+ fontWeight: 'normal',
77
+ },
78
+ extraBold: {
79
+ fontFamily: 'Inter-ExtraBold',
80
+ fontWeight: 'normal',
81
+ },
82
+ } as const;
83
+
84
+ export const typography = {
85
+ cardTitle: {
86
+ font: 'extraBold',
87
+ fontSize: 18,
88
+ lineHeight: 24,
89
+ },
90
+ } as const;
91
+ ```
92
+
93
+ ```ts
94
+ // theme/index.ts
95
+ import {
96
+ createTheme,
97
+ type DefaultUIColors,
98
+ type DefaultUIFonts,
99
+ type DefaultUIRadius,
100
+ type DefaultUISpacing,
101
+ type DefaultUITypography,
102
+ } from '@oneplatformdev/react-native-uikit';
103
+ import { colors, fonts, radius, spacing, typography } from './tokens';
104
+
105
+ type ProjectColorExtensions = Omit<typeof colors, keyof DefaultUIColors>;
106
+ type ProjectFontExtensions = Omit<typeof fonts, keyof DefaultUIFonts>;
107
+ type ProjectRadiusExtensions = Omit<typeof radius, keyof DefaultUIRadius>;
108
+ type ProjectSpacingExtensions = Omit<typeof spacing, keyof DefaultUISpacing>;
109
+ type ProjectTypographyExtensions = Omit<typeof typography, keyof DefaultUITypography>;
110
+
111
+ declare module '@oneplatformdev/react-native-uikit' {
112
+ interface UIColorsExtension extends ProjectColorExtensions {}
113
+ interface UIFontsExtension extends ProjectFontExtensions {}
114
+ interface UIRadiusExtension extends ProjectRadiusExtensions {}
115
+ interface UISpacingExtension extends ProjectSpacingExtensions {}
116
+ interface UITypographyExtension extends ProjectTypographyExtensions {}
117
+ }
118
+
119
+ export const appTheme = createTheme({ colors, fonts, radius, spacing, typography });
120
+ ```
121
+
122
+ Pass `appTheme` to `UIProvider` before using project-specific keys. Module augmentation adds compile-time knowledge globally, but custom tokens are not present in the package's runtime `defaultTheme`.
123
+
124
+ Font tokens are merged with package defaults. Projects using face-specific font files should explicitly provide the complete font token, including `fontWeight`. Using `fontWeight: 'normal'` is the safe default when names such as `Inter-Regular` and `Inter-Bold` already identify a specific face.
125
+
126
+ ## External mode and localization
127
+
128
+ Theme-only usage remains valid:
129
+
130
+ ```tsx
131
+ <UIProvider theme={appTheme}>
132
+ <App />
133
+ </UIProvider>
134
+ ```
135
+
136
+ The application owns state, persistence, localization, and navigation integration. `UIProvider` only exposes the resolved theme and optional application callbacks.
137
+
138
+ MobX applications can adapt observable state without adding MobX to the UI kit:
139
+
140
+ ```tsx
141
+ const AppUIProvider = observer(({ children }: PropsWithChildren) => {
142
+ const theme = settings.mode === 'dark' ? darkTheme : lightTheme;
143
+
144
+ return (
145
+ <UIProvider
146
+ theme={theme}
147
+ onModeChange={settings.setMode}
148
+ localization={{
149
+ locale: localization.locale,
150
+ t: localization.t,
151
+ changeLocale: localization.setLocale,
152
+ }}
153
+ >
154
+ {children}
155
+ </UIProvider>
156
+ );
157
+ });
158
+ ```
159
+
160
+ Plain React state works the same way:
161
+
162
+ ```tsx
163
+ const App = () => {
164
+ const [mode, setMode] = useState<ThemeMode>('light');
165
+ const theme = mode === 'dark' ? darkTheme : lightTheme;
166
+
167
+ return (
168
+ <UIProvider theme={theme} onModeChange={setMode}>
169
+ <Content />
170
+ </UIProvider>
171
+ );
172
+ };
173
+ ```
174
+
175
+ A localization engine is adapted through a small optional contract:
176
+
177
+ ```tsx
178
+ <UIProvider
179
+ theme={appTheme}
180
+ localization={{
181
+ locale: i18n.language,
182
+ t: (key, params) => String(i18n.t(key, params)),
183
+ changeLocale: locale => i18n.changeLanguage(locale),
184
+ direction: i18n.dir() === 'rtl' ? 'rtl' : 'ltr',
185
+ }}
186
+ >
187
+ <App />
188
+ </UIProvider>
189
+ ```
190
+
191
+ Applications without localization omit the prop. Calling `useUILocalization()` without an adapter throws a developer-facing configuration error. `useUIMode()` returns the resolved `theme.mode` and the optional `changeMode` callback.
192
+
193
+ ## Typography
194
+
195
+ Package import: `import { Typography } from '@oneplatformdev/react-native-uikit'`.
196
+
197
+ ```tsx
198
+ <Typography text="Hello" variant="body" />
199
+ <Typography variant="body">Hello</Typography>
200
+ ```
201
+
202
+ Prefer `text` (`string | number`) for simple content. Use `children` for composed or custom content; TypeScript disallows supplying both, and neither is also valid. Typography retains React Native `TextProps`, styles, and ref forwarding. Its main visual props are `variant`, `weight`, `color`, `size`, and `lineHeight`; caller `style` is applied last. Customize its typography/font/color tokens through `createTheme` and the extension interfaces described above. Owned/local source installation: **available** with `npx @oneplatformdev/react-native-uikit add typography`.
203
+
204
+ ## Button
205
+
206
+ Package import: `import { Button } from '@oneplatformdev/react-native-uikit'`. Prefer `text` for ordinary labels:
207
+
208
+ ```tsx
209
+ <Button text="Continue" onPress={onContinue} />
210
+ <Button text="Delete" variant="outline" labelProps={{ color: 'error' }} onPress={onDelete} />
211
+ ```
212
+
213
+ `text` accepts strings and numbers. Primitive `children` remain supported; TypeScript disallows supplying both. Neither is valid for icon-only Buttons, which should provide an explicit `accessibilityLabel`. The `primary` (default), `secondary`, `outline`, and `ghost` variants and `sm`, `md` (default), and `lg` sizes use the theme. `disabled` blocks interaction; `leftIcon` and `rightIcon` are inline with the label.
214
+
215
+ `labelProps` overrides automatic-label typography (`color`, `variant`, `weight`, `size`, `lineHeight`, and `style`) and safe React Native Text behavior such as truncation. It cannot attach label-level press handlers or conflicting accessibility role/state. For example:
216
+
217
+ ```tsx
218
+ <Button
219
+ text="A long label that may be truncated"
220
+ labelProps={{
221
+ numberOfLines: 1,
222
+ ellipsizeMode: 'tail',
223
+ }}
224
+ />
225
+ ```
226
+
227
+ Labels wrap by default and retain React Native accessibility font scaling. Theme defaults apply first; explicit `labelProps` win, with `labelProps.style` last for label-only styles. Caller root `style` remains last for the Button container.
228
+
229
+ `loading` blocks interaction and shows a loader while preserving the active variant appearance by default. Use `loadingAppearance="disabled"` to opt into disabled styling during loading; an explicit `disabled` prop always uses disabled styling.
230
+
231
+ `loadingIndicatorColor` overrides only the loader color; otherwise the loader follows the active or disabled foreground color.
232
+
233
+ `text` and primitive string/number children provide a default `accessibilityLabel` that remains available while loading. An explicit `accessibilityLabel` wins. Button does not generate an `accessibilityHint`.
234
+
235
+ Button works with the package theme without configuration. Applications can partially override variant colors and size/label defaults through `createTheme`:
236
+
237
+ ```ts
238
+ const theme = createTheme({
239
+ button: {
240
+ variants: {
241
+ primary: { background: 'primary', border: 'primary', foreground: 'onPrimary' },
242
+ },
243
+ sizes: {
244
+ md: { minHeight: 48, labelVariant: 'title', labelWeight: 'bold' },
245
+ },
246
+ numberOfLines: 1,
247
+ },
248
+ });
249
+ ```
250
+
251
+ Variant colors reference typed theme color keys. Size values are unscaled design units, scaled once by Button. Unspecified variants and size fields keep their package defaults; `labelProps.numberOfLines` overrides the theme default, and omitting both keeps labels wrappable.
252
+
253
+ Custom ReactNode children are rendered unchanged and bypass `labelProps`. Give custom content an explicit accessible name when needed:
254
+
255
+ ```tsx
256
+ <Button accessibilityLabel="Upload status" onPress={onUpload}>
257
+ <View>
258
+ <Typography text="Upload" />
259
+ <Typography text="3 files" />
260
+ </View>
261
+ </Button>
262
+ ```
263
+
264
+ Owned/local source installation for Button: **available** with `npx @oneplatformdev/react-native-uikit add button`. Use package props/theme for ordinary visual changes and owned source when structure or behavior must diverge substantially.
265
+
266
+ ## Input
267
+
268
+ Package import: `import { Input } from '@oneplatformdev/react-native-uikit'`.
269
+
270
+ ```tsx
271
+ <Input label="Email" value={email} onChangeText={onChangeEmail} placeholder="name@example.com" />
272
+ <Input label="Email" value={email} errorText="Invalid email" onChangeText={onChangeEmail} />
273
+ ```
274
+
275
+ Input forwards native React Native `TextInputProps` and the native TextInput ref. Native `style` styles the TextInput; `containerStyle` styles the outer field wrapper; `inputContainerStyle` styles the bordered row. Explicit styles apply after theme and visual-state styles. `labelProps`, `helperTextProps`, and `errorTextProps` customize their corresponding Typography without changing the native input.
276
+
277
+ `helperText` appears below the field until `errorText` is nonempty or `invalid` is true. A nonempty `errorText` implies invalid and is announced as an alert; `invalid` alone changes the border without a message. Visual priority is disabled, invalid, focused, then normal. `disabled` forces native `editable={false}` and disabled styling; native `editable={false}` or `readOnly={true}` without `disabled` keeps normal styling. Multiline remains native, and callers can override its height with `inputContainerStyle`.
278
+
279
+ Use logical `startAccessory` and `endAccessory` for caller-owned icons or other content. Password visibility is opt-in: `secureTextEntry` stays native, and `passwordToggle` supplies ready localized action labels and icons without requiring an icon or localization dependency in the package:
280
+
281
+ ```tsx
282
+ <Input
283
+ label="Password"
284
+ secureTextEntry
285
+ value={password}
286
+ onChangeText={onChangePassword}
287
+ passwordToggle={{
288
+ show: { label: showPasswordText, icon: <EyeIcon /> },
289
+ hide: { label: hidePasswordText, icon: <EyeOffIcon /> },
290
+ }}
291
+ />
292
+ ```
293
+
294
+ Input derives its native accessibility label from `label` only when the caller omits `accessibilityLabel`; placeholders are not accessible-name substitutes. Explicit accessibility props win. Supply a meaningful `accessibilityLabel` for unlabeled fields. Input does not translate strings or validate values. Configure its colors, geometry, and typography through partial `createTheme({ input: { ... } })` overrides; color/font/variant references remain typed theme tokens and numeric dimensions remain unscaled design values.
295
+
296
+ Owned/local source installation for Input: **available** with `npx @oneplatformdev/react-native-uikit add input`.
297
+
298
+ ## ScreenContainer, CustomAlert, and BottomModal
299
+
300
+ `ScreenContainer` provides a theme-backed safe-area layout, optional scrolling, and optional keyboard-aware scrolling or avoidance. Pass `gradient={{ colors, locations, start, end }}` to render a caller-configured background across the entire screen, behind the safe-area content; omit it for the solid theme background. Gradient coordinates are normalized and are not scaled. Pass ordinary ScrollView props when scrolling is enabled; use `containerStyle` and `contentContainerStyle` for layout overrides.
301
+
302
+ `CustomAlert` animates a centered modal with a dismissible backdrop. Supply `header`, children, and optional `footer` content; compose your own actions with `Button`. Optional `closeAction` accepts an icon and an application-supplied accessibility label. No strings are translated by the kit.
303
+
304
+ `BottomModal` provides the reference-style slide/drag dismissal and keyboard-height-aware positioning. Supply `children`, `onClose`, and optionally `title`, `customHeader`, and a labeled `closeAction` icon. `Keyboard.dismiss()` and the close animation start together; `closeRef` can trigger the same close path externally. The kit does not render app-specific toasts.
305
+
306
+ Both modes require consumers to install the declared native peers used by these components: `react-native-keyboard-controller`, `react-native-linear-gradient`, `react-native-reanimated`, and `react-native-safe-area-context`. Applications using keyboard-aware or safe-area behavior should configure the providers and native setup required by those libraries. The kit does not bundle those peers or install them through `add`.
307
+
308
+ ## ConnectionContainer
309
+
310
+ `ConnectionContainer` displays a dismissible safe-area banner above optional children. The application supplies `isConnected` (`true`, `false`, or `null` while unknown), localized `disconnectedText`/`reconnectedText`, and optional icons. The kit does not subscribe to a network library. Like the reference behavior, sustained disconnection appears after three seconds; a reconnection banner appears only after an offline banner was shown and hides after three seconds. Supply `dismissAccessibilityHint` if the dismiss action needs additional spoken context.
311
+
312
+ ## HeaderWithBackButton and Loader
313
+
314
+ `HeaderWithBackButton` uses caller-owned navigation, icon, and accessibility text. It defaults to a left-aligned title; center mode keeps the title centered across the whole header even when side controls have different widths. The parent (for example `ScreenContainer`) owns safe-area insets.
315
+
316
+ ```tsx
317
+ import { HeaderWithBackButton, Loader } from '@oneplatformdev/react-native-uikit';
318
+
319
+ <HeaderWithBackButton
320
+ title="Details"
321
+ backIcon={<BackIcon />}
322
+ backAccessibilityLabel="Back"
323
+ onBackPress={goBack}
324
+ />
325
+ <HeaderWithBackButton
326
+ title="Account"
327
+ titleAlign="center"
328
+ titleVariant="title"
329
+ titleSize={19}
330
+ backIcon={<BackIcon />}
331
+ backAccessibilityLabel="Back"
332
+ onBackPress={goBack}
333
+ rightAccessory={<SettingsAction />}
334
+ />
335
+
336
+ <Loader accessibilityLabel="Loading" />
337
+ <Loader indicator={<MyAnimatedLoader />} transparent accessibilityLabel="Loading" />
338
+ ```
339
+
340
+ `Loader` is an absolute, container-filling overlay by default; `inline` keeps it in layout. The caller controls whether it is rendered. A custom `indicator` completely replaces the default `ActivityIndicator` without requiring extra dependencies.
341
+
342
+ ## Logger
343
+
344
+ Logger displays application-owned entries; it does not collect, store, or sanitize logs. Pass newest-first entries and control visibility outside the component:
345
+
346
+ ```tsx
347
+ import { Logger, type LoggerEntry } from '@oneplatformdev/react-native-uikit';
348
+
349
+ const logs: LoggerEntry[] = [
350
+ { id: 'response-1', type: 'response', name: 'POST /messages', message: '{"ok":true}',
351
+ http: { correlationId: 'call-1', phase: 'response', status: 201, durationMs: 842 } },
352
+ { id: 'request-1', type: 'request', name: 'POST /messages', message: '',
353
+ requestData: { params: 'page: 1', body: '{"text":"hello"}' },
354
+ http: { correlationId: 'call-1', phase: 'request' } },
355
+ { id: 'event-1', type: 'info', name: 'Sync', message: 'Finished' },
356
+ ];
357
+
358
+ <Logger
359
+ logs={logs}
360
+ visible={visible}
361
+ onOpen={() => setVisible(true)}
362
+ onClose={() => setVisible(false)}
363
+ onClear={() => setLogs([])}
364
+ onCopyEntry={(_entry, text) => copyLogText(text)}
365
+ labels={{ title: 'Logs', clear: 'Clear', close: 'Close', empty: 'No logs',
366
+ request: 'Request', response: 'Response', params: 'Params', body: 'Body',
367
+ payload: 'Payload', status: 'Status', copy: 'Copy' }}
368
+ />
369
+ ```
370
+
371
+ Entries with the same unique `http.correlationId` form one block, with request above response even when storage is newest-first. Each entry keeps its own collapsed-by-default expansion state; pending requests and orphan responses remain visible. Long payload text is selectable, with no clipboard dependency or combined exchange-copy action. Omit `onClear` to hide the clear action; omit `onOpen` if the application supplies its own launcher. The application owns HTTP correlation IDs and any development-only mount policy. Owned source is available with `npx @oneplatformdev/react-native-uikit add logger` (which also installs local Typography).
372
+
373
+ HTTP sections use `request` and `response` labels for accessibility, not visible headings. Response status and duration come from structured `http` metadata. Blank messages have no expand action; applications should pass an empty message for absent payloads.
374
+
375
+ Applications can supply sanitized, display-ready `requestData.params` and `requestData.body` separately from the response `message`. A request section appears only when it has params, body, or a legacy request message. Params are visible without expanding; bodies and response payloads expand independently. `onCopyEntry` receives the individual entry and its copyable text, even while collapsed; `copyIcon` is optional. The application performs the clipboard write and handles any feedback or failure.
376
+
377
+ Params-only requests show params with a top-aligned copy action and no expand control. A collapsed request body is identified by the caller-provided `payload` label; `body` still labels the combined params/body copy text. The caller-provided `status` label precedes structured response metadata, while only the numeric status receives a semantic theme color: 2xx `success`, 3xx `icon`, 4xx `warning`, 5xx `error`, and other values `text`.