@onlynative/components 0.0.0-alpha.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.
@@ -0,0 +1,510 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/text-field/index.ts
21
+ var text_field_exports = {};
22
+ __export(text_field_exports, {
23
+ TextField: () => TextField
24
+ });
25
+ module.exports = __toCommonJS(text_field_exports);
26
+
27
+ // src/text-field/TextField.tsx
28
+ var import_react = require("react");
29
+ var import_react_native4 = require("react-native");
30
+ var import_core = require("@onlynative/core");
31
+
32
+ // ../utils/dist/chunk-OQRDRRQA.mjs
33
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
34
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
35
+ }) : x)(function(x) {
36
+ if (typeof require !== "undefined") return require.apply(this, arguments);
37
+ throw Error('Dynamic require of "' + x + '" is not supported');
38
+ });
39
+
40
+ // ../utils/dist/index.mjs
41
+ var import_react_native = require("react-native");
42
+ var import_react_native2 = require("react-native");
43
+ function parseHexColor(color) {
44
+ const normalized = color.replace("#", "");
45
+ if (normalized.length !== 6 && normalized.length !== 8) {
46
+ return null;
47
+ }
48
+ const r = Number.parseInt(normalized.slice(0, 2), 16);
49
+ const g = Number.parseInt(normalized.slice(2, 4), 16);
50
+ const b = Number.parseInt(normalized.slice(4, 6), 16);
51
+ if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
52
+ return null;
53
+ }
54
+ return { r, g, b };
55
+ }
56
+ function clampAlpha(alpha) {
57
+ return Math.max(0, Math.min(1, alpha));
58
+ }
59
+ function alphaColor(color, alpha) {
60
+ const channels = parseHexColor(color);
61
+ const boundedAlpha = clampAlpha(alpha);
62
+ if (!channels) {
63
+ return color;
64
+ }
65
+ return `rgba(${channels.r}, ${channels.g}, ${channels.b}, ${boundedAlpha})`;
66
+ }
67
+ var _MCIcons = null;
68
+ var _resolved = false;
69
+ function getMaterialCommunityIcons() {
70
+ if (!_resolved) {
71
+ _resolved = true;
72
+ try {
73
+ const mod = __require("@expo/vector-icons/MaterialCommunityIcons");
74
+ _MCIcons = mod.default || mod;
75
+ } catch {
76
+ _MCIcons = null;
77
+ }
78
+ }
79
+ if (!_MCIcons) {
80
+ throw new Error(
81
+ "@expo/vector-icons is required for icon support. Install it with: npx expo install @expo/vector-icons"
82
+ );
83
+ }
84
+ return _MCIcons;
85
+ }
86
+ function transformOrigin(vertical = "top") {
87
+ return import_react_native2.I18nManager.isRTL ? `right ${vertical}` : `left ${vertical}`;
88
+ }
89
+
90
+ // src/text-field/styles.ts
91
+ var import_react_native3 = require("react-native");
92
+ var CONTAINER_HEIGHT = 56;
93
+ var ICON_SIZE = 24;
94
+ var LABEL_FLOATED_LINE_HEIGHT = 16;
95
+ var FILLED_LABEL_RESTING_TOP = 16;
96
+ var FILLED_LABEL_FLOATED_TOP = 8;
97
+ var FILLED_INPUT_TOP = 24;
98
+ var FILLED_INPUT_BOTTOM = 8;
99
+ var OUTLINED_INPUT_VERTICAL = 16;
100
+ var OUTLINED_LABEL_RESTING_TOP = 16;
101
+ var OUTLINED_LABEL_FLOATED_TOP = -(LABEL_FLOATED_LINE_HEIGHT / 2);
102
+ var labelPositions = {
103
+ filledRestingTop: FILLED_LABEL_RESTING_TOP,
104
+ filledFloatedTop: FILLED_LABEL_FLOATED_TOP,
105
+ outlinedRestingTop: OUTLINED_LABEL_RESTING_TOP,
106
+ outlinedFloatedTop: OUTLINED_LABEL_FLOATED_TOP
107
+ };
108
+ function getVariantColors(theme, variant) {
109
+ const disabledOpacity = theme.stateLayer.disabledOpacity;
110
+ const common = {
111
+ focusedBorderColor: theme.colors.primary,
112
+ errorBorderColor: theme.colors.error,
113
+ focusedLabelColor: theme.colors.primary,
114
+ errorLabelColor: theme.colors.error,
115
+ textColor: theme.colors.onSurface,
116
+ disabledTextColor: alphaColor(theme.colors.onSurface, disabledOpacity),
117
+ disabledLabelColor: alphaColor(theme.colors.onSurface, disabledOpacity),
118
+ disabledBorderColor: alphaColor(theme.colors.onSurface, 0.12),
119
+ placeholderColor: theme.colors.onSurfaceVariant,
120
+ supportingTextColor: theme.colors.onSurfaceVariant,
121
+ errorSupportingTextColor: theme.colors.error,
122
+ iconColor: theme.colors.onSurfaceVariant,
123
+ errorIconColor: theme.colors.error,
124
+ disabledIconColor: alphaColor(theme.colors.onSurface, disabledOpacity)
125
+ };
126
+ if (variant === "outlined") {
127
+ return {
128
+ ...common,
129
+ backgroundColor: "transparent",
130
+ borderColor: theme.colors.outline,
131
+ disabledBackgroundColor: "transparent",
132
+ labelColor: theme.colors.onSurfaceVariant
133
+ };
134
+ }
135
+ return {
136
+ ...common,
137
+ backgroundColor: theme.colors.surfaceContainerHighest,
138
+ borderColor: theme.colors.onSurfaceVariant,
139
+ disabledBackgroundColor: alphaColor(theme.colors.onSurface, 0.04),
140
+ labelColor: theme.colors.onSurfaceVariant
141
+ };
142
+ }
143
+ function createStyles(theme, variant) {
144
+ const colors = getVariantColors(theme, variant);
145
+ const bodyLarge = theme.typography.bodyLarge;
146
+ const bodySmall = theme.typography.bodySmall;
147
+ const isFilled = variant === "filled";
148
+ return {
149
+ colors,
150
+ styles: import_react_native3.StyleSheet.create({
151
+ root: {
152
+ alignSelf: "stretch"
153
+ },
154
+ container: {
155
+ minHeight: CONTAINER_HEIGHT,
156
+ flexDirection: "row",
157
+ alignItems: "stretch",
158
+ backgroundColor: colors.backgroundColor,
159
+ paddingHorizontal: theme.spacing.md,
160
+ ...isFilled ? {
161
+ borderTopStartRadius: theme.shape.cornerExtraSmall,
162
+ borderTopEndRadius: theme.shape.cornerExtraSmall
163
+ } : {
164
+ borderRadius: theme.shape.cornerExtraSmall,
165
+ borderWidth: 1,
166
+ borderColor: colors.borderColor
167
+ }
168
+ },
169
+ containerFocused: isFilled ? {} : {
170
+ borderWidth: 2,
171
+ borderColor: colors.focusedBorderColor,
172
+ paddingHorizontal: theme.spacing.md - 1
173
+ },
174
+ containerError: isFilled ? {} : {
175
+ borderWidth: 2,
176
+ borderColor: colors.errorBorderColor,
177
+ paddingHorizontal: theme.spacing.md - 1
178
+ },
179
+ containerDisabled: isFilled ? { backgroundColor: colors.disabledBackgroundColor } : {
180
+ borderColor: colors.disabledBorderColor
181
+ },
182
+ indicator: {
183
+ position: "absolute",
184
+ start: 0,
185
+ end: 0,
186
+ bottom: 0,
187
+ height: 1,
188
+ backgroundColor: colors.borderColor
189
+ },
190
+ indicatorFocused: {
191
+ height: 2,
192
+ backgroundColor: colors.focusedBorderColor
193
+ },
194
+ indicatorError: {
195
+ height: 2,
196
+ backgroundColor: colors.errorBorderColor
197
+ },
198
+ indicatorDisabled: {
199
+ backgroundColor: colors.disabledBorderColor
200
+ },
201
+ inputWrapper: {
202
+ flex: 1,
203
+ justifyContent: "center"
204
+ },
205
+ // When label is present, use explicit padding so the input position
206
+ // matches the label resting top exactly.
207
+ inputWrapperWithLabel: {
208
+ justifyContent: "flex-start",
209
+ paddingTop: isFilled ? FILLED_INPUT_TOP : OUTLINED_INPUT_VERTICAL,
210
+ paddingBottom: isFilled ? FILLED_INPUT_BOTTOM : OUTLINED_INPUT_VERTICAL
211
+ },
212
+ label: {
213
+ position: "absolute",
214
+ zIndex: 1,
215
+ fontFamily: bodySmall.fontFamily,
216
+ fontSize: bodySmall.fontSize,
217
+ lineHeight: bodySmall.lineHeight,
218
+ fontWeight: bodySmall.fontWeight,
219
+ letterSpacing: bodySmall.letterSpacing,
220
+ color: colors.labelColor,
221
+ transformOrigin: transformOrigin("top")
222
+ },
223
+ labelNotch: {
224
+ paddingHorizontal: 4
225
+ },
226
+ input: {
227
+ fontFamily: bodyLarge.fontFamily,
228
+ fontSize: bodyLarge.fontSize,
229
+ lineHeight: bodyLarge.lineHeight,
230
+ fontWeight: bodyLarge.fontWeight,
231
+ letterSpacing: bodyLarge.letterSpacing,
232
+ color: colors.textColor,
233
+ paddingVertical: 0,
234
+ paddingHorizontal: 0,
235
+ margin: 0,
236
+ includeFontPadding: false
237
+ },
238
+ inputDisabled: {
239
+ color: colors.disabledTextColor
240
+ },
241
+ leadingIcon: {
242
+ alignSelf: "center",
243
+ marginStart: -4,
244
+ // 16dp container padding → 12dp icon inset per M3
245
+ marginEnd: theme.spacing.md,
246
+ width: ICON_SIZE,
247
+ height: ICON_SIZE,
248
+ alignItems: "center",
249
+ justifyContent: "center"
250
+ },
251
+ trailingIcon: {
252
+ alignSelf: "center",
253
+ marginStart: theme.spacing.md,
254
+ marginEnd: -4,
255
+ // 16dp container padding → 12dp icon inset per M3
256
+ width: ICON_SIZE,
257
+ height: ICON_SIZE,
258
+ alignItems: "center",
259
+ justifyContent: "center"
260
+ },
261
+ trailingIconPressable: {
262
+ alignSelf: "center"
263
+ },
264
+ supportingTextRow: {
265
+ paddingHorizontal: theme.spacing.md,
266
+ paddingTop: theme.spacing.xs
267
+ },
268
+ supportingText: {
269
+ fontFamily: bodySmall.fontFamily,
270
+ fontSize: bodySmall.fontSize,
271
+ lineHeight: bodySmall.lineHeight,
272
+ fontWeight: bodySmall.fontWeight,
273
+ letterSpacing: bodySmall.letterSpacing,
274
+ color: colors.supportingTextColor
275
+ },
276
+ errorSupportingText: {
277
+ color: colors.errorSupportingTextColor
278
+ }
279
+ })
280
+ };
281
+ }
282
+
283
+ // src/text-field/TextField.tsx
284
+ var import_jsx_runtime = require("react/jsx-runtime");
285
+ var ICON_SIZE2 = 24;
286
+ var ICON_WITH_GAP = 12 + 24 + 16;
287
+ function TextField({
288
+ value,
289
+ onChangeText,
290
+ label,
291
+ placeholder,
292
+ variant = "filled",
293
+ supportingText,
294
+ errorText,
295
+ error = false,
296
+ disabled = false,
297
+ leadingIcon,
298
+ trailingIcon,
299
+ onTrailingIconPress,
300
+ multiline = false,
301
+ onFocus,
302
+ onBlur,
303
+ style,
304
+ containerColor,
305
+ contentColor,
306
+ inputStyle,
307
+ ...textInputProps
308
+ }) {
309
+ const theme = (0, import_core.useTheme)();
310
+ const isDisabled = Boolean(disabled);
311
+ const isError = Boolean(error) || Boolean(errorText);
312
+ const isFilled = variant === "filled";
313
+ const hasLeadingIcon = Boolean(leadingIcon);
314
+ const MaterialCommunityIcons = leadingIcon || trailingIcon ? getMaterialCommunityIcons() : null;
315
+ const { colors, styles } = (0, import_react.useMemo)(
316
+ () => createStyles(theme, variant),
317
+ [theme, variant]
318
+ );
319
+ const [isFocused, setIsFocused] = (0, import_react.useState)(false);
320
+ const [internalHasText, setInternalHasText] = (0, import_react.useState)(
321
+ () => value !== void 0 && value !== ""
322
+ );
323
+ const inputRef = (0, import_react.useRef)(null);
324
+ const isControlled = value !== void 0;
325
+ const hasValue = isControlled ? value !== "" : internalHasText;
326
+ const isLabelFloated = isFocused || hasValue;
327
+ const labelAnimRef = (0, import_react.useRef)(new import_react_native4.Animated.Value(isLabelFloated ? 1 : 0));
328
+ const labelAnim = labelAnimRef.current;
329
+ (0, import_react.useEffect)(() => {
330
+ import_react_native4.Animated.timing(labelAnim, {
331
+ toValue: isLabelFloated ? 1 : 0,
332
+ duration: 150,
333
+ useNativeDriver: import_react_native4.Platform.OS !== "web"
334
+ }).start();
335
+ }, [isLabelFloated, labelAnim]);
336
+ const labelScale = (0, import_react.useMemo)(() => {
337
+ const restingScale = theme.typography.bodyLarge.fontSize / theme.typography.bodySmall.fontSize;
338
+ return labelAnim.interpolate({
339
+ inputRange: [0, 1],
340
+ outputRange: [restingScale, 1]
341
+ });
342
+ }, [
343
+ labelAnim,
344
+ theme.typography.bodyLarge.fontSize,
345
+ theme.typography.bodySmall.fontSize
346
+ ]);
347
+ const labelTranslateY = (0, import_react.useMemo)(() => {
348
+ const restingTop = isFilled ? labelPositions.filledRestingTop : labelPositions.outlinedRestingTop;
349
+ const floatedTop = isFilled ? labelPositions.filledFloatedTop : labelPositions.outlinedFloatedTop;
350
+ const restingOffset = restingTop - floatedTop;
351
+ return labelAnim.interpolate({
352
+ inputRange: [0, 1],
353
+ outputRange: [restingOffset, 0]
354
+ });
355
+ }, [isFilled, labelAnim]);
356
+ const labelStart = theme.spacing.md + (hasLeadingIcon ? ICON_WITH_GAP - theme.spacing.md : 0);
357
+ const labelStaticTop = isFilled ? labelPositions.filledFloatedTop : labelPositions.outlinedFloatedTop;
358
+ const handleChangeText = (0, import_react.useCallback)(
359
+ (text) => {
360
+ if (!isControlled) {
361
+ setInternalHasText(text !== "");
362
+ }
363
+ onChangeText == null ? void 0 : onChangeText(text);
364
+ },
365
+ [isControlled, onChangeText]
366
+ );
367
+ const handleFocus = (0, import_react.useCallback)(
368
+ (event) => {
369
+ if (isDisabled) return;
370
+ setIsFocused(true);
371
+ onFocus == null ? void 0 : onFocus(event);
372
+ },
373
+ [isDisabled, onFocus]
374
+ );
375
+ const handleBlur = (0, import_react.useCallback)(
376
+ (event) => {
377
+ setIsFocused(false);
378
+ onBlur == null ? void 0 : onBlur(event);
379
+ },
380
+ [onBlur]
381
+ );
382
+ const handleContainerPress = (0, import_react.useCallback)(() => {
383
+ var _a;
384
+ if (!isDisabled) {
385
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
386
+ }
387
+ }, [isDisabled]);
388
+ const labelColor = isDisabled ? colors.disabledLabelColor : isError ? colors.errorLabelColor : isFocused ? colors.focusedLabelColor : colors.labelColor;
389
+ const labelBackgroundColor = variant === "outlined" && isLabelFloated ? theme.colors.surface : "transparent";
390
+ const iconColor = isDisabled ? colors.disabledIconColor : isError ? colors.errorIconColor : contentColor != null ? contentColor : colors.iconColor;
391
+ const containerStyle = (0, import_react.useMemo)(
392
+ () => [
393
+ styles.container,
394
+ containerColor && !isDisabled ? { backgroundColor: containerColor } : void 0,
395
+ isFocused && styles.containerFocused,
396
+ isError && !isFocused && styles.containerError,
397
+ isDisabled && styles.containerDisabled
398
+ ],
399
+ [styles, isFocused, isError, isDisabled, containerColor]
400
+ );
401
+ const indicatorStyle = (0, import_react.useMemo)(
402
+ () => [
403
+ styles.indicator,
404
+ isFocused && styles.indicatorFocused,
405
+ isError && !isFocused && styles.indicatorError,
406
+ isDisabled && styles.indicatorDisabled
407
+ ],
408
+ [styles, isFocused, isError, isDisabled]
409
+ );
410
+ const displaySupportingText = isError ? errorText : supportingText;
411
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native4.View, { style: [styles.root, style], children: [
412
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.Pressable, { onPress: handleContainerPress, disabled: isDisabled, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native4.View, { style: containerStyle, children: [
413
+ leadingIcon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.View, { style: styles.leadingIcon, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
414
+ MaterialCommunityIcons,
415
+ {
416
+ name: leadingIcon,
417
+ size: ICON_SIZE2,
418
+ color: iconColor
419
+ }
420
+ ) }) : null,
421
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
422
+ import_react_native4.View,
423
+ {
424
+ style: [
425
+ styles.inputWrapper,
426
+ label ? styles.inputWrapperWithLabel : void 0
427
+ ],
428
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
429
+ import_react_native4.TextInput,
430
+ {
431
+ ref: inputRef,
432
+ ...textInputProps,
433
+ value,
434
+ onChangeText: handleChangeText,
435
+ editable: !isDisabled,
436
+ onFocus: handleFocus,
437
+ onBlur: handleBlur,
438
+ placeholder: isLabelFloated || !label ? placeholder : void 0,
439
+ placeholderTextColor: colors.placeholderColor,
440
+ multiline,
441
+ style: [
442
+ styles.input,
443
+ isDisabled ? styles.inputDisabled : void 0,
444
+ contentColor && !isDisabled ? { color: contentColor } : void 0,
445
+ inputStyle
446
+ ],
447
+ accessibilityLabel: label || void 0,
448
+ accessibilityState: { disabled: isDisabled },
449
+ accessibilityHint: isError && errorText ? errorText : void 0
450
+ }
451
+ )
452
+ }
453
+ ),
454
+ trailingIcon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
455
+ import_react_native4.Pressable,
456
+ {
457
+ onPress: onTrailingIconPress,
458
+ disabled: isDisabled || !onTrailingIconPress,
459
+ accessibilityRole: "button",
460
+ hitSlop: 12,
461
+ style: styles.trailingIconPressable,
462
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.View, { style: styles.trailingIcon, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
463
+ MaterialCommunityIcons,
464
+ {
465
+ name: trailingIcon,
466
+ size: ICON_SIZE2,
467
+ color: iconColor
468
+ }
469
+ ) })
470
+ }
471
+ ) : null,
472
+ label ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
473
+ import_react_native4.Animated.Text,
474
+ {
475
+ numberOfLines: 1,
476
+ style: [
477
+ styles.label,
478
+ {
479
+ top: labelStaticTop,
480
+ start: labelStart,
481
+ color: labelColor,
482
+ backgroundColor: labelBackgroundColor,
483
+ transform: [
484
+ { translateY: labelTranslateY },
485
+ { scale: labelScale }
486
+ ]
487
+ },
488
+ variant === "outlined" && isLabelFloated ? styles.labelNotch : void 0
489
+ ],
490
+ children: label
491
+ }
492
+ ) : null,
493
+ isFilled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.View, { style: indicatorStyle }) : null
494
+ ] }) }),
495
+ displaySupportingText ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.View, { style: styles.supportingTextRow, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
496
+ import_react_native4.Text,
497
+ {
498
+ style: [
499
+ styles.supportingText,
500
+ isError ? styles.errorSupportingText : void 0
501
+ ],
502
+ children: displaySupportingText
503
+ }
504
+ ) }) : null
505
+ ] });
506
+ }
507
+ // Annotate the CommonJS export names for ESM import in node:
508
+ 0 && (module.exports = {
509
+ TextField
510
+ });
@@ -0,0 +1,51 @@
1
+ import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';
2
+ import { ComponentProps } from 'react';
3
+ import { PressableProps } from 'react-native';
4
+
5
+ /** Visual fill style of the icon button. */
6
+ type IconButtonVariant = 'filled' | 'tonal' | 'outlined' | 'standard';
7
+ /** Touch target size of the icon button. */
8
+ type IconButtonSize = 'small' | 'medium' | 'large';
9
+ interface IconButtonProps extends Omit<PressableProps, 'children' | 'onPress' | 'style' | 'accessibilityLabel'> {
10
+ /** MaterialCommunityIcons icon name to display. */
11
+ icon: ComponentProps<typeof MaterialCommunityIcons>['name'];
12
+ /** Icon to display when `selected` is `true` (toggle mode). */
13
+ selectedIcon?: ComponentProps<typeof MaterialCommunityIcons>['name'];
14
+ /** Overrides the automatic icon color derived from the variant and state. */
15
+ iconColor?: string;
16
+ /**
17
+ * Override the content (icon) color.
18
+ * Takes precedence over `iconColor` when both are provided.
19
+ */
20
+ contentColor?: string;
21
+ /**
22
+ * Override the container (background) color.
23
+ * State-layer colors (hover, press) are derived automatically.
24
+ */
25
+ containerColor?: string;
26
+ /** Custom style applied to the root container. */
27
+ style?: PressableProps['style'];
28
+ /** Called when the button is pressed. */
29
+ onPress?: () => void;
30
+ /**
31
+ * Disables the button.
32
+ * @default false
33
+ */
34
+ disabled?: boolean;
35
+ /**
36
+ * Visual style variant.
37
+ * @default 'filled'
38
+ */
39
+ variant?: IconButtonVariant;
40
+ /** Enables toggle mode. The button changes appearance based on selected/unselected state. */
41
+ selected?: boolean;
42
+ /**
43
+ * Physical size of the touch target and icon container.
44
+ * @default 'medium'
45
+ */
46
+ size?: IconButtonSize;
47
+ /** Required — icon-only buttons must have a label for screen readers. */
48
+ accessibilityLabel: string;
49
+ }
50
+
51
+ export type { IconButtonProps as I, IconButtonSize as a, IconButtonVariant as b };
@@ -0,0 +1,28 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, ComponentType } from 'react';
3
+ import { TextProps, StyleProp, TextStyle } from 'react-native';
4
+
5
+ /** Material Design 3 type scale role. */
6
+ type TypographyVariant = 'displayLarge' | 'displayMedium' | 'displaySmall' | 'headlineLarge' | 'headlineMedium' | 'headlineSmall' | 'titleLarge' | 'titleMedium' | 'titleSmall' | 'bodyLarge' | 'bodyMedium' | 'bodySmall' | 'labelLarge' | 'labelMedium' | 'labelSmall';
7
+
8
+ interface TypographyProps extends Omit<TextProps, 'children' | 'style'> {
9
+ /** Content to display. Accepts strings, numbers, or nested elements. */
10
+ children: ReactNode;
11
+ /**
12
+ * MD3 type scale role. Controls font size, weight, line height, and letter spacing.
13
+ * @default 'bodyMedium'
14
+ */
15
+ variant?: TypographyVariant;
16
+ /** Override the text color. Takes priority over `style.color`. Defaults to the theme's `onSurface` color. */
17
+ color?: string;
18
+ /** Additional text styles. Can override the default theme color via `style.color` when no `color` prop is set. */
19
+ style?: StyleProp<TextStyle>;
20
+ /**
21
+ * Override the underlying text component (e.g. Animated.Text).
22
+ * @default Text
23
+ */
24
+ as?: ComponentType<TextProps>;
25
+ }
26
+ declare function Typography({ children, variant, color, style, as: Component, accessibilityRole, ...textProps }: TypographyProps): react_jsx_runtime.JSX.Element;
27
+
28
+ export { Typography, type TypographyProps, type TypographyVariant };
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/typography/index.ts
21
+ var typography_exports = {};
22
+ __export(typography_exports, {
23
+ Typography: () => Typography
24
+ });
25
+ module.exports = __toCommonJS(typography_exports);
26
+
27
+ // src/typography/Typography.tsx
28
+ var import_react = require("react");
29
+ var import_react_native = require("react-native");
30
+ var import_core = require("@onlynative/core");
31
+ var import_jsx_runtime = require("react/jsx-runtime");
32
+ var HEADING_VARIANTS = /* @__PURE__ */ new Set([
33
+ "displayLarge",
34
+ "displayMedium",
35
+ "displaySmall",
36
+ "headlineLarge",
37
+ "headlineMedium",
38
+ "headlineSmall"
39
+ ]);
40
+ function Typography({
41
+ children,
42
+ variant = "bodyMedium",
43
+ color,
44
+ style,
45
+ as: Component = import_react_native.Text,
46
+ accessibilityRole,
47
+ ...textProps
48
+ }) {
49
+ const theme = (0, import_core.useTheme)();
50
+ const typographyStyle = theme.typography[variant];
51
+ const resolvedRole = accessibilityRole != null ? accessibilityRole : HEADING_VARIANTS.has(variant) ? "header" : void 0;
52
+ const lineHeightFix = (0, import_react.useMemo)(() => {
53
+ if (!style) return void 0;
54
+ const flat = import_react_native.StyleSheet.flatten(style);
55
+ if (!(flat == null ? void 0 : flat.fontSize) || flat.lineHeight) return void 0;
56
+ const ratio = typographyStyle.lineHeight / typographyStyle.fontSize;
57
+ return { lineHeight: Math.ceil(flat.fontSize * ratio) };
58
+ }, [style, typographyStyle.fontSize, typographyStyle.lineHeight]);
59
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
60
+ Component,
61
+ {
62
+ ...textProps,
63
+ accessibilityRole: resolvedRole,
64
+ style: [
65
+ { color: theme.colors.onSurface },
66
+ typographyStyle,
67
+ style,
68
+ lineHeightFix,
69
+ color != null ? { color } : void 0
70
+ ],
71
+ children
72
+ }
73
+ );
74
+ }
75
+ // Annotate the CommonJS export names for ESM import in node:
76
+ 0 && (module.exports = {
77
+ Typography
78
+ });