@expokit/cli 0.0.0-stage → 0.3.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.
- package/LICENSE +21 -0
- package/README.md +46 -2
- package/bin/expo-kit.js +1266 -0
- package/bin/lib.mjs +343 -0
- package/bin/mcp.mjs +345 -0
- package/package.json +49 -4
- package/registry/_docs/accordion.json +115 -0
- package/registry/_docs/action-sheet.json +138 -0
- package/registry/_docs/alert.json +155 -0
- package/registry/_docs/avatar.json +227 -0
- package/registry/_docs/badge.json +148 -0
- package/registry/_docs/bottom-nav.json +143 -0
- package/registry/_docs/bottom-sheet.json +186 -0
- package/registry/_docs/bullet.json +27 -0
- package/registry/_docs/button-3d.json +138 -0
- package/registry/_docs/button.json +131 -0
- package/registry/_docs/calendar.json +198 -0
- package/registry/_docs/card-stack.json +124 -0
- package/registry/_docs/card.json +131 -0
- package/registry/_docs/carousel.json +122 -0
- package/registry/_docs/chat.json +184 -0
- package/registry/_docs/checkbox.json +109 -0
- package/registry/_docs/checkout.json +105 -0
- package/registry/_docs/chip-group.json +132 -0
- package/registry/_docs/color-swatch.json +82 -0
- package/registry/_docs/confetti.json +102 -0
- package/registry/_docs/coupon.json +192 -0
- package/registry/_docs/cube-pager.json +100 -0
- package/registry/_docs/dashboard.json +123 -0
- package/registry/_docs/date-picker.json +269 -0
- package/registry/_docs/dialog.json +186 -0
- package/registry/_docs/drawer.json +199 -0
- package/registry/_docs/fab.json +144 -0
- package/registry/_docs/flip-card.json +127 -0
- package/registry/_docs/floating-input.json +211 -0
- package/registry/_docs/gift-card.json +124 -0
- package/registry/_docs/glass-button.json +142 -0
- package/registry/_docs/glass-nav-bar.json +102 -0
- package/registry/_docs/glass-search-bar.json +114 -0
- package/registry/_docs/glass-segmented.json +94 -0
- package/registry/_docs/glass-sheet.json +136 -0
- package/registry/_docs/glass-tab-bar.json +100 -0
- package/registry/_docs/glass.json +94 -0
- package/registry/_docs/inbox.json +75 -0
- package/registry/_docs/isometric-bar.json +75 -0
- package/registry/_docs/kit.json +14 -0
- package/registry/_docs/kpi-card.json +14 -0
- package/registry/_docs/like-button.json +92 -0
- package/registry/_docs/liquidglass-tab.json +30 -0
- package/registry/_docs/list-item.json +216 -0
- package/registry/_docs/loyalty-card.json +124 -0
- package/registry/_docs/morph-button.json +128 -0
- package/registry/_docs/number-ticker.json +112 -0
- package/registry/_docs/offers.json +81 -0
- package/registry/_docs/onboarding.json +69 -0
- package/registry/_docs/otp-input.json +158 -0
- package/registry/_docs/pagination-dots.json +108 -0
- package/registry/_docs/parallax-layers.json +104 -0
- package/registry/_docs/parallax-scroll.json +210 -0
- package/registry/_docs/password-input.json +112 -0
- package/registry/_docs/payment-card.json +169 -0
- package/registry/_docs/pixel-bar.json +69 -0
- package/registry/_docs/polar-area.json +81 -0
- package/registry/_docs/profile.json +87 -0
- package/registry/_docs/progress-steps.json +116 -0
- package/registry/_docs/progress.json +152 -0
- package/registry/_docs/radio-group.json +112 -0
- package/registry/_docs/rating.json +114 -0
- package/registry/_docs/ring-carousel.json +122 -0
- package/registry/_docs/scratch-card.json +110 -0
- package/registry/_docs/search-bar.json +160 -0
- package/registry/_docs/search-results.json +123 -0
- package/registry/_docs/segmented-control.json +123 -0
- package/registry/_docs/select.json +277 -0
- package/registry/_docs/settings.json +75 -0
- package/registry/_docs/shimmer-button.json +98 -0
- package/registry/_docs/sign-in.json +201 -0
- package/registry/_docs/skeleton.json +162 -0
- package/registry/_docs/slide-to-confirm.json +108 -0
- package/registry/_docs/slider.json +177 -0
- package/registry/_docs/spin-wheel.json +138 -0
- package/registry/_docs/spinner.json +121 -0
- package/registry/_docs/stateful-button.json +184 -0
- package/registry/_docs/stepper.json +184 -0
- package/registry/_docs/swipe-deck.json +118 -0
- package/registry/_docs/swipeable-row.json +95 -0
- package/registry/_docs/switch.json +124 -0
- package/registry/_docs/tabs.json +129 -0
- package/registry/_docs/tag-input.json +148 -0
- package/registry/_docs/text-animation.json +141 -0
- package/registry/_docs/textarea.json +191 -0
- package/registry/_docs/ticket-card.json +194 -0
- package/registry/_docs/ticket-shape.json +14 -0
- package/registry/_docs/tilt-card.json +157 -0
- package/registry/_docs/time-picker.json +269 -0
- package/registry/_docs/timeline-chart.json +33 -0
- package/registry/_docs/timeline.json +172 -0
- package/registry/_docs/toast.json +185 -0
- package/registry/_docs/treemap.json +33 -0
- package/registry/_docs/waterfall.json +39 -0
- package/registry/_docs/wheel-list.json +122 -0
- package/registry/accordion/files/src/components/ui/accordion.tsx +233 -0
- package/registry/accordion/files/src/lib/kit.tsx +219 -0
- package/registry/accordion.json +17 -0
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +580 -0
- package/registry/action-sheet/files/src/lib/kit.tsx +219 -0
- package/registry/action-sheet.json +18 -0
- package/registry/alert/files/src/components/ui/alert.tsx +654 -0
- package/registry/alert/files/src/components/ui/button.tsx +274 -0
- package/registry/alert/files/src/lib/kit.tsx +219 -0
- package/registry/alert.json +20 -0
- package/registry/avatar/files/src/components/ui/avatar.tsx +790 -0
- package/registry/avatar/files/src/lib/kit.tsx +219 -0
- package/registry/avatar.json +18 -0
- package/registry/badge/files/src/components/ui/badge.tsx +626 -0
- package/registry/badge/files/src/lib/kit.tsx +219 -0
- package/registry/badge.json +17 -0
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +1050 -0
- package/registry/bottom-nav/files/src/lib/kit.tsx +219 -0
- package/registry/bottom-nav.json +18 -0
- package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +585 -0
- package/registry/bottom-sheet/files/src/lib/kit.tsx +219 -0
- package/registry/bottom-sheet.json +17 -0
- package/registry/bullet.json +14 -0
- package/registry/button/files/src/components/ui/button.tsx +274 -0
- package/registry/button/files/src/lib/kit.tsx +219 -0
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +212 -0
- package/registry/button-3d/files/src/lib/kit.tsx +219 -0
- package/registry/button-3d.json +18 -0
- package/registry/button.json +18 -0
- package/registry/calendar/files/src/components/ui/calendar.tsx +822 -0
- package/registry/calendar/files/src/lib/kit.tsx +219 -0
- package/registry/calendar.json +17 -0
- package/registry/card/files/src/components/ui/card.tsx +300 -0
- package/registry/card/files/src/lib/kit.tsx +219 -0
- package/registry/card-stack.json +15 -0
- package/registry/card.json +17 -0
- package/registry/carousel.json +15 -0
- package/registry/chat.json +18 -0
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +150 -0
- package/registry/checkbox/files/src/lib/kit.tsx +219 -0
- package/registry/checkbox.json +17 -0
- package/registry/checkout.json +26 -0
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +269 -0
- package/registry/chip-group/files/src/lib/kit.tsx +219 -0
- package/registry/chip-group.json +17 -0
- package/registry/color-swatch.json +17 -0
- package/registry/confetti/files/src/components/ui/confetti.tsx +222 -0
- package/registry/confetti/files/src/lib/kit.tsx +219 -0
- package/registry/confetti.json +15 -0
- package/registry/coupon/files/src/components/ui/coupon.tsx +698 -0
- package/registry/coupon/files/src/components/ui/flip-card.tsx +205 -0
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +635 -0
- package/registry/coupon/files/src/lib/kit.tsx +219 -0
- package/registry/coupon/files/src/lib/ticket-shape.tsx +543 -0
- package/registry/coupon.json +23 -0
- package/registry/cube-pager.json +15 -0
- package/registry/dashboard.json +32 -0
- package/registry/date-picker/files/src/components/ui/calendar.tsx +822 -0
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +796 -0
- package/registry/date-picker/files/src/lib/kit.tsx +219 -0
- package/registry/date-picker.json +19 -0
- package/registry/dialog/files/src/components/ui/dialog.tsx +759 -0
- package/registry/dialog/files/src/lib/kit.tsx +219 -0
- package/registry/dialog.json +18 -0
- package/registry/drawer/files/src/components/ui/drawer.tsx +1135 -0
- package/registry/drawer/files/src/lib/kit.tsx +219 -0
- package/registry/drawer.json +18 -0
- package/registry/fab.json +18 -0
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +205 -0
- package/registry/flip-card/files/src/lib/kit.tsx +219 -0
- package/registry/flip-card.json +15 -0
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +528 -0
- package/registry/floating-input/files/src/lib/kit.tsx +219 -0
- package/registry/floating-input.json +17 -0
- package/registry/gift-card.json +21 -0
- package/registry/glass-button.json +22 -0
- package/registry/glass-nav-bar.json +21 -0
- package/registry/glass-search-bar.json +21 -0
- package/registry/glass-segmented.json +22 -0
- package/registry/glass-sheet.json +22 -0
- package/registry/glass-tab-bar.json +22 -0
- package/registry/glass.json +19 -0
- package/registry/inbox.json +30 -0
- package/registry/isometric-bar.json +14 -0
- package/registry/kit/files/src/lib/kit.tsx +219 -0
- package/registry/kit.json +11 -0
- package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +45 -0
- package/registry/kpi-card.json +14 -0
- package/registry/like-button.json +18 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/_layout.tsx +97 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/alerts.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/explore.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/index.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/profile.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/_layout.tsx +33 -0
- package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +259 -0
- package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +122 -0
- package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +61 -0
- package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +130 -0
- package/registry/liquidglass-tab/files/src/lib/tabs.ts +108 -0
- package/registry/liquidglass-tab.json +23 -0
- package/registry/list-item/files/src/components/ui/list-item.tsx +508 -0
- package/registry/list-item/files/src/lib/kit.tsx +219 -0
- package/registry/list-item.json +17 -0
- package/registry/loyalty-card.json +23 -0
- package/registry/morph-button.json +18 -0
- package/registry/number-ticker.json +15 -0
- package/registry/offers.json +24 -0
- package/registry/onboarding.json +17 -0
- package/registry/otp-input/files/src/components/ui/otp-input.tsx +380 -0
- package/registry/otp-input/files/src/lib/kit.tsx +219 -0
- package/registry/otp-input.json +17 -0
- package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +262 -0
- package/registry/pagination-dots/files/src/lib/kit.tsx +219 -0
- package/registry/pagination-dots.json +15 -0
- package/registry/parallax-layers.json +15 -0
- package/registry/parallax-scroll.json +17 -0
- package/registry/password-input/files/src/components/ui/floating-input.tsx +528 -0
- package/registry/password-input/files/src/components/ui/password-input.tsx +271 -0
- package/registry/password-input/files/src/lib/kit.tsx +219 -0
- package/registry/password-input.json +19 -0
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +205 -0
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +385 -0
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +635 -0
- package/registry/payment-card/files/src/lib/kit.tsx +219 -0
- package/registry/payment-card.json +21 -0
- package/registry/pixel-bar.json +14 -0
- package/registry/polar-area.json +14 -0
- package/registry/profile.json +17 -0
- package/registry/progress/files/src/components/ui/progress.tsx +883 -0
- package/registry/progress/files/src/lib/kit.tsx +219 -0
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +559 -0
- package/registry/progress-steps/files/src/lib/kit.tsx +219 -0
- package/registry/progress-steps.json +18 -0
- package/registry/progress.json +17 -0
- package/registry/radio-group/files/src/components/ui/radio-group.tsx +327 -0
- package/registry/radio-group/files/src/lib/kit.tsx +219 -0
- package/registry/radio-group.json +18 -0
- package/registry/rating.json +18 -0
- package/registry/ring-carousel.json +15 -0
- package/registry/scratch-card.json +18 -0
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +530 -0
- package/registry/search-bar/files/src/lib/kit.tsx +219 -0
- package/registry/search-bar.json +17 -0
- package/registry/search-results.json +30 -0
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +341 -0
- package/registry/segmented-control/files/src/lib/kit.tsx +219 -0
- package/registry/segmented-control.json +18 -0
- package/registry/select/files/src/components/ui/select.tsx +1398 -0
- package/registry/select/files/src/lib/kit.tsx +219 -0
- package/registry/select.json +17 -0
- package/registry/settings.json +26 -0
- package/registry/shimmer-button.json +17 -0
- package/registry/sign-in.json +26 -0
- package/registry/skeleton/files/src/components/ui/skeleton.tsx +799 -0
- package/registry/skeleton/files/src/lib/kit.tsx +219 -0
- package/registry/skeleton.json +17 -0
- package/registry/slide-to-confirm.json +18 -0
- package/registry/slider/files/src/components/ui/slider.tsx +716 -0
- package/registry/slider/files/src/lib/kit.tsx +219 -0
- package/registry/slider.json +17 -0
- package/registry/spin-wheel.json +18 -0
- package/registry/spinner/files/src/components/ui/spinner.tsx +748 -0
- package/registry/spinner/files/src/lib/kit.tsx +219 -0
- package/registry/spinner.json +17 -0
- package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +532 -0
- package/registry/stateful-button/files/src/lib/kit.tsx +219 -0
- package/registry/stateful-button.json +18 -0
- package/registry/stepper/files/src/components/ui/stepper.tsx +646 -0
- package/registry/stepper/files/src/lib/kit.tsx +219 -0
- package/registry/stepper.json +17 -0
- package/registry/swipe-deck.json +15 -0
- package/registry/swipeable-row.json +15 -0
- package/registry/switch/files/src/components/ui/switch.tsx +344 -0
- package/registry/switch/files/src/lib/kit.tsx +219 -0
- package/registry/switch.json +18 -0
- package/registry/tabs/files/src/components/ui/tabs.tsx +539 -0
- package/registry/tabs/files/src/lib/kit.tsx +219 -0
- package/registry/tabs.json +17 -0
- package/registry/tag-input/files/src/components/ui/tag-input.tsx +720 -0
- package/registry/tag-input/files/src/lib/kit.tsx +219 -0
- package/registry/tag-input.json +17 -0
- package/registry/text-animation/files/src/components/ui/text-animation.tsx +650 -0
- package/registry/text-animation/files/src/lib/kit.tsx +219 -0
- package/registry/text-animation.json +15 -0
- package/registry/textarea/files/src/components/ui/textarea.tsx +553 -0
- package/registry/textarea/files/src/lib/kit.tsx +219 -0
- package/registry/textarea.json +17 -0
- package/registry/ticket-card.json +24 -0
- package/registry/ticket-shape/files/src/lib/kit.tsx +219 -0
- package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +543 -0
- package/registry/ticket-shape.json +17 -0
- package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +635 -0
- package/registry/tilt-card/files/src/lib/kit.tsx +219 -0
- package/registry/tilt-card.json +17 -0
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +1159 -0
- package/registry/time-picker/files/src/lib/kit.tsx +219 -0
- package/registry/time-picker.json +17 -0
- package/registry/timeline/files/src/components/ui/avatar.tsx +790 -0
- package/registry/timeline/files/src/components/ui/timeline.tsx +1059 -0
- package/registry/timeline/files/src/lib/kit.tsx +219 -0
- package/registry/timeline-chart.json +14 -0
- package/registry/timeline.json +20 -0
- package/registry/toast/files/src/components/ui/button.tsx +274 -0
- package/registry/toast/files/src/components/ui/toast.tsx +1284 -0
- package/registry/toast/files/src/lib/kit.tsx +219 -0
- package/registry/toast.json +20 -0
- package/registry/treemap.json +14 -0
- package/registry/waterfall.json +14 -0
- package/registry/wheel-list.json +17 -0
- package/schema/components.schema.json +13 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
import React, { useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
Image,
|
|
5
|
+
Pressable,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Text,
|
|
8
|
+
View,
|
|
9
|
+
type StyleProp,
|
|
10
|
+
type TextStyle,
|
|
11
|
+
type ViewStyle,
|
|
12
|
+
} from 'react-native';
|
|
13
|
+
import Svg, { Circle, Path, Rect } from 'react-native-svg';
|
|
14
|
+
|
|
15
|
+
import { alpha, isTextNode, readableOn, useKit } from '@/lib/kit';
|
|
16
|
+
|
|
17
|
+
export type ListItemSize = 'sm' | 'md' | 'lg';
|
|
18
|
+
|
|
19
|
+
export type ListItemIconName =
|
|
20
|
+
| 'user'
|
|
21
|
+
| 'bell'
|
|
22
|
+
| 'lock'
|
|
23
|
+
| 'shield'
|
|
24
|
+
| 'mail'
|
|
25
|
+
| 'card'
|
|
26
|
+
| 'help'
|
|
27
|
+
| 'logout'
|
|
28
|
+
| 'moon'
|
|
29
|
+
| 'globe'
|
|
30
|
+
| 'star'
|
|
31
|
+
| 'trash'
|
|
32
|
+
| 'settings'
|
|
33
|
+
| 'heart'
|
|
34
|
+
| 'phone'
|
|
35
|
+
| 'wifi';
|
|
36
|
+
|
|
37
|
+
export interface ListItemIconProps {
|
|
38
|
+
color: string;
|
|
39
|
+
size: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ListItemAvatar {
|
|
43
|
+
/** Image URL. Falls back to initials while loading or when it fails. */
|
|
44
|
+
src?: string;
|
|
45
|
+
/** Used for the initials and the fallback colour. */
|
|
46
|
+
name?: string;
|
|
47
|
+
/** Fallback circle colour (default: picked from the name). */
|
|
48
|
+
color?: string;
|
|
49
|
+
/** Shows a green presence dot. */
|
|
50
|
+
online?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ListItemProps {
|
|
54
|
+
title: string;
|
|
55
|
+
/** One muted line under the title. */
|
|
56
|
+
subtitle?: string;
|
|
57
|
+
/** Longer muted text under the subtitle, clamped to `numberOfLines`. */
|
|
58
|
+
description?: string;
|
|
59
|
+
/** Lines shown of `description` (default 2). */
|
|
60
|
+
numberOfLines?: number;
|
|
61
|
+
/** Small text at the right end of the title row, e.g. a time ("9:41"). */
|
|
62
|
+
meta?: string;
|
|
63
|
+
|
|
64
|
+
/** Built-in icon name or your own icon, drawn in a rounded tile. */
|
|
65
|
+
icon?: ListItemIconName | ((props: ListItemIconProps) => React.ReactNode);
|
|
66
|
+
/** Tile colour (default: theme accent; the danger colour when `destructive`). */
|
|
67
|
+
iconColor?: string;
|
|
68
|
+
/** 'filled' white glyph on a solid tile (iOS Settings) · 'tinted' coloured glyph on a soft tile. */
|
|
69
|
+
iconVariant?: 'filled' | 'tinted';
|
|
70
|
+
/** Round avatar instead of an icon tile. */
|
|
71
|
+
avatar?: ListItemAvatar;
|
|
72
|
+
/** Anything else on the left (overrides `icon` / `avatar`). */
|
|
73
|
+
leading?: React.ReactNode;
|
|
74
|
+
|
|
75
|
+
/** Muted value text on the right, e.g. "English" or "1.2 GB". */
|
|
76
|
+
value?: string;
|
|
77
|
+
/** `true` draws a dot; a number or string draws a pill (99+ above 99). */
|
|
78
|
+
badge?: boolean | number | string;
|
|
79
|
+
/** Shows a chevron. Defaults to true when the row is pressable and has no `trailing` / `checked`. */
|
|
80
|
+
chevron?: boolean;
|
|
81
|
+
/** Shows an accent check mark (for pickers). */
|
|
82
|
+
checked?: boolean;
|
|
83
|
+
/** Anything on the right, e.g. a <Switch />. Rendered after `value` and `badge`. */
|
|
84
|
+
trailing?: React.ReactNode;
|
|
85
|
+
|
|
86
|
+
onPress?: () => void;
|
|
87
|
+
onLongPress?: () => void;
|
|
88
|
+
/** Red title and icon, for "Sign out" or "Delete account". */
|
|
89
|
+
destructive?: boolean;
|
|
90
|
+
disabled?: boolean;
|
|
91
|
+
size?: ListItemSize;
|
|
92
|
+
/** Hairline under the row that starts past the leading slot. ListGroup / ListSection set it for you. */
|
|
93
|
+
divider?: boolean;
|
|
94
|
+
/** Highlight colour for checks, badges and default icon tiles (default: theme accent). */
|
|
95
|
+
accent?: string;
|
|
96
|
+
titleStyle?: StyleProp<TextStyle>;
|
|
97
|
+
accessibilityLabel?: string;
|
|
98
|
+
style?: StyleProp<ViewStyle>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const SIZES: Record<ListItemSize, { minH: number; tile: number; glyph: number; avatar: number; title: number; sub: number; padY: number }> = {
|
|
102
|
+
sm: { minH: 44, tile: 26, glyph: 15, avatar: 32, title: 15, sub: 13, padY: 8 },
|
|
103
|
+
md: { minH: 52, tile: 30, glyph: 18, avatar: 40, title: 16, sub: 13.5, padY: 11 },
|
|
104
|
+
lg: { minH: 68, tile: 38, glyph: 21, avatar: 48, title: 17, sub: 14, padY: 14 },
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const PAD_X = 16;
|
|
108
|
+
const GAP = 12;
|
|
109
|
+
const AVATAR_COLORS = ['#f97316', '#0ea5e9', '#22c55e', '#a855f7', '#ec4899', '#14b8a6', '#6366f1', '#eab308'];
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Settings / contacts style row: icon tile, avatar or custom leading; title, subtitle and
|
|
113
|
+
* description; value, badge, check, chevron or any control on the right. Pressing fades in a
|
|
114
|
+
* highlight on the native driver and fades it out slowly, like UIKit table cells.
|
|
115
|
+
*/
|
|
116
|
+
export function ListItem({
|
|
117
|
+
title,
|
|
118
|
+
subtitle,
|
|
119
|
+
description,
|
|
120
|
+
numberOfLines = 2,
|
|
121
|
+
meta,
|
|
122
|
+
icon,
|
|
123
|
+
iconColor,
|
|
124
|
+
iconVariant = 'filled',
|
|
125
|
+
avatar,
|
|
126
|
+
leading,
|
|
127
|
+
value,
|
|
128
|
+
badge,
|
|
129
|
+
chevron,
|
|
130
|
+
checked,
|
|
131
|
+
trailing,
|
|
132
|
+
onPress,
|
|
133
|
+
onLongPress,
|
|
134
|
+
destructive,
|
|
135
|
+
disabled,
|
|
136
|
+
size = 'md',
|
|
137
|
+
divider,
|
|
138
|
+
accent,
|
|
139
|
+
titleStyle,
|
|
140
|
+
accessibilityLabel,
|
|
141
|
+
style,
|
|
142
|
+
}: ListItemProps) {
|
|
143
|
+
const { colors, radius } = useKit();
|
|
144
|
+
const tint = accent ?? colors.accent;
|
|
145
|
+
const s = SIZES[size];
|
|
146
|
+
const pressable = !!(onPress || onLongPress);
|
|
147
|
+
const showChevron = chevron ?? (pressable && trailing === undefined && checked === undefined);
|
|
148
|
+
const titleColor = destructive ? colors.danger : colors.foreground;
|
|
149
|
+
|
|
150
|
+
// Press highlight: an overlay whose opacity runs on the native driver.
|
|
151
|
+
const highlight = useRef(new Animated.Value(0)).current;
|
|
152
|
+
const fade = (to: number, duration: number) =>
|
|
153
|
+
Animated.timing(highlight, { toValue: to, duration, useNativeDriver: true }).start();
|
|
154
|
+
|
|
155
|
+
// ---- leading -------------------------------------------------------------------------------
|
|
156
|
+
let lead: React.ReactNode = leading ?? null;
|
|
157
|
+
let leadW = 0;
|
|
158
|
+
if (leading) {
|
|
159
|
+
leadW = -1; // unknown width: divider starts at the text via onLayout below
|
|
160
|
+
} else if (avatar) {
|
|
161
|
+
lead = <AvatarCircle {...avatar} size={s.avatar} ring={colors.card} />;
|
|
162
|
+
leadW = s.avatar;
|
|
163
|
+
} else if (icon) {
|
|
164
|
+
const tile = iconColor ?? (destructive ? colors.danger : tint);
|
|
165
|
+
const filled = iconVariant === 'filled';
|
|
166
|
+
const glyphColor = filled ? readableOn(tile) : tile;
|
|
167
|
+
lead = (
|
|
168
|
+
<View
|
|
169
|
+
style={[
|
|
170
|
+
styles.tile,
|
|
171
|
+
{ width: s.tile, height: s.tile, borderRadius: Math.min(s.tile / 2, radius.scale(Math.round(s.tile * 0.27))), backgroundColor: filled ? tile : alpha(tile, 0.14) },
|
|
172
|
+
]}
|
|
173
|
+
>
|
|
174
|
+
{typeof icon === 'function' ? icon({ color: glyphColor, size: s.glyph }) : <ListIcon name={icon} color={glyphColor} size={s.glyph} />}
|
|
175
|
+
</View>
|
|
176
|
+
);
|
|
177
|
+
leadW = s.tile;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Custom leading content: measure it so the divider still lines up with the text.
|
|
181
|
+
const [measured, setMeasured] = useState(0);
|
|
182
|
+
const inset = PAD_X + (leadW === -1 ? measured : leadW ? leadW + GAP : 0);
|
|
183
|
+
|
|
184
|
+
const badgeText = typeof badge === 'number' ? (badge > 99 ? '99+' : String(badge)) : typeof badge === 'string' ? badge : null;
|
|
185
|
+
|
|
186
|
+
const content = (
|
|
187
|
+
<>
|
|
188
|
+
{lead ? (
|
|
189
|
+
<View onLayout={leadW === -1 ? (e) => setMeasured(e.nativeEvent.layout.width + GAP) : undefined} style={styles.leading}>
|
|
190
|
+
{lead}
|
|
191
|
+
</View>
|
|
192
|
+
) : null}
|
|
193
|
+
<View style={styles.body}>
|
|
194
|
+
<View style={styles.titleRow}>
|
|
195
|
+
<Text numberOfLines={1} style={[styles.title, { fontSize: s.title, color: titleColor }, titleStyle]}>
|
|
196
|
+
{title}
|
|
197
|
+
</Text>
|
|
198
|
+
{meta ? <Text style={[styles.meta, { color: colors.mutedForeground }]}>{meta}</Text> : null}
|
|
199
|
+
</View>
|
|
200
|
+
{subtitle ? (
|
|
201
|
+
<Text numberOfLines={1} style={[styles.sub, { fontSize: s.sub, color: colors.mutedForeground }]}>
|
|
202
|
+
{subtitle}
|
|
203
|
+
</Text>
|
|
204
|
+
) : null}
|
|
205
|
+
{description ? (
|
|
206
|
+
<Text numberOfLines={numberOfLines} style={[styles.desc, { fontSize: s.sub, lineHeight: s.sub * 1.36, color: colors.mutedForeground }]}>
|
|
207
|
+
{description}
|
|
208
|
+
</Text>
|
|
209
|
+
) : null}
|
|
210
|
+
</View>
|
|
211
|
+
{value || badge || checked || trailing !== undefined || showChevron ? (
|
|
212
|
+
<View style={styles.trailing}>
|
|
213
|
+
{value ? (
|
|
214
|
+
<Text numberOfLines={1} style={[styles.value, { color: colors.mutedForeground }]}>
|
|
215
|
+
{value}
|
|
216
|
+
</Text>
|
|
217
|
+
) : null}
|
|
218
|
+
{badge === true ? <View style={[styles.dot, { backgroundColor: tint }]} /> : null}
|
|
219
|
+
{badgeText ? (
|
|
220
|
+
<View style={[styles.badge, { borderRadius: radius.capsule(22), backgroundColor: tint }]}>
|
|
221
|
+
<Text style={[styles.badgeText, { color: readableOn(tint) }]}>{badgeText}</Text>
|
|
222
|
+
</View>
|
|
223
|
+
) : null}
|
|
224
|
+
{isTextNode(trailing) ? <Text style={[styles.value, { color: colors.mutedForeground }]}>{trailing}</Text> : trailing}
|
|
225
|
+
{checked ? <Check color={tint} /> : null}
|
|
226
|
+
{showChevron ? <Chevron color={alpha(colors.mutedForeground, 0.7)} /> : null}
|
|
227
|
+
</View>
|
|
228
|
+
) : null}
|
|
229
|
+
{divider ? (
|
|
230
|
+
<View pointerEvents="none" style={[styles.divider, { left: inset, backgroundColor: colors.border }]} />
|
|
231
|
+
) : null}
|
|
232
|
+
</>
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const rowStyle = [styles.row, { minHeight: s.minH, paddingVertical: s.padY }, disabled && styles.disabled, style];
|
|
236
|
+
|
|
237
|
+
if (!pressable) {
|
|
238
|
+
return (
|
|
239
|
+
<View accessible accessibilityLabel={accessibilityLabel} style={rowStyle}>
|
|
240
|
+
{content}
|
|
241
|
+
</View>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<Pressable
|
|
247
|
+
onPress={onPress}
|
|
248
|
+
onLongPress={onLongPress}
|
|
249
|
+
disabled={disabled}
|
|
250
|
+
onPressIn={() => fade(1, 60)}
|
|
251
|
+
onPressOut={() => fade(0, 320)}
|
|
252
|
+
accessibilityRole="button"
|
|
253
|
+
accessibilityState={{ disabled: !!disabled, checked: checked === undefined ? undefined : checked }}
|
|
254
|
+
accessibilityLabel={accessibilityLabel ?? [title, subtitle, value, badgeText ? `${badgeText} new` : null].filter(Boolean).join(', ')}
|
|
255
|
+
style={rowStyle}
|
|
256
|
+
>
|
|
257
|
+
<Animated.View
|
|
258
|
+
pointerEvents="none"
|
|
259
|
+
style={[
|
|
260
|
+
StyleSheet.absoluteFill,
|
|
261
|
+
{ backgroundColor: destructive ? alpha(colors.danger, 0.1) : alpha(colors.foreground, 0.07), opacity: highlight },
|
|
262
|
+
]}
|
|
263
|
+
/>
|
|
264
|
+
{content}
|
|
265
|
+
</Pressable>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface ListGroupProps {
|
|
270
|
+
/** Small uppercase heading above the card. */
|
|
271
|
+
header?: string;
|
|
272
|
+
/** Muted explanation under the card. */
|
|
273
|
+
footer?: string;
|
|
274
|
+
/** Card colour (default: theme card). */
|
|
275
|
+
background?: string;
|
|
276
|
+
children: React.ReactNode;
|
|
277
|
+
style?: StyleProp<ViewStyle>;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* iOS inset-grouped card: rounded surface with a header and footer, and hairline separators
|
|
282
|
+
* between rows that start past the icon so they line up with the text.
|
|
283
|
+
*/
|
|
284
|
+
export function ListGroup({ header, footer, background, children, style }: ListGroupProps) {
|
|
285
|
+
const { colors, radius } = useKit();
|
|
286
|
+
return (
|
|
287
|
+
<View style={[styles.group, style]}>
|
|
288
|
+
{header ? (
|
|
289
|
+
<Text accessibilityRole="header" style={[styles.header, { color: colors.mutedForeground }]}>
|
|
290
|
+
{header}
|
|
291
|
+
</Text>
|
|
292
|
+
) : null}
|
|
293
|
+
<View
|
|
294
|
+
style={[
|
|
295
|
+
styles.card,
|
|
296
|
+
{ backgroundColor: background ?? colors.card, borderColor: colors.border, borderRadius: radius.lg },
|
|
297
|
+
]}
|
|
298
|
+
>
|
|
299
|
+
{withDividers(children)}
|
|
300
|
+
</View>
|
|
301
|
+
{footer ? <Text style={[styles.footer, { color: colors.mutedForeground }]}>{footer}</Text> : null}
|
|
302
|
+
</View>
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export interface ListSectionProps {
|
|
307
|
+
/** Section heading, e.g. "A" in a contacts list or "Today" in an inbox. */
|
|
308
|
+
header?: string;
|
|
309
|
+
footer?: string;
|
|
310
|
+
/** Hairline under the last row too (default false). */
|
|
311
|
+
dividerAfterLast?: boolean;
|
|
312
|
+
children: React.ReactNode;
|
|
313
|
+
style?: StyleProp<ViewStyle>;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Plain full-width section: header text, then rows with inset separators. */
|
|
317
|
+
export function ListSection({ header, footer, dividerAfterLast, children, style }: ListSectionProps) {
|
|
318
|
+
const { colors } = useKit();
|
|
319
|
+
return (
|
|
320
|
+
<View style={style}>
|
|
321
|
+
{header ? (
|
|
322
|
+
<Text accessibilityRole="header" style={[styles.sectionHeader, { color: colors.mutedForeground }]}>
|
|
323
|
+
{header}
|
|
324
|
+
</Text>
|
|
325
|
+
) : null}
|
|
326
|
+
{withDividers(children, dividerAfterLast)}
|
|
327
|
+
{footer ? <Text style={[styles.footer, { color: colors.mutedForeground }]}>{footer}</Text> : null}
|
|
328
|
+
</View>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Gives every ListItem but the last a divider (unless it sets `divider` itself). */
|
|
333
|
+
function withDividers(children: React.ReactNode, last = false) {
|
|
334
|
+
const items = React.Children.toArray(children).filter(React.isValidElement);
|
|
335
|
+
return items.map((child, i) => {
|
|
336
|
+
if (child.type !== ListItem) return child;
|
|
337
|
+
const el = child as React.ReactElement<ListItemProps>;
|
|
338
|
+
if (el.props.divider !== undefined) return el;
|
|
339
|
+
return React.cloneElement(el, { divider: last || i < items.length - 1 });
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function AvatarCircle({ src, name = '', color, online, size, ring }: ListItemAvatar & { size: number; ring: string }) {
|
|
344
|
+
const { colors } = useKit();
|
|
345
|
+
const [failed, setFailed] = useState(false);
|
|
346
|
+
const initials = name
|
|
347
|
+
.split(/\s+/)
|
|
348
|
+
.filter(Boolean)
|
|
349
|
+
.slice(0, 2)
|
|
350
|
+
.map((w) => w[0]!.toUpperCase())
|
|
351
|
+
.join('');
|
|
352
|
+
let hash = 0;
|
|
353
|
+
for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0;
|
|
354
|
+
const bg = color ?? AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
|
|
355
|
+
const dot = Math.max(10, Math.round(size * 0.28));
|
|
356
|
+
return (
|
|
357
|
+
<View style={{ width: size, height: size }}>
|
|
358
|
+
<View style={[styles.avatar, { width: size, height: size, borderRadius: size / 2, backgroundColor: bg }]}>
|
|
359
|
+
<Text style={{ color: readableOn(bg), fontSize: size * 0.38, fontWeight: '400' }}>{initials}</Text>
|
|
360
|
+
{src && !failed ? (
|
|
361
|
+
<Image source={{ uri: src }} onError={() => setFailed(true)} style={[StyleSheet.absoluteFill, { borderRadius: size / 2 }]} />
|
|
362
|
+
) : null}
|
|
363
|
+
</View>
|
|
364
|
+
{online ? (
|
|
365
|
+
<View
|
|
366
|
+
style={[
|
|
367
|
+
styles.online,
|
|
368
|
+
{ width: dot, height: dot, borderRadius: dot / 2, borderColor: ring, backgroundColor: colors.success },
|
|
369
|
+
]}
|
|
370
|
+
/>
|
|
371
|
+
) : null}
|
|
372
|
+
</View>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function Chevron({ color }: { color: string }) {
|
|
377
|
+
return (
|
|
378
|
+
<Svg width={14} height={14} viewBox="0 0 24 24" fill="none">
|
|
379
|
+
<Path d="M9 5l7 7-7 7" stroke={color} strokeWidth={2.6} strokeLinecap="round" strokeLinejoin="round" />
|
|
380
|
+
</Svg>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function Check({ color }: { color: string }) {
|
|
385
|
+
return (
|
|
386
|
+
<Svg width={18} height={18} viewBox="0 0 24 24" fill="none">
|
|
387
|
+
<Path d="M5 12.5l4.5 4.5L19 7.5" stroke={color} strokeWidth={2.6} strokeLinecap="round" strokeLinejoin="round" />
|
|
388
|
+
</Svg>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** The built-in glyphs, 24×24 line icons with a 1.8 stroke. */
|
|
393
|
+
export function ListIcon({ name, color, size = 20 }: { name: ListItemIconName; color: string; size?: number }) {
|
|
394
|
+
const p = { stroke: color, strokeWidth: 1.8, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const, fill: 'none' };
|
|
395
|
+
const paths: Record<ListItemIconName, React.ReactNode> = {
|
|
396
|
+
user: (
|
|
397
|
+
<>
|
|
398
|
+
<Circle cx={12} cy={8} r={4} {...p} />
|
|
399
|
+
<Path d="M4 20.5c1.2-3.6 4.3-5.5 8-5.5s6.8 1.9 8 5.5" {...p} />
|
|
400
|
+
</>
|
|
401
|
+
),
|
|
402
|
+
bell: (
|
|
403
|
+
<>
|
|
404
|
+
<Path d="M6 16v-5a6 6 0 0 1 12 0v5l1.5 2h-15z" {...p} />
|
|
405
|
+
<Path d="M10 21h4" {...p} />
|
|
406
|
+
</>
|
|
407
|
+
),
|
|
408
|
+
lock: (
|
|
409
|
+
<>
|
|
410
|
+
<Rect x={5} y={11} width={14} height={10} rx={2.5} {...p} />
|
|
411
|
+
<Path d="M8 11V8a4 4 0 0 1 8 0v3" {...p} />
|
|
412
|
+
</>
|
|
413
|
+
),
|
|
414
|
+
shield: (
|
|
415
|
+
<>
|
|
416
|
+
<Path d="M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6z" {...p} />
|
|
417
|
+
<Path d="M9 12l2 2 4-4" {...p} />
|
|
418
|
+
</>
|
|
419
|
+
),
|
|
420
|
+
mail: (
|
|
421
|
+
<>
|
|
422
|
+
<Rect x={3} y={5} width={18} height={14} rx={2.5} {...p} />
|
|
423
|
+
<Path d="M3.5 7l8.5 6 8.5-6" {...p} />
|
|
424
|
+
</>
|
|
425
|
+
),
|
|
426
|
+
card: (
|
|
427
|
+
<>
|
|
428
|
+
<Rect x={3} y={5} width={18} height={14} rx={2.5} {...p} />
|
|
429
|
+
<Path d="M3 10h18M7 15h4" {...p} />
|
|
430
|
+
</>
|
|
431
|
+
),
|
|
432
|
+
help: (
|
|
433
|
+
<>
|
|
434
|
+
<Circle cx={12} cy={12} r={9} {...p} />
|
|
435
|
+
<Path d="M9.5 9.5a2.5 2.5 0 1 1 3.5 2.3c-.6.3-1 .9-1 1.6v.3" {...p} />
|
|
436
|
+
<Circle cx={12} cy={17} r={1} fill={color} />
|
|
437
|
+
</>
|
|
438
|
+
),
|
|
439
|
+
logout: (
|
|
440
|
+
<>
|
|
441
|
+
<Path d="M15 4h3a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-3" {...p} />
|
|
442
|
+
<Path d="M10 8l-4 4 4 4M6 12h10" {...p} />
|
|
443
|
+
</>
|
|
444
|
+
),
|
|
445
|
+
moon: <Path d="M20 14.2A8 8 0 1 1 9.8 4a6.4 6.4 0 0 0 10.2 10.2z" {...p} />,
|
|
446
|
+
globe: (
|
|
447
|
+
<>
|
|
448
|
+
<Circle cx={12} cy={12} r={9} {...p} />
|
|
449
|
+
<Path d="M3 12h18M12 3c2.5 2.6 3.7 5.6 3.7 9s-1.2 6.4-3.7 9c-2.5-2.6-3.7-5.6-3.7-9S9.5 5.6 12 3z" {...p} />
|
|
450
|
+
</>
|
|
451
|
+
),
|
|
452
|
+
star: <Path d="M12 3.5l2.6 5.4 5.9.8-4.3 4.1 1 5.8L12 16.8l-5.2 2.8 1-5.8-4.3-4.1 5.9-.8z" {...p} />,
|
|
453
|
+
trash: (
|
|
454
|
+
<>
|
|
455
|
+
<Path d="M4 7h16M10 11v6M14 11v6M9 7V4.5h6V7" {...p} />
|
|
456
|
+
<Path d="M6 7l1 13h10l1-13" {...p} />
|
|
457
|
+
</>
|
|
458
|
+
),
|
|
459
|
+
settings: (
|
|
460
|
+
<>
|
|
461
|
+
<Circle cx={12} cy={12} r={3} {...p} />
|
|
462
|
+
<Path
|
|
463
|
+
d="M12 2.8l1.6 2.3 2.7-.6.6 2.7 2.3 1.6-1.2 2.5 1.2 2.5-2.3 1.6-.6 2.7-2.7-.6L12 21.2l-1.6-2.3-2.7.6-.6-2.7-2.3-1.6L6 12.7 4.8 10.2l2.3-1.6.6-2.7 2.7.6z"
|
|
464
|
+
{...p}
|
|
465
|
+
/>
|
|
466
|
+
</>
|
|
467
|
+
),
|
|
468
|
+
heart: <Path d="M12 20s-7.5-4.6-7.5-10A4.3 4.3 0 0 1 12 7.4 4.3 4.3 0 0 1 19.5 10c0 5.4-7.5 10-7.5 10z" {...p} />,
|
|
469
|
+
phone: <Path d="M5 4h3.5l1.7 4.3-2.2 1.4a11 11 0 0 0 6.3 6.3l1.4-2.2L20 15.5V19a2 2 0 0 1-2.2 2A16.5 16.5 0 0 1 3 6.2 2 2 0 0 1 5 4z" {...p} />,
|
|
470
|
+
wifi: (
|
|
471
|
+
<>
|
|
472
|
+
<Path d="M2.5 9a14 14 0 0 1 19 0M5.8 12.5a9.2 9.2 0 0 1 12.4 0M9.1 16a4.5 4.5 0 0 1 5.8 0" {...p} />
|
|
473
|
+
<Circle cx={12} cy={19.2} r={1.1} fill={color} />
|
|
474
|
+
</>
|
|
475
|
+
),
|
|
476
|
+
};
|
|
477
|
+
return (
|
|
478
|
+
<Svg width={size} height={size} viewBox="0 0 24 24">
|
|
479
|
+
{paths[name]}
|
|
480
|
+
</Svg>
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const styles = StyleSheet.create({
|
|
485
|
+
row: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: PAD_X, gap: GAP, overflow: 'hidden' },
|
|
486
|
+
disabled: { opacity: 0.45 },
|
|
487
|
+
leading: { alignItems: 'center', justifyContent: 'center' },
|
|
488
|
+
tile: { alignItems: 'center', justifyContent: 'center' },
|
|
489
|
+
body: { flex: 1, minWidth: 0, justifyContent: 'center' },
|
|
490
|
+
titleRow: { flexDirection: 'row', alignItems: 'baseline', gap: 8 },
|
|
491
|
+
title: { flex: 1, fontWeight: '400', letterSpacing: -0.2 },
|
|
492
|
+
meta: { fontSize: 12.5, fontVariant: ['tabular-nums'] },
|
|
493
|
+
sub: { marginTop: 2 },
|
|
494
|
+
desc: { marginTop: 2 },
|
|
495
|
+
trailing: { flexDirection: 'row', alignItems: 'center', gap: 8, flexShrink: 0, maxWidth: '50%' },
|
|
496
|
+
value: { fontSize: 15, flexShrink: 1 },
|
|
497
|
+
dot: { width: 9, height: 9, borderRadius: 5 },
|
|
498
|
+
badge: { minWidth: 22, height: 22, paddingHorizontal: 7, alignItems: 'center', justifyContent: 'center' },
|
|
499
|
+
badgeText: { fontSize: 12, fontWeight: '400', fontVariant: ['tabular-nums'] },
|
|
500
|
+
divider: { position: 'absolute', right: 0, bottom: 0, height: StyleSheet.hairlineWidth },
|
|
501
|
+
avatar: { alignItems: 'center', justifyContent: 'center', overflow: 'hidden' },
|
|
502
|
+
online: { position: 'absolute', right: -1, bottom: -1, borderWidth: 2 },
|
|
503
|
+
group: { gap: 8, alignSelf: 'stretch' },
|
|
504
|
+
header: { fontSize: 12.5, fontWeight: '400', textTransform: 'uppercase', letterSpacing: 0.6, paddingHorizontal: 16 },
|
|
505
|
+
card: { overflow: 'hidden', borderWidth: StyleSheet.hairlineWidth },
|
|
506
|
+
footer: { fontSize: 12.5, lineHeight: 17, paddingHorizontal: 16 },
|
|
507
|
+
sectionHeader: { fontSize: 13, fontWeight: '400', letterSpacing: 0.3, paddingHorizontal: 16, paddingTop: 12, paddingBottom: 4 },
|
|
508
|
+
});
|