@sincpro/mobile-ui 0.7.2 → 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.
- package/README.md +575 -230
- package/dist/Actions/Actions.Button.d.ts +98 -93
- package/dist/Actions/Actions.Button.js +58 -52
- package/dist/Actions/Actions.FAB.d.ts +3 -1
- package/dist/Actions/Actions.FAB.js +12 -8
- package/dist/Actions/Actions.FilterButton.d.ts +10 -2
- package/dist/Actions/Actions.FilterButton.js +23 -8
- package/dist/Actions/Actions.IconButton.d.ts +36 -33
- package/dist/Actions/Actions.IconButton.js +10 -6
- package/dist/Actions/index.d.ts +12 -12
- package/dist/Actions/index.js +6 -6
- package/dist/Dialog/BottomSheet/BottomSheet.Root.js +1 -1
- package/dist/Dialog/BottomSheet/index.d.ts +1 -1
- package/dist/Dialog/BottomSheet/index.js +1 -1
- package/dist/Dialog/Confirmation.context.js +3 -3
- package/dist/Dialog/Dialog.ActionSheet.d.ts +2 -1
- package/dist/Dialog/Dialog.ActionSheet.js +19 -9
- package/dist/Dialog/Dialog.Confirmation.js +7 -7
- package/dist/Dialog/Dialog.EditValue.js +4 -4
- package/dist/Dialog/Dialog.NativeSheet.d.ts +10 -0
- package/dist/Dialog/Dialog.NativeSheet.js +21 -0
- package/dist/Dialog/Dialog.Sheet.d.ts +5 -1
- package/dist/Dialog/Dialog.Sheet.js +19 -11
- package/dist/Dialog/index.d.ts +5 -5
- package/dist/Dialog/index.js +5 -5
- package/dist/Display/Display.Accordion.js +5 -5
- package/dist/Display/Display.Avatar.d.ts +30 -30
- package/dist/Display/Display.Avatar.js +2 -2
- package/dist/Display/Display.AvatarGroup.js +3 -3
- package/dist/Display/Display.Badge.d.ts +3 -3
- package/dist/Display/Display.Badge.js +1 -1
- package/dist/Display/Display.Card.d.ts +15 -15
- package/dist/Display/Display.Card.js +4 -4
- package/dist/Display/Display.Carousel.js +2 -2
- package/dist/Display/Display.Chip.d.ts +3 -3
- package/dist/Display/Display.Chip.js +12 -12
- package/dist/Display/Display.CircleButton.d.ts +1 -1
- package/dist/Display/Display.CircleButton.js +3 -3
- package/dist/Display/Display.CopyableText.d.ts +9 -2
- package/dist/Display/Display.CopyableText.js +76 -8
- package/dist/Display/Display.CountRecords.js +2 -2
- package/dist/Display/Display.DataTable.js +2 -2
- package/dist/Display/Display.Date.d.ts +1 -1
- package/dist/Display/Display.Date.js +5 -5
- package/dist/Display/Display.Divider.d.ts +24 -24
- package/dist/Display/Display.Divider.js +1 -1
- package/dist/Display/Display.GradientSurface.d.ts +58 -39
- package/dist/Display/Display.GradientSurface.js +31 -15
- package/dist/Display/Display.Icon.d.ts +1 -1
- package/dist/Display/Display.Icon.js +2 -1
- package/dist/Display/Display.IconBadge.js +3 -3
- package/dist/Display/Display.Image.js +4 -4
- package/dist/Display/Display.KeyValue.d.ts +11 -4
- package/dist/Display/Display.KeyValue.js +8 -6
- package/dist/Display/Display.ListGroup.d.ts +23 -0
- package/dist/Display/Display.ListGroup.js +17 -0
- package/dist/Display/Display.ListItem.d.ts +16 -2
- package/dist/Display/Display.ListItem.js +29 -12
- package/dist/Display/Display.Logo.d.ts +3 -3
- package/dist/Display/Display.Logo.js +2 -2
- package/dist/Display/Display.MediaCard.js +4 -4
- package/dist/Display/Display.MediaEmbed.js +7 -7
- package/dist/Display/Display.MenuButton.d.ts +4 -4
- package/dist/Display/Display.MenuButton.js +6 -6
- package/dist/Display/Display.MenuCard.d.ts +1 -1
- package/dist/Display/Display.MenuCard.js +3 -3
- package/dist/Display/Display.MenuGrid.d.ts +1 -1
- package/dist/Display/Display.MenuGrid.js +1 -1
- package/dist/Display/Display.Monetary.d.ts +1 -1
- package/dist/Display/Display.Monetary.js +2 -2
- package/dist/Display/Display.Pattern.d.ts +33 -12
- package/dist/Display/Display.Pattern.js +60 -14
- package/dist/Display/Display.PriceTag.js +2 -2
- package/dist/Display/Display.ProductCard.js +7 -7
- package/dist/Display/Display.Rating.js +3 -3
- package/dist/Display/Display.SearchHistory.js +6 -6
- package/dist/Display/Display.SectionHeader.d.ts +11 -1
- package/dist/Display/Display.SectionHeader.js +14 -5
- package/dist/Display/Display.Stat.js +2 -2
- package/dist/Display/Display.StatCard.js +4 -4
- package/dist/Display/Display.StatusDot.js +3 -3
- package/dist/Display/Display.Story.js +3 -3
- package/dist/Display/Display.StoryTray.js +5 -5
- package/dist/Display/Display.StoryViewer.js +4 -4
- package/dist/Display/Display.SwipeableRow.d.ts +3 -1
- package/dist/Display/Display.SwipeableRow.js +7 -7
- package/dist/Display/Display.Tag.js +2 -2
- package/dist/Display/Display.Timeline.js +4 -4
- package/dist/Display/Display.Tooltip.js +3 -3
- package/dist/Display/index.d.ts +49 -45
- package/dist/Display/index.js +44 -42
- package/dist/Feedback/ErrorBoundary.d.ts +1 -1
- package/dist/Feedback/ErrorBoundary.js +1 -1
- package/dist/Feedback/Feedback.Banner.d.ts +3 -3
- package/dist/Feedback/Feedback.Banner.js +6 -6
- package/dist/Feedback/Feedback.DomainSplashScreen.js +5 -4
- package/dist/Feedback/Feedback.Empty.js +3 -3
- package/dist/Feedback/Feedback.EmptyState.d.ts +2 -1
- package/dist/Feedback/Feedback.EmptyState.js +7 -7
- package/dist/Feedback/Feedback.Error.d.ts +3 -1
- package/dist/Feedback/Feedback.Error.js +7 -6
- package/dist/Feedback/Feedback.Loading.d.ts +1 -1
- package/dist/Feedback/Feedback.Loading.js +2 -2
- package/dist/Feedback/Feedback.PermissionCard.js +5 -5
- package/dist/Feedback/Feedback.Progress.js +2 -2
- package/dist/Feedback/Feedback.ProgressCircle.js +1 -1
- package/dist/Feedback/Feedback.Skeleton.js +2 -2
- package/dist/Feedback/Feedback.Spinner.js +3 -3
- package/dist/Feedback/Feedback.Splash.d.ts +1 -1
- package/dist/Feedback/Feedback.Splash.js +7 -6
- package/dist/Feedback/Toast.context.js +7 -7
- package/dist/Feedback/index.d.ts +13 -13
- package/dist/Feedback/index.js +13 -13
- package/dist/Form/Form.Button.d.ts +2 -2
- package/dist/Form/Form.Button.js +1 -1
- package/dist/Form/Form.Checkbox.d.ts +4 -1
- package/dist/Form/Form.Checkbox.js +5 -5
- package/dist/Form/Form.ControlledCheckbox.js +1 -1
- package/dist/Form/Form.ControlledInput.js +1 -1
- package/dist/Form/Form.ControlledMonetaryInput.js +1 -1
- package/dist/Form/Form.ControlledRadioButton.js +1 -1
- package/dist/Form/Form.DatePicker.d.ts +2 -1
- package/dist/Form/Form.DatePicker.js +8 -7
- package/dist/Form/Form.Dropdown.js +7 -7
- package/dist/Form/Form.FAB.d.ts +2 -2
- package/dist/Form/Form.FAB.js +1 -1
- package/dist/Form/Form.FilterButton.d.ts +2 -2
- package/dist/Form/Form.FilterButton.js +1 -1
- package/dist/Form/Form.FractionalQuantityInput.js +4 -4
- package/dist/Form/Form.IconButton.d.ts +2 -2
- package/dist/Form/Form.IconButton.js +1 -1
- package/dist/Form/Form.Input.d.ts +5 -3
- package/dist/Form/Form.Input.js +5 -5
- package/dist/Form/Form.InputField.d.ts +1 -1
- package/dist/Form/Form.InputField.js +3 -3
- package/dist/Form/Form.KeyboardForm.d.ts +11 -0
- package/dist/Form/Form.KeyboardForm.js +9 -0
- package/dist/Form/Form.KeyboardToolbar.d.ts +21 -0
- package/dist/Form/Form.KeyboardToolbar.js +70 -0
- package/dist/Form/Form.MonetaryInput.js +5 -4
- package/dist/Form/Form.PasswordInput.d.ts +1 -1
- package/dist/Form/Form.PasswordInput.js +3 -3
- package/dist/Form/Form.PinInput.js +5 -5
- package/dist/Form/Form.QuantitySelector.js +3 -3
- package/dist/Form/Form.RadioButton.js +4 -4
- package/dist/Form/Form.SearchBar.js +4 -4
- package/dist/Form/Form.Select.d.ts +3 -1
- package/dist/Form/Form.Select.js +27 -14
- package/dist/Form/Form.Slider.js +3 -3
- package/dist/Form/Form.Switch.js +4 -4
- package/dist/Form/Form.TextInput.js +3 -3
- package/dist/Form/index.d.ts +33 -27
- package/dist/Form/index.js +29 -25
- package/dist/Navigation/Navigation.AppBar.d.ts +11 -6
- package/dist/Navigation/Navigation.AppBar.js +16 -16
- package/dist/Navigation/Navigation.BottomNav.d.ts +4 -2
- package/dist/Navigation/Navigation.BottomNav.js +34 -21
- package/dist/Navigation/Navigation.CollapsingHeader.d.ts +1 -1
- package/dist/Navigation/Navigation.CollapsingHeader.js +3 -3
- package/dist/Navigation/Navigation.FloatingBar.js +2 -2
- package/dist/Navigation/Navigation.Header.js +6 -6
- package/dist/Navigation/Navigation.HeaderToolbar.js +5 -5
- package/dist/Navigation/Navigation.Menu.js +6 -6
- package/dist/Navigation/Navigation.MenuSection.js +2 -2
- package/dist/Navigation/Navigation.RowItem.d.ts +2 -2
- package/dist/Navigation/Navigation.RowItem.js +4 -4
- package/dist/Navigation/Navigation.SearchBar.d.ts +6 -1
- package/dist/Navigation/Navigation.SearchBar.js +33 -12
- package/dist/Navigation/Navigation.SearchSuggestions.d.ts +14 -0
- package/dist/Navigation/Navigation.SearchSuggestions.js +7 -0
- package/dist/Navigation/Navigation.SegmentedControl.d.ts +3 -1
- package/dist/Navigation/Navigation.SegmentedControl.js +6 -5
- package/dist/Navigation/Navigation.Steps.js +5 -5
- package/dist/Navigation/Navigation.TabIcon.js +2 -2
- package/dist/Navigation/Navigation.TabItem.js +2 -2
- package/dist/Navigation/Navigation.Tabs.js +5 -5
- package/dist/Navigation/index.d.ts +23 -19
- package/dist/Navigation/index.js +17 -15
- package/dist/Typography/Typography.Heading.d.ts +1 -1
- package/dist/Typography/Typography.Heading.js +2 -2
- package/dist/Typography/Typography.SelectableText.d.ts +10 -0
- package/dist/Typography/Typography.SelectableText.js +16 -0
- package/dist/Typography/Typography.Text.d.ts +2 -2
- package/dist/Typography/Typography.Text.js +5 -19
- package/dist/Typography/index.d.ts +5 -2
- package/dist/Typography/index.js +5 -2
- package/dist/branding.d.ts +22 -0
- package/dist/branding.js +20 -3
- package/dist/icons/AddItemIcon.d.ts +1 -1
- package/dist/icons/ArrangeCircle.d.ts +1 -1
- package/dist/icons/ArrowDownIcon.d.ts +1 -1
- package/dist/icons/ArrowUpIcon.d.ts +1 -1
- package/dist/icons/BoxIcon.d.ts +1 -1
- package/dist/icons/BoxTimeIcon.d.ts +1 -1
- package/dist/icons/CalendarIcon.d.ts +1 -1
- package/dist/icons/CardIcon.d.ts +1 -1
- package/dist/icons/CashierIcon.d.ts +1 -1
- package/dist/icons/DuoIcon.d.ts +1 -1
- package/dist/icons/HomeIcon.d.ts +1 -1
- package/dist/icons/HomeIconOutlined.d.ts +1 -1
- package/dist/icons/LogoIcon.d.ts +1 -1
- package/dist/icons/MoneyReceiveIcon.d.ts +1 -1
- package/dist/icons/PinIcon.d.ts +1 -1
- package/dist/icons/PrinterIcon.d.ts +1 -1
- package/dist/icons/ProfileIcon.d.ts +1 -1
- package/dist/icons/SettingsIcon.d.ts +1 -1
- package/dist/icons/SinpeIcon.d.ts +1 -1
- package/dist/icons/ToggleOffIcon.d.ts +1 -1
- package/dist/icons/WalletMoneyIcon.d.ts +1 -1
- package/dist/index.d.ts +24 -23
- package/dist/index.js +26 -24
- package/dist/layouts/AuthContainer.d.ts +9 -3
- package/dist/layouts/AuthContainer.js +8 -8
- package/dist/layouts/Container.js +1 -1
- package/dist/layouts/GradientContainer.js +1 -1
- package/dist/layouts/ScrollContainer.js +1 -1
- package/dist/layouts/TabNavigatorLayout.js +4 -4
- package/dist/primitives/AspectRatio.js +1 -1
- package/dist/primitives/BaseButton.d.ts +92 -87
- package/dist/primitives/BaseButton.js +52 -54
- package/dist/primitives/Box.js +1 -1
- package/dist/primitives/Pressable.js +1 -1
- package/dist/primitives/Row.js +1 -1
- package/dist/primitives/Stack.js +1 -1
- package/dist/primitives/index.d.ts +6 -6
- package/dist/primitives/index.js +6 -6
- package/dist/theme/active.d.ts +1 -1
- package/dist/theme/active.js +14 -1
- package/dist/theme/build.d.ts +33 -0
- package/dist/theme/build.js +118 -0
- package/dist/theme/color.d.ts +26 -0
- package/dist/theme/color.js +109 -0
- package/dist/theme/components/index.d.ts +1 -1
- package/dist/theme/components/index.js +1 -1
- package/dist/theme/contrast.d.ts +29 -0
- package/dist/theme/contrast.js +57 -0
- package/dist/theme/css-vars.d.ts +3 -1
- package/dist/theme/css-vars.js +29 -3
- package/dist/theme/dark.d.ts +3 -4
- package/dist/theme/dark.js +91 -48
- package/dist/theme/default.d.ts +5 -1
- package/dist/theme/default.js +89 -41
- package/dist/theme/define.d.ts +4 -1
- package/dist/theme/define.js +4 -1
- package/dist/theme/globals.css +61 -46
- package/dist/theme/index.d.ts +14 -10
- package/dist/theme/index.js +13 -9
- package/dist/theme/theme.d.ts +1 -1
- package/dist/theme/theme.js +2 -2
- package/dist/theme/types.d.ts +77 -16
- package/dist/theme/types.js +13 -3
- package/dist/theme/typography.d.ts +19 -4
- package/dist/theme/typography.js +45 -28
- package/dist/theme/useTheme.d.ts +1 -1
- package/dist/theme/useTheme.js +1 -1
- package/dist/theme/validate.d.ts +16 -0
- package/dist/theme/validate.js +200 -0
- package/dist/tokens/index.d.ts +2 -2
- package/dist/tokens/index.js +2 -2
- package/dist/utils/criteria.d.ts +56 -0
- package/dist/utils/criteria.js +99 -0
- package/dist/utils/reorder.d.ts +4 -0
- package/dist/utils/reorder.js +32 -0
- package/dist/views/AppSplashView.d.ts +2 -2
- package/dist/views/AppSplashView.js +3 -3
- package/dist/views/AuthFormView.d.ts +5 -2
- package/dist/views/AuthFormView.js +5 -5
- package/dist/views/FormViewV2.d.ts +9 -4
- package/dist/views/FormViewV2.js +44 -25
- package/dist/views/ListViewV2.d.ts +12 -9
- package/dist/views/ListViewV2.js +49 -36
- package/dist/views/Wizard/Wizard.context.js +1 -1
- package/dist/views/Wizard/Wizard.d.ts +1 -1
- package/dist/views/Wizard/Wizard.js +1 -1
- package/dist/views/Wizard/index.d.ts +4 -4
- package/dist/views/Wizard/index.js +2 -2
- package/dist/views/types/IFormView.d.ts +1 -1
- package/dist/views/types/IListView.d.ts +1 -1
- package/dist/widgets/CircleButton.d.ts +2 -2
- package/dist/widgets/CircleButton.js +1 -1
- package/dist/widgets/CollectionOptions.d.ts +15 -0
- package/dist/widgets/CollectionOptions.js +127 -0
- package/dist/widgets/FilterBar.d.ts +21 -0
- package/dist/widgets/FilterBar.js +14 -0
- package/dist/widgets/FilterBuilder.d.ts +10 -0
- package/dist/widgets/FilterBuilder.js +115 -0
- package/dist/widgets/GeoPermissionCard.js +3 -3
- package/dist/widgets/HomeHeader.js +3 -3
- package/dist/widgets/JSONViewer.js +2 -2
- package/dist/widgets/MenuCard.d.ts +2 -2
- package/dist/widgets/MenuCard.js +1 -1
- package/dist/widgets/MenuGrid.d.ts +3 -3
- package/dist/widgets/MenuGrid.js +1 -1
- package/dist/widgets/MenuSection.d.ts +2 -2
- package/dist/widgets/MenuSection.js +1 -1
- package/dist/widgets/PromoBanner.js +3 -3
- package/dist/widgets/PromoModal.js +3 -3
- package/dist/widgets/QueryChoice.d.ts +10 -0
- package/dist/widgets/QueryChoice.js +13 -0
- package/dist/widgets/QuickActions.js +3 -3
- package/dist/widgets/ReorderableList.d.ts +51 -0
- package/dist/widgets/ReorderableList.js +138 -0
- package/dist/widgets/ScreenHeader.js +2 -2
- package/dist/widgets/TimeZoneSelector.d.ts +1 -1
- package/dist/widgets/TimeZoneSelector.js +4 -4
- package/dist/widgets/TimeZoneSelectorModal.d.ts +1 -1
- package/dist/widgets/TimeZoneSelectorModal.js +5 -5
- package/dist/widgets/index.d.ts +20 -16
- package/dist/widgets/index.js +13 -10
- package/package.json +70 -50
- package/tailwind.preset.js +19 -0
- package/dist/icons/icon_getters.d.ts +0 -6
- package/dist/icons/icon_getters.js +0 -23
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @sincpro/mobile-ui
|
|
2
2
|
|
|
3
|
-
**Sincpro Mobile Design System** — the UI contract between design and engineering for React Native / Expo SDK
|
|
3
|
+
**Sincpro Mobile Design System** — the UI contract between design and engineering for React Native / Expo SDK 58 applications.
|
|
4
4
|
|
|
5
5
|
This package is maintained by Sincpro as part of the mobile framework ecosystem. Anyone can read the source and use the components. What Sincpro provides is the commitment behind it: stable APIs, predictable behavior, long-term support, and a team that ensures this keeps working as platforms evolve. Build on it knowing it will still be there.
|
|
6
6
|
|
|
@@ -20,17 +20,14 @@ Wire the Tailwind preset, then import by category:
|
|
|
20
20
|
// tailwind.config.js
|
|
21
21
|
module.exports = {
|
|
22
22
|
presets: [require("@sincpro/mobile-ui/tailwind.preset")],
|
|
23
|
-
content: [
|
|
24
|
-
"./App.tsx",
|
|
25
|
-
"./src/**/*.{ts,tsx}",
|
|
26
|
-
"./node_modules/@sincpro/mobile-ui/**/*.js",
|
|
27
|
-
],
|
|
23
|
+
content: ["./App.tsx", "./src/**/*.{ts,tsx}", "./node_modules/@sincpro/mobile-ui/**/*.js"],
|
|
28
24
|
};
|
|
29
25
|
```
|
|
30
26
|
|
|
31
27
|
```tsx
|
|
28
|
+
import { Actions } from "@sincpro/mobile-ui/Actions";
|
|
32
29
|
import { Display } from "@sincpro/mobile-ui/Display";
|
|
33
|
-
import { Form }
|
|
30
|
+
import { Form } from "@sincpro/mobile-ui/Form";
|
|
34
31
|
import { useToast, ToastProvider } from "@sincpro/mobile-ui/Feedback";
|
|
35
32
|
|
|
36
33
|
export default function InvoiceScreen() {
|
|
@@ -38,10 +35,12 @@ export default function InvoiceScreen() {
|
|
|
38
35
|
return (
|
|
39
36
|
<Display.Card>
|
|
40
37
|
<Display.KeyValue label="Invoice #" value="INV-0042" copyable />
|
|
41
|
-
<Display.Monetary
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
<Display.Monetary value={1500.0} variant="dataLarge" />
|
|
39
|
+
<Actions.Button
|
|
40
|
+
accessibleSizing
|
|
41
|
+
onPress={() => toast.success("Invoice sent")}
|
|
42
|
+
title="Send Invoice"
|
|
43
|
+
/>
|
|
45
44
|
</Display.Card>
|
|
46
45
|
);
|
|
47
46
|
}
|
|
@@ -80,6 +79,338 @@ Without a design system, every developer makes local decisions: which shade of g
|
|
|
80
79
|
|
|
81
80
|
## Philosophy
|
|
82
81
|
|
|
82
|
+
**Composable behavior, customizable presentation.** Sincpro defaults are a starting
|
|
83
|
+
point, not a mandatory visual style. Preserve existing appearances while exposing public
|
|
84
|
+
APIs for fonts, sizes, shapes, content and composition. See the
|
|
85
|
+
[permanent design-system manifesto](docs/MANIFESTO.md) for the goal, acceptance criteria
|
|
86
|
+
and current limitations; full customizability is an objective, not a completed audit.
|
|
87
|
+
|
|
88
|
+
The [UX/UI evolution plan](docs/UX-UI-PLAN.md) tracks the rollout. The first native pilot
|
|
89
|
+
is in Storybook under `Patterns/Records/Detail`: list, detail, copy, edit, cancel and
|
|
90
|
+
recoverable error. It uses fictional data and local state, not persistence or a backend.
|
|
91
|
+
|
|
92
|
+
### Accessible interaction
|
|
93
|
+
|
|
94
|
+
- `Actions.Button` (including `.Icon`), `Actions.IconButton`, `BaseButton`,
|
|
95
|
+
`Feedback.EmptyState`, `Feedback.Error` and `Form.Input` accept `accessibleSizing` to
|
|
96
|
+
opt into a minimum 48-point target. Buttons also allow enlarged, multiline labels in
|
|
97
|
+
this mode. Existing sizing remains the default for compatibility.
|
|
98
|
+
- Supply `accessibilityLabel` for icon-only actions. Loading buttons expose busy/disabled
|
|
99
|
+
state and retain their accessible name.
|
|
100
|
+
- `Display.CopyableText` uses a familiar copy icon: tap a short value to copy it.
|
|
101
|
+
A transient checkmark confirms success without adding a “Copiado” block or shifting
|
|
102
|
+
layout; screen readers receive an announcement. Use `selectable` for longer text:
|
|
103
|
+
native text selection stays independent of the 48-point icon action that copies the
|
|
104
|
+
full value. Failures stay explicit and retryable. It accepts `disabled`, `testID`,
|
|
105
|
+
`accessibilityLabel`, `onCopied` and `onCopyError`. Copy targets intentionally reserve 48 points.
|
|
106
|
+
- `Display.KeyValue copyable` enables copying string/number values (enforced by its type).
|
|
107
|
+
Use `align="stacked"` for long content or `align="auto"` to stack below 360 points or
|
|
108
|
+
above a 1.3 font scale. Existing `align="row"` remains unchanged; React nodes are
|
|
109
|
+
display-only.
|
|
110
|
+
- `KeyValue selectable` also supports native text selection, with or without copying.
|
|
111
|
+
Choose the mode by the purpose of the field, not an automatic character-count threshold.
|
|
112
|
+
- `Typography.SelectableText` provides read-only native range selection without opening
|
|
113
|
+
the keyboard. It preserves typography/tokens and backs the longer-text modes above.
|
|
114
|
+
On iOS, `Text selectable` alone only exposes full-paragraph copying, not range handles.
|
|
115
|
+
- Headings and section titles expose native header semantics.
|
|
116
|
+
- Checkbox/Select announce control state; their `accessibleSizing` mode enlarges targets.
|
|
117
|
+
- Sheet supports accessible escape and `showCloseButton`; `SwipeableRow visibleActions`
|
|
118
|
+
supplies a non-gesture alternative. Keep explicit labels for essential actions.
|
|
119
|
+
- `Dialog/NativeSheet` is an optional Expo 58 native presentation (`@expo/ui` peer);
|
|
120
|
+
import it by subpath, not the root barrel. Use short action sheets for choices and a
|
|
121
|
+
sheet for a scoped task; do not stack sheets. Mobile-task stories demonstrate native
|
|
122
|
+
summaries, cancelable selection, filters applied only on confirmation and archive/undo.
|
|
123
|
+
- Form/List view refresh always clears the busy state on failure and accepts
|
|
124
|
+
`onRefreshError` for application recovery without discarding content.
|
|
125
|
+
- AppBar, BottomNav and SegmentedControl support accessible sizing; banners expose status
|
|
126
|
+
semantics, and confirmation defaults use explicit verbs.
|
|
127
|
+
|
|
128
|
+
The [audit and validation report](docs/UX-AUDIT.md) records scope, evidence and device limits.
|
|
129
|
+
|
|
130
|
+
### Clean Sincpro grouping and customization
|
|
131
|
+
|
|
132
|
+
`Display.ListGroup` groups related settings or a short list on one surface, with a title,
|
|
133
|
+
optional description/footer/action and hairline separators only between rows. Choose
|
|
134
|
+
`surface="plain"` for grouping by proximity instead of a rounded container.
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
<Display.ListGroup
|
|
138
|
+
title="Cuenta"
|
|
139
|
+
footer="Puedes cambiar estas preferencias en cualquier momento."
|
|
140
|
+
contentClassName="rounded-lg bg-bg-muted"
|
|
141
|
+
>
|
|
142
|
+
<Display.ListItem
|
|
143
|
+
variant="mobile"
|
|
144
|
+
title="Idioma"
|
|
145
|
+
value="Español"
|
|
146
|
+
chevron
|
|
147
|
+
onPress={openLanguageChoices}
|
|
148
|
+
/>
|
|
149
|
+
<Display.ListItem
|
|
150
|
+
variant="mobile"
|
|
151
|
+
title="Avisos importantes"
|
|
152
|
+
trailing={notificationControl}
|
|
153
|
+
/>
|
|
154
|
+
</Display.ListGroup>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- `ListItem variant="mobile"` opts into wrapping, a 48-point target,
|
|
158
|
+
tonal row press feedback and adaptive accessories. `layout="auto"` stacks values/controls
|
|
159
|
+
below the text on narrow screens or enlarged text; use `layout="stacked"` for narrow
|
|
160
|
+
embedded containers. Navigation values are also exposed as native accessibility values.
|
|
161
|
+
- New mobile rows default to `density="compact"`: regular 14-point titles/values,
|
|
162
|
+
13-point supporting text and 8-point vertical padding. `density="comfortable"` restores
|
|
163
|
+
the earlier PR's 17/16-point reading presentation and 12-point padding.
|
|
164
|
+
Legacy rows (`variant="default"`) keep their original appearance.
|
|
165
|
+
- Put a toggle/checkbox in a **non-pressable** row; do not nest a second action inside a
|
|
166
|
+
pressable row. The leading/trailing slots remain consumer-owned.
|
|
167
|
+
- `Typography.Text variant="bodyReadable"` and `Typography.SelectableText` with the same
|
|
168
|
+
variant opt into 17 points while retaining `configureFonts` and system font scaling.
|
|
169
|
+
The 14-point `body` and existing ListItem appearance remain unchanged.
|
|
170
|
+
- Customize per instance with `className`, `contentClassName`, slots, surface and separators.
|
|
171
|
+
Rows also accept `style`, `titleTextProps`, `subtitleTextProps` and `valueTextProps`.
|
|
172
|
+
Text props accept typography variants, weights, font families and native style arrays.
|
|
173
|
+
`ListGroup.headerProps` forwards header styling/text props; `footerTextProps` controls
|
|
174
|
+
string footers. Default groups use a 16-point section heading and 13-point description/footer.
|
|
175
|
+
Group `style`, `contentStyle` and `separatorStyle` control spacing, surface and dividers.
|
|
176
|
+
Headers expose `style`, `contentStyle`, `actionStyle` and `actionTextProps` as well.
|
|
177
|
+
For example, `titleTextProps={{ style: { fontFamily: "MyLoadedFont", fontSize: 15 } }}`
|
|
178
|
+
changes only one row, without copying its focus, accessibility or press behavior.
|
|
179
|
+
Customize branding centrally with `buildThemeFromBrand`/`extendTheme`, `setActiveTheme`
|
|
180
|
+
and `themeToVars` on the host, plus `configureFonts` for already-loaded font families.
|
|
181
|
+
Do not introduce a competing palette or hardcode colors in every screen.
|
|
182
|
+
- This group is not virtualized: use FlatList/SectionList for large or unbounded collections.
|
|
183
|
+
|
|
184
|
+
Stories: `Patterns/Forms/Settings` shows Sincpro defaults, long content, customized
|
|
185
|
+
surfaces and text roles. `Foundations/Typography/CompactHierarchy` and
|
|
186
|
+
`ComfortableHierarchy` show the same content at two densities, not two mandatory screens.
|
|
187
|
+
Customization is not a complete visual approval.
|
|
188
|
+
|
|
189
|
+
### Compose collections, not a prescribed screen
|
|
190
|
+
|
|
191
|
+
`Patterns/Collections/Browsing` shows the same local data as rows, cards and
|
|
192
|
+
long-label variants. It combines icons, bounded 56-point artwork, readable text, metrics,
|
|
193
|
+
independent open/save actions and a native detail sheet. These are recipes to adapt,
|
|
194
|
+
not new domain-specific screen components or a web toolbar squeezed onto a phone.
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
import { FilterBar } from "@sincpro/mobile-ui/widgets";
|
|
198
|
+
|
|
199
|
+
<FilterBar
|
|
200
|
+
filters={filterOptions}
|
|
201
|
+
onFilterPress={toggleFilter}
|
|
202
|
+
onClearFilters={clearFilters}
|
|
203
|
+
onOpenFilters={openFilterSheet}
|
|
204
|
+
resultsLabel={resultsLabel}
|
|
205
|
+
search={{
|
|
206
|
+
value: query,
|
|
207
|
+
onChangeText: setQuery,
|
|
208
|
+
accessibilityLabel: "Buscar recursos",
|
|
209
|
+
clearAccessibilityLabel: "Limpiar búsqueda",
|
|
210
|
+
}}
|
|
211
|
+
/>;
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Each filter supplies `id`, `label`, `selected` and optional `disabled`. State, matching,
|
|
215
|
+
result counts and advanced-filter drafts belong to the host. Clearing filters does not
|
|
216
|
+
silently clear the search query. Search, advanced action, result label and clear action
|
|
217
|
+
are optional; customize labels, `className`, theme and `layout="scroll|wrap|auto"`.
|
|
218
|
+
Auto wraps above font scale 1.3, otherwise it scrolls horizontally. Quick chips apply
|
|
219
|
+
immediately; the recipe's advanced sheet applies only on confirmation.
|
|
220
|
+
|
|
221
|
+
`Actions.FilterButton variant="mobile"` adds readable wrapping labels and 48-point targets.
|
|
222
|
+
`FilterButtonGroup layout="scroll|wrap"` and `Navigation.SearchBar accessibleSizing` are
|
|
223
|
+
additive; legacy filter layout and search sizing remain unchanged.
|
|
224
|
+
When placing these controls in a host ScrollView, use `keyboardShouldPersistTaps="handled"`
|
|
225
|
+
so a filter can act on the first tap while the keyboard is open.
|
|
226
|
+
Use virtualized lists for production-sized collections.
|
|
227
|
+
|
|
228
|
+
Dependencies follow the latest stable version compatible with Expo 58, not unbounded
|
|
229
|
+
upgrades. Validate with `CI=1 npx expo install --check` in addition to the quality gates.
|
|
230
|
+
|
|
231
|
+
### Controlled reordering without a closed row design
|
|
232
|
+
|
|
233
|
+
Optional import: `@sincpro/mobile-ui/widgets/ReorderableList`. Install
|
|
234
|
+
`react-native-sortables@^1.10.1` in the host; it uses the host's Gesture Handler 3.3+,
|
|
235
|
+
Reanimated 4.7+ and Worklets setup. Wrap the app in `GestureHandlerRootView`.
|
|
236
|
+
The adapter is deliberately **not** in the generic widgets/root barrels: applications
|
|
237
|
+
that do not use drag-and-drop do not need to resolve this optional peer.
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import ReorderableList from "@sincpro/mobile-ui/widgets/ReorderableList";
|
|
241
|
+
|
|
242
|
+
<ReorderableList
|
|
243
|
+
items={sections}
|
|
244
|
+
keyExtractor={(item) => item.id}
|
|
245
|
+
getItemLabel={(item) => item.name}
|
|
246
|
+
onChange={(next) => setSections(next)}
|
|
247
|
+
renderItem={({ item }) => <MySectionContent section={item} />}
|
|
248
|
+
itemStyle={{ borderRadius: 16, padding: 8 }}
|
|
249
|
+
/>;
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- A one-column, **non-virtualized short-list** adapter with variable-height content.
|
|
253
|
+
It does not own screen scrolling, persistence, confirmation or domain rules.
|
|
254
|
+
Do not use it as an unbounded feed, kanban board, file drop zone or cross-app drag API.
|
|
255
|
+
- Only the dedicated handle starts drag. Default arrows and the handle's native
|
|
256
|
+
increment/decrement actions offer alternatives; content can retain buttons, inputs
|
|
257
|
+
or any custom layout. Drag/drop uses insertion, not swapping.
|
|
258
|
+
- `onChange(next, { key, fromIndex, toIndex, source })` delivers an immutable order
|
|
259
|
+
(`source` is `drag` or `button`). Update controlled state synchronously; persist later,
|
|
260
|
+
showing save failures/retry in the host. A drop in the original position emits no change.
|
|
261
|
+
Draft/apply/cancel/undo can be built by owning that state, without replacing the gesture.
|
|
262
|
+
- Keys must be unique non-empty strings, stable across renders, never array indices.
|
|
263
|
+
Content updates with the same keys preserve current objects. Insert/remove/external
|
|
264
|
+
reorder or disabling **during** drag cancels the gesture, retains host data and shows
|
|
265
|
+
an accessible error. `onReorderRejected` reports `data-changed` or `disabled`.
|
|
266
|
+
The gesture engine remounts on rejection; keep persistent item state in the host.
|
|
267
|
+
- `renderItem` controls content. `renderControls` receives `dragHandle`, movement
|
|
268
|
+
callbacks and boundary flags; keep that handle and an accessible non-drag alternative
|
|
269
|
+
when replacing controls. `renderHandleContent` replaces **noninteractive** handle
|
|
270
|
+
contents only, keeping the gesture, accessible name and adjustable actions.
|
|
271
|
+
- Root `className`/`style`, `itemStyle`, `contentStyle`, `controlsStyle`, `handleStyle`
|
|
272
|
+
and `actionStyle` accept local layout/shape/spacing. Child content owns typography.
|
|
273
|
+
`labels` localizes controls, announcements and conflict errors.
|
|
274
|
+
- `sortableProps` forwards engine options such as activation delay, row gap, decorations,
|
|
275
|
+
animation settings and autoscroll, while the adapter owns identity, single-column
|
|
276
|
+
insertion and controlled callbacks. Provide a stable Reanimated `scrollableRef` to
|
|
277
|
+
the **actual Animated.ScrollView** for autoscroll; do not nest another scroll container
|
|
278
|
+
or attach the ref to a different view. Native scroll props/ref belong to the host.
|
|
279
|
+
Custom motion, targets and colors still require accessibility validation.
|
|
280
|
+
|
|
281
|
+
`Patterns/Collections/Reordering` demonstrates default, custom content/controls, long
|
|
282
|
+
rows and an external-update conflict. Only this recipe uses the new dependency;
|
|
283
|
+
existing form/list/query templates keep their behavior.
|
|
284
|
+
|
|
285
|
+
### Keyboard-aware forms and field navigation
|
|
286
|
+
|
|
287
|
+
Expo 58 does not automatically make arbitrary form layouts keyboard-safe.
|
|
288
|
+
The host must include `react-native-keyboard-controller` and its `KeyboardProvider` at
|
|
289
|
+
the app root. Use a development build when the runtime does not include that native module.
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
<Form.KeyboardForm contentContainerStyle={{ padding: 16, gap: 16 }}>
|
|
293
|
+
{fields}
|
|
294
|
+
</Form.KeyboardForm>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`KeyboardForm` owns the scroll and scopes editable inputs with `KeyboardToolbar.Group`.
|
|
298
|
+
Its toolbar provides previous/next/dismiss actions, including phone/number keyboards.
|
|
299
|
+
The scroll reserves the measured toolbar height plus 24 points (normally 72 total);
|
|
300
|
+
it grows with enlarged text. Override `bottomOffset` for additional accessories.
|
|
301
|
+
`toolbar={false}` keeps automatic avoidance without the toolbar.
|
|
302
|
+
Customize `toolbarLabels` or compose `Form.KeyboardToolbar` with an existing aware scroll,
|
|
303
|
+
using `onHeightChange` to reserve its measured height. The toolbar compensates for space
|
|
304
|
+
below its parent, such as a safe area or form footer, instead of floating over the caret.
|
|
305
|
+
The default toolbar uses compact icons; `toolbarProps={{ showDoneLabel: true }}` opts into
|
|
306
|
+
a text dismiss action. `toolbarProps` also accepts `className`, `style`, `actionStyle`,
|
|
307
|
+
`labelStyle`, `iconSize` and `renderActionContent(action, { label, disabled })`.
|
|
308
|
+
The content slot replaces only non-interactive content: the toolbar retains focus actions,
|
|
309
|
+
accessible labels and disabled states. `toolbarLabels` remains supported;
|
|
310
|
+
`toolbarProps` takes precedence. Font size/family and shape can be changed without
|
|
311
|
+
reimplementing keyboard behavior.
|
|
312
|
+
Do not nest this component inside another vertical ScrollView.
|
|
313
|
+
|
|
314
|
+
For existing templates, opt into `FormViewV2.Content keyboardToolbar` or
|
|
315
|
+
`AuthFormView keyboardToolbar` / `AuthContainer keyboardToolbar`; defaults stay unchanged.
|
|
316
|
+
`Form.Input` forwards its native ref for explicit return-key navigation:
|
|
317
|
+
`onSubmitEditing={() => nextRef.current?.focus()}` with `submitBehavior="submit"`.
|
|
318
|
+
`KeyboardForm` forwards its ref to the native aware scroll for methods such as
|
|
319
|
+
`assureFocusedInputVisible()` after validation/layout changes.
|
|
320
|
+
The three templates accept `keyboardFormProps` (excluding `children` and `toolbar`) for
|
|
321
|
+
the same ref, offsets, toolbar customization, scroll callbacks and content styling.
|
|
322
|
+
`keyboardToolbar` remains the explicit opt-in; omitting it preserves the original
|
|
323
|
+
template layout without a toolbar.
|
|
324
|
+
Multiline Return remains a newline; dismissing the keyboard does not submit the form.
|
|
325
|
+
Story: `Patterns/Forms/Keyboard navigation`.
|
|
326
|
+
|
|
327
|
+
`ListViewV2.Content` accepts native `keyboardShouldPersistTaps` and `keyboardDismissMode`
|
|
328
|
+
across populated, empty and error states. Handled row/retry actions receive the first tap
|
|
329
|
+
while search has focus (`"handled"` by default); pass `"never"` for the old dismiss-first
|
|
330
|
+
behavior. Drag-dismiss remains opt-in (`keyboardDismissMode="on-drag"`).
|
|
331
|
+
This changes interaction, not the list's typography or layout.
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
<FormViewV2.Content
|
|
335
|
+
keyboardToolbar
|
|
336
|
+
keyboardFormProps={{
|
|
337
|
+
ref: scrollRef,
|
|
338
|
+
toolbarProps: {
|
|
339
|
+
style: { borderRadius: 16 },
|
|
340
|
+
iconSize: 18,
|
|
341
|
+
showDoneLabel: true,
|
|
342
|
+
labelStyle: { fontFamily: "MyFont", fontSize: 13 },
|
|
343
|
+
},
|
|
344
|
+
}}
|
|
345
|
+
>
|
|
346
|
+
{fields}
|
|
347
|
+
</FormViewV2.Content>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Default list rows now honor the template's `onPressItem` and `readonly` settings;
|
|
351
|
+
direct `Navigation.RowItem` usage can still supply the legacy callback on `item`.
|
|
352
|
+
|
|
353
|
+
### Structured queries, not just quick chips
|
|
354
|
+
|
|
355
|
+
`widgets.FilterBuilder` edits a controlled `CriteriaGroup`: nested `all`/`any`/`none`
|
|
356
|
+
groups and configured conditions. Supply fields with their label, type, permitted
|
|
357
|
+
operators and arity (`none`, `one`, `range`); choice values are explicit. Numbers remain
|
|
358
|
+
editable strings until validated/committed, date fields use the native picker, and invalid
|
|
359
|
+
conditions produce visible errors. The mobile editor opens one group at a time instead of
|
|
360
|
+
squeezing an arbitrarily indented desktop tree into the screen.
|
|
361
|
+
|
|
362
|
+
`widgets.CollectionOptions` is a full-screen, keyboard-aware workspace:
|
|
363
|
+
|
|
364
|
+
```tsx
|
|
365
|
+
<CollectionOptions
|
|
366
|
+
visible={open}
|
|
367
|
+
value={query}
|
|
368
|
+
fields={filterFields}
|
|
369
|
+
sortFields={sortFields}
|
|
370
|
+
groupFields={groupFields}
|
|
371
|
+
savedQueries={savedQueries}
|
|
372
|
+
onApply={applyQuery}
|
|
373
|
+
onClose={closeOptions}
|
|
374
|
+
onSave={saveQuery}
|
|
375
|
+
/>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
`CollectionQuery` contains `search`, `filter`, ordered `sort` entries and ordered
|
|
379
|
+
`groupBy` field IDs. Order and grouping priorities have explicit move/remove actions;
|
|
380
|
+
dragging is not required. Loading a saved configuration changes the draft only;
|
|
381
|
+
Apply commits all dimensions together, Cancel discards the draft. Save errors keep the
|
|
382
|
+
draft/name for retry. The host owns saving/loading and closes the workspace after Apply.
|
|
383
|
+
|
|
384
|
+
The DS does not fetch models, evaluate filters, sort a page as if it were the full dataset,
|
|
385
|
+
persist favorites or translate this tree into a backend query language. Relational paths,
|
|
386
|
+
remote model discovery and a JSON editor are not implemented here. The web builder is a
|
|
387
|
+
semantic reference, not a component copied into mobile.
|
|
388
|
+
|
|
389
|
+
`Navigation.SearchBar suggestions={{ items, onSelect, loading, error, onRetry }}` shows
|
|
390
|
+
host-supplied autocomplete while focused. Choosing a suggestion calls `onSelect` without
|
|
391
|
+
silently rewriting the query. A host may complete a title or create a field-specific
|
|
392
|
+
condition; it owns matching, request cancellation and stale responses.
|
|
393
|
+
|
|
394
|
+
Story: `Patterns/Collections/Query configuration` exercises actual local filtering,
|
|
395
|
+
multiple sorting priorities, grouping levels and saving/loading configurations.
|
|
396
|
+
|
|
397
|
+
### Native E2E
|
|
398
|
+
|
|
399
|
+
With Maestro installed and an iOS simulator running Expo Go, start an isolated test server:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
EXPO_PUBLIC_STORYBOOK_TEST=1 npx expo start --port 8082
|
|
403
|
+
make storybook-e2e
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
This disables Storybook's on-device chrome for tests without changing the normal server.
|
|
407
|
+
The flows verify copy, edit, cancel, failed-save recovery and long-content/dark interaction.
|
|
408
|
+
`STORYBOOK_URL` can override the default `exp://127.0.0.1:8082`.
|
|
409
|
+
Maestro analytics are disabled by the Makefile target. These flows target Expo Go on iOS;
|
|
410
|
+
Android needs a connected device and its appropriate app ID/host configuration.
|
|
411
|
+
Tests stop/relaunch Expo Go and switch stories; run them on a dedicated test simulator.
|
|
412
|
+
Enlarged-text screenshots are accessibility stress tests, not the default typography.
|
|
413
|
+
|
|
83
414
|
### Maintained for the long term
|
|
84
415
|
|
|
85
416
|
Enterprise software lives for years. The screens built with this DS will be in production long after the initial project is delivered. Sincpro's commitment is to maintain API stability, patch what breaks on platform upgrades, and evolve the system without forcing rewrites on consumers.
|
|
@@ -133,7 +464,7 @@ Queued toast system with promise support — covers the full loading → success
|
|
|
133
464
|
import { ToastProvider, useToast } from "@sincpro/mobile-ui/Feedback";
|
|
134
465
|
|
|
135
466
|
// 1. Wrap the app once (createAppShell does this automatically)
|
|
136
|
-
<ToastProvider position="top">{children}</ToastProvider
|
|
467
|
+
<ToastProvider position="top">{children}</ToastProvider>;
|
|
137
468
|
|
|
138
469
|
// 2. Call from anywhere in the tree
|
|
139
470
|
const toast = useToast();
|
|
@@ -141,7 +472,7 @@ toast.success("Invoice saved");
|
|
|
141
472
|
toast.promise(sendInvoice(), {
|
|
142
473
|
loading: "Sending…",
|
|
143
474
|
success: "Invoice sent",
|
|
144
|
-
error:
|
|
475
|
+
error: "Failed to send",
|
|
145
476
|
});
|
|
146
477
|
```
|
|
147
478
|
|
|
@@ -153,8 +484,8 @@ import { ConfirmationProvider, useConfirmationContext } from "@sincpro/mobile-ui
|
|
|
153
484
|
|
|
154
485
|
// Controlled sheet — built on RN Modal, no native peer deps
|
|
155
486
|
<Sheet visible={open} onClose={() => setOpen(false)} title="Options">
|
|
156
|
-
<
|
|
157
|
-
</Sheet
|
|
487
|
+
<Actions.Button onPress={confirm}>Confirm</Actions.Button>
|
|
488
|
+
</Sheet>;
|
|
158
489
|
|
|
159
490
|
// Imperative confirmation
|
|
160
491
|
const { confirm } = useConfirmationContext();
|
|
@@ -167,10 +498,16 @@ const agreed = await confirm({ title: "Delete invoice?", message: "This cannot b
|
|
|
167
498
|
import { Wizard, useWizard } from "@sincpro/mobile-ui/views/Wizard";
|
|
168
499
|
|
|
169
500
|
<Wizard initialStep="customer">
|
|
170
|
-
<Wizard.Step id="customer"
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
501
|
+
<Wizard.Step id="customer">
|
|
502
|
+
<CustomerStep />
|
|
503
|
+
</Wizard.Step>
|
|
504
|
+
<Wizard.Step id="items">
|
|
505
|
+
<ItemsStep />
|
|
506
|
+
</Wizard.Step>
|
|
507
|
+
<Wizard.Step id="review">
|
|
508
|
+
<ReviewStep />
|
|
509
|
+
</Wizard.Step>
|
|
510
|
+
</Wizard>;
|
|
174
511
|
|
|
175
512
|
// Inside any step
|
|
176
513
|
const { go, back } = useWizard();
|
|
@@ -196,162 +533,163 @@ import { Display } from "@sincpro/mobile-ui/Display";
|
|
|
196
533
|
|
|
197
534
|
### Actions
|
|
198
535
|
|
|
199
|
-
| Component
|
|
200
|
-
|
|
201
|
-
| `Actions.Button`
|
|
202
|
-
| `Actions.IconButton`
|
|
203
|
-
| `Actions.FAB`
|
|
204
|
-
| `Actions.FilterButton`
|
|
205
|
-
| `Actions.FilterButtonGroup` | Horizontal scroll container for filter chips
|
|
536
|
+
| Component | Description |
|
|
537
|
+
| --------------------------- | -------------------------------------------------------------------------------- |
|
|
538
|
+
| `Actions.Button` | Primary action — gradient, outline, flat, link variants; optional confirm dialog |
|
|
539
|
+
| `Actions.IconButton` | Icon-only tap target — ghost, primary, secondary, tertiary variants |
|
|
540
|
+
| `Actions.FAB` | Floating Action Button — fixed position, optional label |
|
|
541
|
+
| `Actions.FilterButton` | Chip-style active/inactive toggle |
|
|
542
|
+
| `Actions.FilterButtonGroup` | Horizontal scroll container for filter chips |
|
|
206
543
|
|
|
207
544
|
### Display
|
|
208
545
|
|
|
209
|
-
| Component
|
|
210
|
-
|
|
211
|
-
| `Display.Badge`
|
|
212
|
-
| `Display.Card`
|
|
213
|
-
| `Display.KeyValue`
|
|
214
|
-
| `Display.Stat`
|
|
215
|
-
| `Display.StatCard`
|
|
216
|
-
| `Display.Monetary`
|
|
217
|
-
| `Display.PriceTag`
|
|
218
|
-
| `Display.DataTable`
|
|
219
|
-
| `Display.ListItem`
|
|
220
|
-
| `Display.Chip`
|
|
221
|
-
| `Display.Avatar` / `AvatarGroup`
|
|
222
|
-
| `Display.Icon`
|
|
223
|
-
| `Display.StatusDot`
|
|
224
|
-
| `Display.Divider`
|
|
225
|
-
| `Display.GradientSurface`
|
|
226
|
-
| `Display.Timeline`
|
|
227
|
-
| `Display.SwipeableRow`
|
|
228
|
-
| `Display.Tooltip`
|
|
229
|
-
| `Display.CopyableText`
|
|
230
|
-
| `Display.Logo`
|
|
231
|
-
| `Display.Accordion`
|
|
232
|
-
| `Display.Carousel`
|
|
233
|
-
| `Display.Story` / `StoryTray` / `StoryViewer` | Stories UI pattern
|
|
234
|
-
| `Display.MediaCard`
|
|
235
|
-
| `Display.Pattern`
|
|
546
|
+
| Component | Description |
|
|
547
|
+
| --------------------------------------------- | ------------------------------------------------------ |
|
|
548
|
+
| `Display.Badge` | Status badge — success, warning, danger, info, neutral |
|
|
549
|
+
| `Display.Card` | Surface container with optional shadow and padding |
|
|
550
|
+
| `Display.KeyValue` | Label + value row, optional clipboard copy |
|
|
551
|
+
| `Display.Stat` | KPI cell — label, value, optional trend indicator |
|
|
552
|
+
| `Display.StatCard` | Card-wrapped KPI |
|
|
553
|
+
| `Display.Monetary` | Formatted monetary amount using mono typography |
|
|
554
|
+
| `Display.PriceTag` | Currency-formatted price with size options |
|
|
555
|
+
| `Display.DataTable` | Scrollable table for structured data |
|
|
556
|
+
| `Display.ListItem` | Row with icon, label, sublabel, optional action |
|
|
557
|
+
| `Display.Chip` | Compact label tag |
|
|
558
|
+
| `Display.Avatar` / `AvatarGroup` | User avatar, stacked group |
|
|
559
|
+
| `Display.Icon` | Vector icon wrapper (`@expo/vector-icons`) |
|
|
560
|
+
| `Display.StatusDot` | Colored presence / state indicator |
|
|
561
|
+
| `Display.Divider` | Horizontal separator |
|
|
562
|
+
| `Display.GradientSurface` | Container with linear gradient background |
|
|
563
|
+
| `Display.Timeline` | Vertical event timeline |
|
|
564
|
+
| `Display.SwipeableRow` | Row with swipe-to-reveal actions |
|
|
565
|
+
| `Display.Tooltip` | Overlay tooltip |
|
|
566
|
+
| `Display.CopyableText` | Text with one-tap clipboard copy |
|
|
567
|
+
| `Display.Logo` | Brand logo (falls back to `setBranding` value) |
|
|
568
|
+
| `Display.Accordion` | Collapsible content section |
|
|
569
|
+
| `Display.Carousel` | Horizontal swipe carousel |
|
|
570
|
+
| `Display.Story` / `StoryTray` / `StoryViewer` | Stories UI pattern |
|
|
571
|
+
| `Display.MediaCard` | Card with image or video embed |
|
|
572
|
+
| `Display.Pattern` | Decorative background pattern |
|
|
236
573
|
|
|
237
574
|
### Form
|
|
238
575
|
|
|
239
|
-
| Component
|
|
240
|
-
|
|
241
|
-
| `Form.TextInput`
|
|
242
|
-
| `Form.PasswordInput`
|
|
243
|
-
| `Form.PinInput`
|
|
244
|
-
| `Form.MonetaryInput`
|
|
245
|
-
| `Form.QuantitySelector`
|
|
246
|
-
| `Form.FractionalQuantityInput`
|
|
247
|
-
| `Form.Select`
|
|
248
|
-
| `Form.Dropdown`
|
|
249
|
-
| `Form.RadioButton`
|
|
250
|
-
| `Form.Checkbox`
|
|
251
|
-
| `Form.Switch`
|
|
252
|
-
| `Form.Slider`
|
|
253
|
-
| `Form.DatePicker`
|
|
254
|
-
| `Form.SearchBar`
|
|
255
|
-
| `Form.ControlledInput`
|
|
256
|
-
| `Form.ControlledRadioButton`
|
|
257
|
-
| `Form.ControlledCheckbox`
|
|
258
|
-
| `Form.ControlledDropdown`
|
|
259
|
-
| `Form.ControlledMonetaryInput`
|
|
260
|
-
| `
|
|
576
|
+
| Component | Description |
|
|
577
|
+
| --------------------------------------- | ------------------------------------------------------------------- |
|
|
578
|
+
| `Form.TextInput` | Text field with label, error, and helper text |
|
|
579
|
+
| `Form.PasswordInput` | Text input with show/hide toggle |
|
|
580
|
+
| `Form.PinInput` | PIN code entry |
|
|
581
|
+
| `Form.MonetaryInput` | Currency input with formatting |
|
|
582
|
+
| `Form.QuantitySelector` | Integer increment / decrement |
|
|
583
|
+
| `Form.FractionalQuantityInput` | Decimal quantity input |
|
|
584
|
+
| `Form.Select` | Dropdown select (modal-based) |
|
|
585
|
+
| `Form.Dropdown` | Inline dropdown menu |
|
|
586
|
+
| `Form.RadioButton` | Single-select radio option |
|
|
587
|
+
| `Form.Checkbox` | Multi-select checkbox |
|
|
588
|
+
| `Form.Switch` | Toggle switch |
|
|
589
|
+
| `Form.Slider` | Range slider (`@react-native-community/slider` peer) |
|
|
590
|
+
| `Form.DatePicker` | Date picker (`@react-native-community/datetimepicker` peer) |
|
|
591
|
+
| `Form.SearchBar` | Search input with clear button |
|
|
592
|
+
| `Form.ControlledInput` | `TextInput` + react-hook-form |
|
|
593
|
+
| `Form.ControlledRadioButton` | `RadioButton` + react-hook-form |
|
|
594
|
+
| `Form.ControlledCheckbox` | `Checkbox` + react-hook-form |
|
|
595
|
+
| `Form.ControlledDropdown` | `Dropdown` + react-hook-form |
|
|
596
|
+
| `Form.ControlledMonetaryInput` | `MonetaryInput` + react-hook-form |
|
|
597
|
+
| `Actions.Button` / `IconButton` / `FAB` | Action components (preferred over deprecated `Form.Button` aliases) |
|
|
261
598
|
|
|
262
599
|
### Feedback
|
|
263
600
|
|
|
264
|
-
| Component
|
|
265
|
-
|
|
266
|
-
| `Feedback.Spinner`
|
|
267
|
-
| `Feedback.Loading`
|
|
268
|
-
| `Feedback.Skeleton`
|
|
269
|
-
| `Feedback.Progress`
|
|
270
|
-
| `Feedback.ProgressCircle`
|
|
271
|
-
| `Feedback.Banner`
|
|
272
|
-
| `Feedback.Empty` / `EmptyState` | Empty list or page placeholder
|
|
273
|
-
| `Feedback.Error`
|
|
274
|
-
| `Feedback.PermissionCard`
|
|
275
|
-
| `ToastProvider` / `useToast`
|
|
276
|
-
| `ErrorBoundary`
|
|
601
|
+
| Component | Description |
|
|
602
|
+
| ------------------------------- | -------------------------------- |
|
|
603
|
+
| `Feedback.Spinner` | Animated loading spinner |
|
|
604
|
+
| `Feedback.Loading` | Full-screen loading overlay |
|
|
605
|
+
| `Feedback.Skeleton` | Content placeholder shimmer |
|
|
606
|
+
| `Feedback.Progress` | Linear progress bar |
|
|
607
|
+
| `Feedback.ProgressCircle` | Circular progress indicator |
|
|
608
|
+
| `Feedback.Banner` | Contextual notification banner |
|
|
609
|
+
| `Feedback.Empty` / `EmptyState` | Empty list or page placeholder |
|
|
610
|
+
| `Feedback.Error` | Error state with optional retry |
|
|
611
|
+
| `Feedback.PermissionCard` | Permission request UI |
|
|
612
|
+
| `ToastProvider` / `useToast` | Queued toast notification system |
|
|
613
|
+
| `ErrorBoundary` | React error boundary wrapper |
|
|
277
614
|
|
|
278
615
|
### Navigation
|
|
279
616
|
|
|
280
|
-
| Component
|
|
281
|
-
|
|
282
|
-
| `Navigation.AppBar`
|
|
283
|
-
| `Navigation.BottomNav`
|
|
284
|
-
| `Navigation.Header`
|
|
285
|
-
| `Navigation.HeaderToolbar`
|
|
286
|
-
| `Navigation.CollapsingHeader`
|
|
287
|
-
| `Navigation.FloatingBar`
|
|
288
|
-
| `Navigation.SearchBar`
|
|
289
|
-
| `Navigation.SegmentedControl`
|
|
290
|
-
| `Navigation.Tabs`
|
|
291
|
-
| `Navigation.Steps`
|
|
292
|
-
| `Navigation.Menu` / `MenuSection` | Dropdown menu with sections
|
|
617
|
+
| Component | Description |
|
|
618
|
+
| --------------------------------- | ------------------------------------- |
|
|
619
|
+
| `Navigation.AppBar` | Top navigation bar with action slots |
|
|
620
|
+
| `Navigation.BottomNav` | Bottom tab bar |
|
|
621
|
+
| `Navigation.Header` | Screen header |
|
|
622
|
+
| `Navigation.HeaderToolbar` | Header with left, center, right slots |
|
|
623
|
+
| `Navigation.CollapsingHeader` | Header that collapses on scroll |
|
|
624
|
+
| `Navigation.FloatingBar` | Floating action bar |
|
|
625
|
+
| `Navigation.SearchBar` | Navigation-level search input |
|
|
626
|
+
| `Navigation.SegmentedControl` | Segmented control switcher |
|
|
627
|
+
| `Navigation.Tabs` | Tab navigation container |
|
|
628
|
+
| `Navigation.Steps` | Step indicator / breadcrumb |
|
|
629
|
+
| `Navigation.Menu` / `MenuSection` | Dropdown menu with sections |
|
|
293
630
|
|
|
294
631
|
### Dialog
|
|
295
632
|
|
|
296
|
-
| Component
|
|
297
|
-
|
|
298
|
-
| `Dialog.BottomSheet`
|
|
299
|
-
| `Dialog.ActionSheet`
|
|
300
|
-
| `Dialog.Confirmation`
|
|
301
|
-
| `Dialog.EditValue`
|
|
302
|
-
| `Sheet` (direct import)
|
|
303
|
-
| `ConfirmationProvider` / `useConfirmationContext` | Imperative confirmation API
|
|
633
|
+
| Component | Description |
|
|
634
|
+
| ------------------------------------------------- | --------------------------------------------- |
|
|
635
|
+
| `Dialog.BottomSheet` | Bottom drawer (RN Modal, no native peer deps) |
|
|
636
|
+
| `Dialog.ActionSheet` | Action sheet with options list |
|
|
637
|
+
| `Dialog.Confirmation` | Confirm / cancel modal |
|
|
638
|
+
| `Dialog.EditValue` | Inline edit value modal |
|
|
639
|
+
| `Sheet` (direct import) | Controlled sheet via `Dialog/Dialog.Sheet` |
|
|
640
|
+
| `ConfirmationProvider` / `useConfirmationContext` | Imperative confirmation API |
|
|
304
641
|
|
|
305
642
|
### Typography
|
|
306
643
|
|
|
307
|
-
| Component
|
|
308
|
-
|
|
309
|
-
| `Typography.Text`
|
|
310
|
-
| `Typography.Heading` | Heading shorthand
|
|
644
|
+
| Component | Description |
|
|
645
|
+
| -------------------- | ---------------------------------------------------------------- |
|
|
646
|
+
| `Typography.Text` | Main text component — accepts `variant` prop from the full scale |
|
|
647
|
+
| `Typography.Heading` | Heading shorthand |
|
|
311
648
|
|
|
312
649
|
### Primitives
|
|
313
650
|
|
|
314
|
-
| Component
|
|
315
|
-
|
|
316
|
-
| `Box`
|
|
317
|
-
| `Row`
|
|
318
|
-
| `Stack`
|
|
319
|
-
| `Spacer`
|
|
320
|
-
| `Pressable`
|
|
321
|
-
| `AspectRatio` | Aspect-ratio container
|
|
651
|
+
| Component | Description |
|
|
652
|
+
| ------------- | --------------------------- |
|
|
653
|
+
| `Box` | NativeWind layout container |
|
|
654
|
+
| `Row` | Horizontal flex |
|
|
655
|
+
| `Stack` | Vertical flex |
|
|
656
|
+
| `Spacer` | Flexible space filler |
|
|
657
|
+
| `Pressable` | Animated press target |
|
|
658
|
+
| `AspectRatio` | Aspect-ratio container |
|
|
322
659
|
|
|
323
660
|
### Layouts
|
|
324
661
|
|
|
325
|
-
| Component
|
|
326
|
-
|
|
327
|
-
| `Container`
|
|
328
|
-
| `ScrollContainer`
|
|
329
|
-
| `GradientContainer`
|
|
330
|
-
| `PlainLayout`
|
|
662
|
+
| Component | Description |
|
|
663
|
+
| -------------------- | ------------------------------------- |
|
|
664
|
+
| `Container` | Padded layout container |
|
|
665
|
+
| `ScrollContainer` | Scrollable layout wrapper |
|
|
666
|
+
| `GradientContainer` | Container with gradient background |
|
|
667
|
+
| `PlainLayout` | Unstyled layout for custom screens |
|
|
331
668
|
| `TabNavigatorLayout` | Layout for tab-based screen structure |
|
|
332
669
|
|
|
333
670
|
### Views
|
|
334
671
|
|
|
335
|
-
| Component
|
|
336
|
-
|
|
337
|
-
| `ListViewV2`
|
|
338
|
-
| `FormViewV2`
|
|
339
|
-
| `AuthFormView`
|
|
672
|
+
| Component | Description |
|
|
673
|
+
| ---------------------- | ------------------------------------------------- |
|
|
674
|
+
| `ListViewV2` | Paginated list with filtering and empty states |
|
|
675
|
+
| `FormViewV2` | Dynamic form view with field management |
|
|
676
|
+
| `AuthFormView` | Authentication form layout |
|
|
340
677
|
| `Wizard` / `useWizard` | Multi-step wizard with guards and lifecycle hooks |
|
|
678
|
+
| `AppSplashView` | Branded splash screen with logo and loading state |
|
|
341
679
|
|
|
342
680
|
### Widgets
|
|
343
681
|
|
|
344
|
-
| Component
|
|
345
|
-
|
|
346
|
-
| `ScreenHeader`
|
|
347
|
-
| `MenuGrid` / `MenuCard` | Grid and card-based navigation menus
|
|
348
|
-
| `QuickActions`
|
|
349
|
-
| `PromoBanner`
|
|
350
|
-
| `PromoModal`
|
|
351
|
-
| `HomeHeader`
|
|
352
|
-
| `GeoPermissionCard`
|
|
353
|
-
| `TimeZoneSelector`
|
|
354
|
-
| `JSONViewer`
|
|
682
|
+
| Component | Description |
|
|
683
|
+
| ----------------------- | ----------------------------------------- |
|
|
684
|
+
| `ScreenHeader` | Composite screen header widget |
|
|
685
|
+
| `MenuGrid` / `MenuCard` | Grid and card-based navigation menus |
|
|
686
|
+
| `QuickActions` | Quick action icon grid |
|
|
687
|
+
| `PromoBanner` | Swipeable promotional carousel |
|
|
688
|
+
| `PromoModal` | Promotional modal overlay |
|
|
689
|
+
| `HomeHeader` | Home screen hero header |
|
|
690
|
+
| `GeoPermissionCard` | Geolocation permission request card |
|
|
691
|
+
| `TimeZoneSelector` | Timezone picker widget |
|
|
692
|
+
| `JSONViewer` | Collapsible JSON tree (debug / dev tools) |
|
|
355
693
|
|
|
356
694
|
---
|
|
357
695
|
|
|
@@ -372,7 +710,7 @@ setActiveTheme(MY_DARK_THEME);
|
|
|
372
710
|
|
|
373
711
|
// Inject CSS variables into a subtree
|
|
374
712
|
const themeStyle = vars(themeToVars(MY_DARK_THEME));
|
|
375
|
-
<View style={themeStyle}>{children}</View
|
|
713
|
+
<View style={themeStyle}>{children}</View>;
|
|
376
714
|
```
|
|
377
715
|
|
|
378
716
|
### Reading the theme in components
|
|
@@ -382,7 +720,9 @@ import { useTheme } from "@sincpro/mobile-ui/theme";
|
|
|
382
720
|
|
|
383
721
|
function PriceCard() {
|
|
384
722
|
const theme = useTheme();
|
|
385
|
-
return
|
|
723
|
+
return (
|
|
724
|
+
<View style={{ backgroundColor: theme.bg.card, borderColor: theme.border.default }} />
|
|
725
|
+
);
|
|
386
726
|
}
|
|
387
727
|
```
|
|
388
728
|
|
|
@@ -393,33 +733,33 @@ import { extendTheme } from "@sincpro/mobile-ui/theme";
|
|
|
393
733
|
|
|
394
734
|
// Deep-merges on top of the DS base theme
|
|
395
735
|
const MY_THEME = extendTheme({
|
|
396
|
-
accent:
|
|
397
|
-
primary:
|
|
736
|
+
accent: "#00C357",
|
|
737
|
+
primary: "#0B0B0B",
|
|
398
738
|
secondary: "#6B6963",
|
|
399
739
|
bg: {
|
|
400
740
|
page: "#FAFAFA",
|
|
401
741
|
card: "#FFFFFF",
|
|
402
742
|
},
|
|
403
743
|
text: {
|
|
404
|
-
accent: "#006E2E",
|
|
744
|
+
accent: "#006E2E", // text on accent-tinted surfaces
|
|
405
745
|
},
|
|
406
746
|
});
|
|
407
747
|
```
|
|
408
748
|
|
|
409
749
|
### ThemeTokens reference
|
|
410
750
|
|
|
411
|
-
| Group
|
|
412
|
-
|
|
413
|
-
| `primary / secondary / accent`
|
|
414
|
-
| `bg.*`
|
|
415
|
-
| `text.*`
|
|
416
|
-
| `icon.*`
|
|
417
|
-
| `border.*`
|
|
418
|
-
| `success / warning / danger / info` | Semantic state colors
|
|
419
|
-
| `*Light`
|
|
420
|
-
| `ring / input`
|
|
421
|
-
| `gradient.*`
|
|
422
|
-
| `shadow.*`
|
|
751
|
+
| Group | Keys |
|
|
752
|
+
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
753
|
+
| `primary / secondary / accent` | Main brand color scales |
|
|
754
|
+
| `bg.*` | `page, card, muted, accent, hover, disabled, popover` |
|
|
755
|
+
| `text.*` | `primary, secondary, tertiary, muted, accent, inverse, disabled, onPrimary, onSecondary, onAccent, onDanger, onSuccess` |
|
|
756
|
+
| `icon.*` | `primary, secondary, tertiary, inverse, disabled` |
|
|
757
|
+
| `border.*` | `default, light, strong, focus` |
|
|
758
|
+
| `success / warning / danger / info` | Semantic state colors |
|
|
759
|
+
| `*Light` | Tinted backgrounds for each state |
|
|
760
|
+
| `ring / input` | Focus ring and input border |
|
|
761
|
+
| `gradient.*` | `primary, accent` — arrays of color stops |
|
|
762
|
+
| `shadow.*` | `sm, md, lg` — React Native shadow objects |
|
|
423
763
|
|
|
424
764
|
---
|
|
425
765
|
|
|
@@ -443,16 +783,16 @@ import { Typography } from "@sincpro/mobile-ui/Typography";
|
|
|
443
783
|
<Typography.Text variant="button">Action Label</Typography.Text>
|
|
444
784
|
```
|
|
445
785
|
|
|
446
|
-
| Group
|
|
447
|
-
|
|
448
|
-
| Display
|
|
449
|
-
| Headings
|
|
450
|
-
| Body
|
|
451
|
-
| Labels
|
|
452
|
-
| Data (mono) | `data` `dataLarge`
|
|
453
|
-
| Meta (mono) | `overline` `caption` `captionSmall`
|
|
454
|
-
| Buttons
|
|
455
|
-
| Legacy
|
|
786
|
+
| Group | Variants |
|
|
787
|
+
| ----------- | ------------------------------------ |
|
|
788
|
+
| Display | `display` |
|
|
789
|
+
| Headings | `h1` `h2` `h3` `h4` `h5` `h6` |
|
|
790
|
+
| Body | `bodyLarge` `body` `bodySmall` |
|
|
791
|
+
| Labels | `label` `labelSmall` |
|
|
792
|
+
| Data (mono) | `data` `dataLarge` |
|
|
793
|
+
| Meta (mono) | `overline` `caption` `captionSmall` |
|
|
794
|
+
| Buttons | `button` `buttonSmall` `buttonLarge` |
|
|
795
|
+
| Legacy | `title` `subtitle` |
|
|
456
796
|
|
|
457
797
|
### FontRole
|
|
458
798
|
|
|
@@ -473,9 +813,9 @@ import { configureFonts, resetFonts } from "@sincpro/mobile-ui/theme/typography"
|
|
|
473
813
|
|
|
474
814
|
configureFonts({
|
|
475
815
|
titleRegular: "Satoshi-Regular",
|
|
476
|
-
titleMedium:
|
|
477
|
-
title:
|
|
478
|
-
titleBlack:
|
|
816
|
+
titleMedium: "Satoshi-Medium",
|
|
817
|
+
title: "Satoshi-Bold",
|
|
818
|
+
titleBlack: "Satoshi-Black",
|
|
479
819
|
});
|
|
480
820
|
|
|
481
821
|
// Reset to defaults — useful in tests and Storybook stories
|
|
@@ -484,15 +824,15 @@ resetFonts();
|
|
|
484
824
|
|
|
485
825
|
### Font size scale
|
|
486
826
|
|
|
487
|
-
| Token
|
|
488
|
-
|
|
489
|
-
| `xs`
|
|
490
|
-
| `sm`
|
|
491
|
-
| `base` | 14
|
|
492
|
-
| `lg`
|
|
493
|
-
| `xl`
|
|
494
|
-
| `2xl`
|
|
495
|
-
| `3xl`
|
|
827
|
+
| Token | `px` | Token | `px` |
|
|
828
|
+
| ------ | ---- | ----- | ---- |
|
|
829
|
+
| `xs` | 10 | `4xl` | 28 |
|
|
830
|
+
| `sm` | 13 | `5xl` | 32 |
|
|
831
|
+
| `base` | 14 | `6xl` | 36 |
|
|
832
|
+
| `lg` | 16 | `7xl` | 48 |
|
|
833
|
+
| `xl` | 18 | `8xl` | 64 |
|
|
834
|
+
| `2xl` | 20 | | |
|
|
835
|
+
| `3xl` | 24 | | |
|
|
496
836
|
|
|
497
837
|
### Custom text styles
|
|
498
838
|
|
|
@@ -504,7 +844,7 @@ const errorLabel = createTextStyle("labelSmall", { color: "#FF4D1F" });
|
|
|
504
844
|
|
|
505
845
|
// Fully custom
|
|
506
846
|
const amountStyle = createCustomTextStyle("4xl", "monoMedium", {
|
|
507
|
-
lineHeight:
|
|
847
|
+
lineHeight: "none",
|
|
508
848
|
letterSpacing: "tight",
|
|
509
849
|
});
|
|
510
850
|
```
|
|
@@ -519,22 +859,22 @@ Centralized animation constants so motion feels consistent across every screen
|
|
|
519
859
|
import { duration, easing, spring } from "@sincpro/mobile-ui/tokens";
|
|
520
860
|
|
|
521
861
|
// Durations (ms)
|
|
522
|
-
duration.instant
|
|
523
|
-
duration.fast
|
|
524
|
-
duration.normal
|
|
525
|
-
duration.slow
|
|
526
|
-
duration.slower
|
|
862
|
+
duration.instant; // 0 — immediate state swap, no transition
|
|
863
|
+
duration.fast; // 120 — micro-interactions (button press, toggle)
|
|
864
|
+
duration.normal; // 200 — standard transitions (modal, drawer)
|
|
865
|
+
duration.slow; // 320 — large layout shifts
|
|
866
|
+
duration.slower; // 480 — onboarding, emphasis animations
|
|
527
867
|
|
|
528
868
|
// Bezier easing curves
|
|
529
|
-
easing.standard
|
|
530
|
-
easing.decelerate
|
|
531
|
-
easing.accelerate
|
|
532
|
-
easing.emphasized
|
|
869
|
+
easing.standard; // general purpose
|
|
870
|
+
easing.decelerate; // elements entering the screen
|
|
871
|
+
easing.accelerate; // elements leaving the screen
|
|
872
|
+
easing.emphasized; // expressive, attention-drawing transitions
|
|
533
873
|
|
|
534
874
|
// Reanimated spring presets
|
|
535
|
-
spring.default
|
|
536
|
-
spring.gentle
|
|
537
|
-
spring.snappy
|
|
875
|
+
spring.default; // balanced bounce
|
|
876
|
+
spring.gentle; // smooth, no overshoot
|
|
877
|
+
spring.snappy; // fast with slight bounce
|
|
538
878
|
```
|
|
539
879
|
|
|
540
880
|
Usage with Reanimated:
|
|
@@ -544,7 +884,7 @@ import { withTiming, withSpring } from "react-native-reanimated";
|
|
|
544
884
|
import { duration, easing, spring } from "@sincpro/mobile-ui/tokens";
|
|
545
885
|
|
|
546
886
|
const opacity = withTiming(1, { duration: duration.normal, easing: easing.decelerate });
|
|
547
|
-
const scale
|
|
887
|
+
const scale = withSpring(1, spring.snappy);
|
|
548
888
|
```
|
|
549
889
|
|
|
550
890
|
---
|
|
@@ -575,19 +915,15 @@ The preset ships the full token class set (`bg-primary`, `text-text-secondary`,
|
|
|
575
915
|
```js
|
|
576
916
|
module.exports = {
|
|
577
917
|
presets: [require("@sincpro/mobile-ui/tailwind.preset")],
|
|
578
|
-
content: [
|
|
579
|
-
"./App.tsx",
|
|
580
|
-
"./src/**/*.{ts,tsx}",
|
|
581
|
-
"./node_modules/@sincpro/mobile-ui/**/*.js",
|
|
582
|
-
],
|
|
918
|
+
content: ["./App.tsx", "./src/**/*.{ts,tsx}", "./node_modules/@sincpro/mobile-ui/**/*.js"],
|
|
583
919
|
theme: {
|
|
584
920
|
extend: {
|
|
585
921
|
// Override font families to match loaded font names
|
|
586
922
|
fontFamily: {
|
|
587
|
-
light:
|
|
588
|
-
regular:
|
|
589
|
-
medium:
|
|
590
|
-
semibold:
|
|
923
|
+
light: ["Inter_300Light"],
|
|
924
|
+
regular: ["Inter_400Regular"],
|
|
925
|
+
medium: ["Inter_500Medium"],
|
|
926
|
+
semibold: ["Inter_600SemiBold"],
|
|
591
927
|
extrabold: ["Inter_800ExtraBold"],
|
|
592
928
|
},
|
|
593
929
|
},
|
|
@@ -612,13 +948,13 @@ import "@sincpro/mobile-ui/theme/globals.css";
|
|
|
612
948
|
Best tree-shaking; makes the component's category visible at the call site:
|
|
613
949
|
|
|
614
950
|
```tsx
|
|
615
|
-
import { Display }
|
|
616
|
-
import { Form }
|
|
617
|
-
import { Feedback }
|
|
951
|
+
import { Display } from "@sincpro/mobile-ui/Display";
|
|
952
|
+
import { Form } from "@sincpro/mobile-ui/Form";
|
|
953
|
+
import { Feedback } from "@sincpro/mobile-ui/Feedback";
|
|
618
954
|
import { Navigation } from "@sincpro/mobile-ui/Navigation";
|
|
619
|
-
import { Dialog }
|
|
955
|
+
import { Dialog } from "@sincpro/mobile-ui/Dialog";
|
|
620
956
|
import { Typography } from "@sincpro/mobile-ui/Typography";
|
|
621
|
-
import { Actions }
|
|
957
|
+
import { Actions } from "@sincpro/mobile-ui/Actions";
|
|
622
958
|
```
|
|
623
959
|
|
|
624
960
|
### Theme and tokens
|
|
@@ -634,18 +970,18 @@ For components outside a namespace:
|
|
|
634
970
|
|
|
635
971
|
```tsx
|
|
636
972
|
import Sheet from "@sincpro/mobile-ui/Dialog/Dialog.Sheet";
|
|
637
|
-
import { ConfirmationProvider }
|
|
638
|
-
import { ToastProvider, useToast }
|
|
639
|
-
import { Wizard, useWizard }
|
|
973
|
+
import { ConfirmationProvider } from "@sincpro/mobile-ui/Dialog";
|
|
974
|
+
import { ToastProvider, useToast } from "@sincpro/mobile-ui/Feedback";
|
|
975
|
+
import { Wizard, useWizard } from "@sincpro/mobile-ui/views/Wizard";
|
|
640
976
|
```
|
|
641
977
|
|
|
642
978
|
### Layout primitives
|
|
643
979
|
|
|
644
980
|
```tsx
|
|
645
|
-
import { Box, Row, Stack, Spacer }
|
|
646
|
-
import Container
|
|
647
|
-
import PlainLayout
|
|
648
|
-
import TabNavigatorLayout
|
|
981
|
+
import { Box, Row, Stack, Spacer } from "@sincpro/mobile-ui/primitives";
|
|
982
|
+
import Container from "@sincpro/mobile-ui/layouts/Container";
|
|
983
|
+
import PlainLayout from "@sincpro/mobile-ui/layouts/PlainLayout";
|
|
984
|
+
import TabNavigatorLayout from "@sincpro/mobile-ui/layouts/TabNavigatorLayout";
|
|
649
985
|
```
|
|
650
986
|
|
|
651
987
|
---
|
|
@@ -661,26 +997,35 @@ make storybook-android # expo run:android
|
|
|
661
997
|
|
|
662
998
|
Stories are organized into four groups:
|
|
663
999
|
|
|
664
|
-
| Group
|
|
665
|
-
|
|
666
|
-
| **Foundations** | Tokens, typography scale, color palette, motion
|
|
667
|
-
| **Components**
|
|
668
|
-
| **Patterns**
|
|
669
|
-
| **Legacy**
|
|
1000
|
+
| Group | Contents |
|
|
1001
|
+
| --------------- | ------------------------------------------------------- |
|
|
1002
|
+
| **Foundations** | Tokens, typography scale, color palette, motion |
|
|
1003
|
+
| **Components** | All compound component namespaces |
|
|
1004
|
+
| **Patterns** | Composed examples (list screen, form screen, auth flow) |
|
|
1005
|
+
| **Legacy** | Deprecated components kept for backwards compatibility |
|
|
670
1006
|
|
|
671
1007
|
Stories live in `.rnstorybook/stories/**/*.stories.tsx`. The Storybook harness is excluded from the published package (`files: ["dist", ...]`) and never lands in consumer bundles.
|
|
672
1008
|
|
|
1009
|
+
UX is a cross-cutting quality, not another catalog category. Component states live with
|
|
1010
|
+
their component (`Actions/Button`, `Data Display/CopyableText`, `Inputs/Select`, `Feedback`);
|
|
1011
|
+
task compositions live under named `Patterns/Forms`, `Patterns/Records`,
|
|
1012
|
+
`Patterns/Collections`, `Patterns/Views` and `Patterns/Profiles`.
|
|
1013
|
+
The former `Patterns/UX`, top-level `Views` and `Templates` story IDs changed;
|
|
1014
|
+
package exports did not. The taxonomy is regression-tested.
|
|
1015
|
+
Stories that own a full-screen scroll set `parameters.ownsScroll = true`; the canvas does
|
|
1016
|
+
not wrap them in a second vertical scroll. Other canvases use keyboard-aware scrolling.
|
|
1017
|
+
|
|
673
1018
|
---
|
|
674
1019
|
|
|
675
1020
|
## Development
|
|
676
1021
|
|
|
677
|
-
| Command
|
|
678
|
-
|
|
679
|
-
| `make build`
|
|
680
|
-
| `make format`
|
|
681
|
-
| `make verify-format` | CI gate — runs `format`, fails if any file changed
|
|
682
|
-
| `make storybook`
|
|
683
|
-
| `make publish`
|
|
1022
|
+
| Command | What it does |
|
|
1023
|
+
| -------------------- | ------------------------------------------------------ |
|
|
1024
|
+
| `make build` | `tsc` + `tsc-alias` → `dist/` (JS + type declarations) |
|
|
1025
|
+
| `make format` | Auto-fix: ESLint + Prettier + type-check |
|
|
1026
|
+
| `make verify-format` | CI gate — runs `format`, fails if any file changed |
|
|
1027
|
+
| `make storybook` | Start Expo dev server for Storybook |
|
|
1028
|
+
| `make publish` | Bump version + `npm publish` |
|
|
684
1029
|
|
|
685
1030
|
After building, copy the dist to consumers in the local monorepo (no symlinks setup):
|
|
686
1031
|
|