orn-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -0
- package/bin/orn-ui.mjs +228 -0
- package/lib/commonjs/atoms/Avatar.js +42 -0
- package/lib/commonjs/atoms/Avatar.js.map +1 -0
- package/lib/commonjs/atoms/Badge.js +82 -0
- package/lib/commonjs/atoms/Badge.js.map +1 -0
- package/lib/commonjs/atoms/Button.js +222 -0
- package/lib/commonjs/atoms/Button.js.map +1 -0
- package/lib/commonjs/atoms/Card.js +33 -0
- package/lib/commonjs/atoms/Card.js.map +1 -0
- package/lib/commonjs/atoms/Checkbox.js +107 -0
- package/lib/commonjs/atoms/Checkbox.js.map +1 -0
- package/lib/commonjs/atoms/Divider.js +27 -0
- package/lib/commonjs/atoms/Divider.js.map +1 -0
- package/lib/commonjs/atoms/EmptyState.js +69 -0
- package/lib/commonjs/atoms/EmptyState.js.map +1 -0
- package/lib/commonjs/atoms/Fab.js +62 -0
- package/lib/commonjs/atoms/Fab.js.map +1 -0
- package/lib/commonjs/atoms/IconButton.js +60 -0
- package/lib/commonjs/atoms/IconButton.js.map +1 -0
- package/lib/commonjs/atoms/Image.js +113 -0
- package/lib/commonjs/atoms/Image.js.map +1 -0
- package/lib/commonjs/atoms/Input.js +173 -0
- package/lib/commonjs/atoms/Input.js.map +1 -0
- package/lib/commonjs/atoms/KeyValueRow.js +60 -0
- package/lib/commonjs/atoms/KeyValueRow.js.map +1 -0
- package/lib/commonjs/atoms/PressableScale.js +60 -0
- package/lib/commonjs/atoms/PressableScale.js.map +1 -0
- package/lib/commonjs/atoms/Spinner.js +156 -0
- package/lib/commonjs/atoms/Spinner.js.map +1 -0
- package/lib/commonjs/atoms/Text.js +63 -0
- package/lib/commonjs/atoms/Text.js.map +1 -0
- package/lib/commonjs/atoms/index.js +135 -0
- package/lib/commonjs/atoms/index.js.map +1 -0
- package/lib/commonjs/icons/Icon.js +40 -0
- package/lib/commonjs/icons/Icon.js.map +1 -0
- package/lib/commonjs/icons/defaultIcons.js +386 -0
- package/lib/commonjs/icons/defaultIcons.js.map +1 -0
- package/lib/commonjs/icons/index.js +20 -0
- package/lib/commonjs/icons/index.js.map +1 -0
- package/lib/commonjs/icons/types.js +6 -0
- package/lib/commonjs/icons/types.js.map +1 -0
- package/lib/commonjs/index.js +61 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/molecules/AvatarHeader.js +87 -0
- package/lib/commonjs/molecules/AvatarHeader.js.map +1 -0
- package/lib/commonjs/molecules/FormActions.js +47 -0
- package/lib/commonjs/molecules/FormActions.js.map +1 -0
- package/lib/commonjs/molecules/InfoRow.js +83 -0
- package/lib/commonjs/molecules/InfoRow.js.map +1 -0
- package/lib/commonjs/molecules/OptionCard.js +98 -0
- package/lib/commonjs/molecules/OptionCard.js.map +1 -0
- package/lib/commonjs/molecules/Stepper.js +104 -0
- package/lib/commonjs/molecules/Stepper.js.map +1 -0
- package/lib/commonjs/molecules/Steps.js +220 -0
- package/lib/commonjs/molecules/Steps.js.map +1 -0
- package/lib/commonjs/molecules/ThemeToggle.js +91 -0
- package/lib/commonjs/molecules/ThemeToggle.js.map +1 -0
- package/lib/commonjs/molecules/index.js +55 -0
- package/lib/commonjs/molecules/index.js.map +1 -0
- package/lib/commonjs/organisms/Alert.js +223 -0
- package/lib/commonjs/organisms/Alert.js.map +1 -0
- package/lib/commonjs/organisms/AlertProvider.js +109 -0
- package/lib/commonjs/organisms/AlertProvider.js.map +1 -0
- package/lib/commonjs/organisms/BottomSheet.js +154 -0
- package/lib/commonjs/organisms/BottomSheet.js.map +1 -0
- package/lib/commonjs/organisms/DateField.js +147 -0
- package/lib/commonjs/organisms/DateField.js.map +1 -0
- package/lib/commonjs/organisms/DatePicker.js +253 -0
- package/lib/commonjs/organisms/DatePicker.js.map +1 -0
- package/lib/commonjs/organisms/List.js +143 -0
- package/lib/commonjs/organisms/List.js.map +1 -0
- package/lib/commonjs/organisms/Modal.js +286 -0
- package/lib/commonjs/organisms/Modal.js.map +1 -0
- package/lib/commonjs/organisms/Screen.js +75 -0
- package/lib/commonjs/organisms/Screen.js.map +1 -0
- package/lib/commonjs/organisms/SearchList.js +180 -0
- package/lib/commonjs/organisms/SearchList.js.map +1 -0
- package/lib/commonjs/organisms/Select.js +178 -0
- package/lib/commonjs/organisms/Select.js.map +1 -0
- package/lib/commonjs/organisms/Toast.js +157 -0
- package/lib/commonjs/organisms/Toast.js.map +1 -0
- package/lib/commonjs/organisms/ToastProvider.js +131 -0
- package/lib/commonjs/organisms/ToastProvider.js.map +1 -0
- package/lib/commonjs/organisms/Wizard.js +116 -0
- package/lib/commonjs/organisms/Wizard.js.map +1 -0
- package/lib/commonjs/organisms/index.js +127 -0
- package/lib/commonjs/organisms/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/theme/UIProvider.js +110 -0
- package/lib/commonjs/theme/UIProvider.js.map +1 -0
- package/lib/commonjs/theme/createStyles.js +42 -0
- package/lib/commonjs/theme/createStyles.js.map +1 -0
- package/lib/commonjs/theme/createTheme.js +50 -0
- package/lib/commonjs/theme/createTheme.js.map +1 -0
- package/lib/commonjs/theme/index.js +151 -0
- package/lib/commonjs/theme/index.js.map +1 -0
- package/lib/commonjs/theme/insets.js +15 -0
- package/lib/commonjs/theme/insets.js.map +1 -0
- package/lib/commonjs/theme/labels.js +18 -0
- package/lib/commonjs/theme/labels.js.map +1 -0
- package/lib/commonjs/theme/palettes.js +42 -0
- package/lib/commonjs/theme/palettes.js.map +1 -0
- package/lib/commonjs/theme/tokens.js +120 -0
- package/lib/commonjs/theme/tokens.js.map +1 -0
- package/lib/commonjs/theme/types.js +6 -0
- package/lib/commonjs/theme/types.js.map +1 -0
- package/lib/module/atoms/Avatar.js +37 -0
- package/lib/module/atoms/Avatar.js.map +1 -0
- package/lib/module/atoms/Badge.js +77 -0
- package/lib/module/atoms/Badge.js.map +1 -0
- package/lib/module/atoms/Button.js +217 -0
- package/lib/module/atoms/Button.js.map +1 -0
- package/lib/module/atoms/Card.js +28 -0
- package/lib/module/atoms/Card.js.map +1 -0
- package/lib/module/atoms/Checkbox.js +102 -0
- package/lib/module/atoms/Checkbox.js.map +1 -0
- package/lib/module/atoms/Divider.js +22 -0
- package/lib/module/atoms/Divider.js.map +1 -0
- package/lib/module/atoms/EmptyState.js +64 -0
- package/lib/module/atoms/EmptyState.js.map +1 -0
- package/lib/module/atoms/Fab.js +57 -0
- package/lib/module/atoms/Fab.js.map +1 -0
- package/lib/module/atoms/IconButton.js +55 -0
- package/lib/module/atoms/IconButton.js.map +1 -0
- package/lib/module/atoms/Image.js +108 -0
- package/lib/module/atoms/Image.js.map +1 -0
- package/lib/module/atoms/Input.js +168 -0
- package/lib/module/atoms/Input.js.map +1 -0
- package/lib/module/atoms/KeyValueRow.js +55 -0
- package/lib/module/atoms/KeyValueRow.js.map +1 -0
- package/lib/module/atoms/PressableScale.js +55 -0
- package/lib/module/atoms/PressableScale.js.map +1 -0
- package/lib/module/atoms/Spinner.js +151 -0
- package/lib/module/atoms/Spinner.js.map +1 -0
- package/lib/module/atoms/Text.js +58 -0
- package/lib/module/atoms/Text.js.map +1 -0
- package/lib/module/atoms/index.js +18 -0
- package/lib/module/atoms/index.js.map +1 -0
- package/lib/module/icons/Icon.js +35 -0
- package/lib/module/icons/Icon.js.map +1 -0
- package/lib/module/icons/defaultIcons.js +381 -0
- package/lib/module/icons/defaultIcons.js.map +1 -0
- package/lib/module/icons/index.js +5 -0
- package/lib/module/icons/index.js.map +1 -0
- package/lib/module/icons/types.js +4 -0
- package/lib/module/icons/types.js.map +1 -0
- package/lib/module/index.js +8 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/molecules/AvatarHeader.js +82 -0
- package/lib/module/molecules/AvatarHeader.js.map +1 -0
- package/lib/module/molecules/FormActions.js +42 -0
- package/lib/module/molecules/FormActions.js.map +1 -0
- package/lib/module/molecules/InfoRow.js +78 -0
- package/lib/module/molecules/InfoRow.js.map +1 -0
- package/lib/module/molecules/OptionCard.js +93 -0
- package/lib/module/molecules/OptionCard.js.map +1 -0
- package/lib/module/molecules/Stepper.js +99 -0
- package/lib/module/molecules/Stepper.js.map +1 -0
- package/lib/module/molecules/Steps.js +215 -0
- package/lib/module/molecules/Steps.js.map +1 -0
- package/lib/module/molecules/ThemeToggle.js +86 -0
- package/lib/module/molecules/ThemeToggle.js.map +1 -0
- package/lib/module/molecules/index.js +10 -0
- package/lib/module/molecules/index.js.map +1 -0
- package/lib/module/organisms/Alert.js +218 -0
- package/lib/module/organisms/Alert.js.map +1 -0
- package/lib/module/organisms/AlertProvider.js +103 -0
- package/lib/module/organisms/AlertProvider.js.map +1 -0
- package/lib/module/organisms/BottomSheet.js +149 -0
- package/lib/module/organisms/BottomSheet.js.map +1 -0
- package/lib/module/organisms/DateField.js +142 -0
- package/lib/module/organisms/DateField.js.map +1 -0
- package/lib/module/organisms/DatePicker.js +247 -0
- package/lib/module/organisms/DatePicker.js.map +1 -0
- package/lib/module/organisms/List.js +138 -0
- package/lib/module/organisms/List.js.map +1 -0
- package/lib/module/organisms/Modal.js +281 -0
- package/lib/module/organisms/Modal.js.map +1 -0
- package/lib/module/organisms/Screen.js +70 -0
- package/lib/module/organisms/Screen.js.map +1 -0
- package/lib/module/organisms/SearchList.js +175 -0
- package/lib/module/organisms/SearchList.js.map +1 -0
- package/lib/module/organisms/Select.js +173 -0
- package/lib/module/organisms/Select.js.map +1 -0
- package/lib/module/organisms/Toast.js +152 -0
- package/lib/module/organisms/Toast.js.map +1 -0
- package/lib/module/organisms/ToastProvider.js +125 -0
- package/lib/module/organisms/ToastProvider.js.map +1 -0
- package/lib/module/organisms/Wizard.js +111 -0
- package/lib/module/organisms/Wizard.js.map +1 -0
- package/lib/module/organisms/index.js +16 -0
- package/lib/module/organisms/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/theme/UIProvider.js +97 -0
- package/lib/module/theme/UIProvider.js.map +1 -0
- package/lib/module/theme/createStyles.js +38 -0
- package/lib/module/theme/createStyles.js.map +1 -0
- package/lib/module/theme/createTheme.js +45 -0
- package/lib/module/theme/createTheme.js.map +1 -0
- package/lib/module/theme/index.js +10 -0
- package/lib/module/theme/index.js.map +1 -0
- package/lib/module/theme/insets.js +11 -0
- package/lib/module/theme/insets.js.map +1 -0
- package/lib/module/theme/labels.js +14 -0
- package/lib/module/theme/labels.js.map +1 -0
- package/lib/module/theme/palettes.js +38 -0
- package/lib/module/theme/palettes.js.map +1 -0
- package/lib/module/theme/tokens.js +117 -0
- package/lib/module/theme/tokens.js.map +1 -0
- package/lib/module/theme/types.js +4 -0
- package/lib/module/theme/types.js.map +1 -0
- package/lib/typescript/atoms/Avatar.d.ts +15 -0
- package/lib/typescript/atoms/Avatar.d.ts.map +1 -0
- package/lib/typescript/atoms/Badge.d.ts +19 -0
- package/lib/typescript/atoms/Badge.d.ts.map +1 -0
- package/lib/typescript/atoms/Button.d.ts +30 -0
- package/lib/typescript/atoms/Button.d.ts.map +1 -0
- package/lib/typescript/atoms/Card.d.ts +8 -0
- package/lib/typescript/atoms/Card.d.ts.map +1 -0
- package/lib/typescript/atoms/Checkbox.d.ts +11 -0
- package/lib/typescript/atoms/Checkbox.d.ts.map +1 -0
- package/lib/typescript/atoms/Divider.d.ts +8 -0
- package/lib/typescript/atoms/Divider.d.ts.map +1 -0
- package/lib/typescript/atoms/EmptyState.d.ts +12 -0
- package/lib/typescript/atoms/EmptyState.d.ts.map +1 -0
- package/lib/typescript/atoms/Fab.d.ts +22 -0
- package/lib/typescript/atoms/Fab.d.ts.map +1 -0
- package/lib/typescript/atoms/IconButton.d.ts +23 -0
- package/lib/typescript/atoms/IconButton.d.ts.map +1 -0
- package/lib/typescript/atoms/Image.d.ts +45 -0
- package/lib/typescript/atoms/Image.d.ts.map +1 -0
- package/lib/typescript/atoms/Input.d.ts +24 -0
- package/lib/typescript/atoms/Input.d.ts.map +1 -0
- package/lib/typescript/atoms/KeyValueRow.d.ts +17 -0
- package/lib/typescript/atoms/KeyValueRow.d.ts.map +1 -0
- package/lib/typescript/atoms/PressableScale.d.ts +15 -0
- package/lib/typescript/atoms/PressableScale.d.ts.map +1 -0
- package/lib/typescript/atoms/Spinner.d.ts +20 -0
- package/lib/typescript/atoms/Spinner.d.ts.map +1 -0
- package/lib/typescript/atoms/Text.d.ts +13 -0
- package/lib/typescript/atoms/Text.d.ts.map +1 -0
- package/lib/typescript/atoms/index.d.ts +31 -0
- package/lib/typescript/atoms/index.d.ts.map +1 -0
- package/lib/typescript/icons/Icon.d.ts +18 -0
- package/lib/typescript/icons/Icon.d.ts.map +1 -0
- package/lib/typescript/icons/defaultIcons.d.ts +5 -0
- package/lib/typescript/icons/defaultIcons.d.ts.map +1 -0
- package/lib/typescript/icons/index.d.ts +5 -0
- package/lib/typescript/icons/index.d.ts.map +1 -0
- package/lib/typescript/icons/types.d.ts +10 -0
- package/lib/typescript/icons/types.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +6 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/molecules/AvatarHeader.d.ts +20 -0
- package/lib/typescript/molecules/AvatarHeader.d.ts.map +1 -0
- package/lib/typescript/molecules/FormActions.d.ts +21 -0
- package/lib/typescript/molecules/FormActions.d.ts.map +1 -0
- package/lib/typescript/molecules/InfoRow.d.ts +17 -0
- package/lib/typescript/molecules/InfoRow.d.ts.map +1 -0
- package/lib/typescript/molecules/OptionCard.d.ts +20 -0
- package/lib/typescript/molecules/OptionCard.d.ts.map +1 -0
- package/lib/typescript/molecules/Stepper.d.ts +15 -0
- package/lib/typescript/molecules/Stepper.d.ts.map +1 -0
- package/lib/typescript/molecules/Steps.d.ts +29 -0
- package/lib/typescript/molecules/Steps.d.ts.map +1 -0
- package/lib/typescript/molecules/ThemeToggle.d.ts +15 -0
- package/lib/typescript/molecules/ThemeToggle.d.ts.map +1 -0
- package/lib/typescript/molecules/index.d.ts +15 -0
- package/lib/typescript/molecules/index.d.ts.map +1 -0
- package/lib/typescript/organisms/Alert.d.ts +29 -0
- package/lib/typescript/organisms/Alert.d.ts.map +1 -0
- package/lib/typescript/organisms/AlertProvider.d.ts +32 -0
- package/lib/typescript/organisms/AlertProvider.d.ts.map +1 -0
- package/lib/typescript/organisms/BottomSheet.d.ts +15 -0
- package/lib/typescript/organisms/BottomSheet.d.ts.map +1 -0
- package/lib/typescript/organisms/DateField.d.ts +26 -0
- package/lib/typescript/organisms/DateField.d.ts.map +1 -0
- package/lib/typescript/organisms/DatePicker.d.ts +49 -0
- package/lib/typescript/organisms/DatePicker.d.ts.map +1 -0
- package/lib/typescript/organisms/List.d.ts +35 -0
- package/lib/typescript/organisms/List.d.ts.map +1 -0
- package/lib/typescript/organisms/Modal.d.ts +25 -0
- package/lib/typescript/organisms/Modal.d.ts.map +1 -0
- package/lib/typescript/organisms/Screen.d.ts +26 -0
- package/lib/typescript/organisms/Screen.d.ts.map +1 -0
- package/lib/typescript/organisms/SearchList.d.ts +40 -0
- package/lib/typescript/organisms/SearchList.d.ts.map +1 -0
- package/lib/typescript/organisms/Select.d.ts +25 -0
- package/lib/typescript/organisms/Select.d.ts.map +1 -0
- package/lib/typescript/organisms/Toast.d.ts +28 -0
- package/lib/typescript/organisms/Toast.d.ts.map +1 -0
- package/lib/typescript/organisms/ToastProvider.d.ts +36 -0
- package/lib/typescript/organisms/ToastProvider.d.ts.map +1 -0
- package/lib/typescript/organisms/Wizard.d.ts +40 -0
- package/lib/typescript/organisms/Wizard.d.ts.map +1 -0
- package/lib/typescript/organisms/index.d.ts +27 -0
- package/lib/typescript/organisms/index.d.ts.map +1 -0
- package/lib/typescript/theme/UIProvider.d.ts +47 -0
- package/lib/typescript/theme/UIProvider.d.ts.map +1 -0
- package/lib/typescript/theme/createStyles.d.ts +21 -0
- package/lib/typescript/theme/createStyles.d.ts.map +1 -0
- package/lib/typescript/theme/createTheme.d.ts +9 -0
- package/lib/typescript/theme/createTheme.d.ts.map +1 -0
- package/lib/typescript/theme/index.d.ts +14 -0
- package/lib/typescript/theme/index.d.ts.map +1 -0
- package/lib/typescript/theme/insets.d.ts +9 -0
- package/lib/typescript/theme/insets.d.ts.map +1 -0
- package/lib/typescript/theme/labels.d.ts +13 -0
- package/lib/typescript/theme/labels.d.ts.map +1 -0
- package/lib/typescript/theme/palettes.d.ts +24 -0
- package/lib/typescript/theme/palettes.d.ts.map +1 -0
- package/lib/typescript/theme/tokens.d.ts +41 -0
- package/lib/typescript/theme/tokens.d.ts.map +1 -0
- package/lib/typescript/theme/types.d.ts +24 -0
- package/lib/typescript/theme/types.d.ts.map +1 -0
- package/package.json +311 -0
- package/registry/alert-provider.json +21 -0
- package/registry/alert.json +19 -0
- package/registry/avatar-header.json +18 -0
- package/registry/avatar.json +18 -0
- package/registry/badge.json +18 -0
- package/registry/bottom-sheet.json +18 -0
- package/registry/button.json +18 -0
- package/registry/card.json +18 -0
- package/registry/checkbox.json +18 -0
- package/registry/core.json +64 -0
- package/registry/date-field.json +24 -0
- package/registry/date-picker.json +23 -0
- package/registry/divider.json +18 -0
- package/registry/empty-state.json +18 -0
- package/registry/fab.json +19 -0
- package/registry/form-actions.json +19 -0
- package/registry/icon-button.json +19 -0
- package/registry/image.json +20 -0
- package/registry/index.json +465 -0
- package/registry/info-row.json +19 -0
- package/registry/input.json +18 -0
- package/registry/key-value-row.json +18 -0
- package/registry/list.json +20 -0
- package/registry/manifest.json +263 -0
- package/registry/modal.json +21 -0
- package/registry/option-card.json +18 -0
- package/registry/pressable-scale.json +18 -0
- package/registry/screen.json +18 -0
- package/registry/search-list.json +23 -0
- package/registry/select.json +18 -0
- package/registry/spinner.json +18 -0
- package/registry/stepper.json +18 -0
- package/registry/steps.json +18 -0
- package/registry/theme-toggle.json +18 -0
- package/registry/title.json +21 -0
- package/registry/toast-provider.json +20 -0
- package/registry/toast.json +18 -0
- package/registry/wizard.json +20 -0
- package/src/atoms/Avatar.tsx +40 -0
- package/src/atoms/Badge.tsx +55 -0
- package/src/atoms/Button.tsx +186 -0
- package/src/atoms/Card.tsx +27 -0
- package/src/atoms/Checkbox.tsx +75 -0
- package/src/atoms/Divider.tsx +14 -0
- package/src/atoms/EmptyState.tsx +60 -0
- package/src/atoms/Fab.tsx +63 -0
- package/src/atoms/IconButton.tsx +53 -0
- package/src/atoms/Image.tsx +137 -0
- package/src/atoms/Input.tsx +166 -0
- package/src/atoms/KeyValueRow.tsx +48 -0
- package/src/atoms/PressableScale.tsx +52 -0
- package/src/atoms/Spinner.tsx +127 -0
- package/src/atoms/Text.tsx +58 -0
- package/src/atoms/index.ts +30 -0
- package/src/icons/Icon.tsx +27 -0
- package/src/icons/defaultIcons.tsx +292 -0
- package/src/icons/index.ts +4 -0
- package/src/icons/types.ts +27 -0
- package/src/index.ts +5 -0
- package/src/molecules/AvatarHeader.tsx +69 -0
- package/src/molecules/FormActions.tsx +52 -0
- package/src/molecules/InfoRow.tsx +52 -0
- package/src/molecules/OptionCard.tsx +82 -0
- package/src/molecules/Stepper.tsx +76 -0
- package/src/molecules/Steps.tsx +198 -0
- package/src/molecules/ThemeToggle.tsx +67 -0
- package/src/molecules/index.ts +14 -0
- package/src/organisms/Alert.tsx +162 -0
- package/src/organisms/AlertProvider.tsx +125 -0
- package/src/organisms/BottomSheet.tsx +95 -0
- package/src/organisms/DateField.tsx +134 -0
- package/src/organisms/DatePicker.tsx +255 -0
- package/src/organisms/List.tsx +154 -0
- package/src/organisms/Modal.tsx +251 -0
- package/src/organisms/Screen.tsx +84 -0
- package/src/organisms/SearchList.tsx +186 -0
- package/src/organisms/Select.tsx +156 -0
- package/src/organisms/Toast.tsx +135 -0
- package/src/organisms/ToastProvider.tsx +166 -0
- package/src/organisms/Wizard.tsx +137 -0
- package/src/organisms/index.ts +26 -0
- package/src/theme/UIProvider.tsx +129 -0
- package/src/theme/createStyles.ts +40 -0
- package/src/theme/createTheme.ts +49 -0
- package/src/theme/index.ts +22 -0
- package/src/theme/insets.ts +9 -0
- package/src/theme/labels.ts +23 -0
- package/src/theme/palettes.ts +53 -0
- package/src/theme/tokens.ts +113 -0
- package/src/theme/types.ts +27 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Toast",
|
|
3
|
+
"slug": "toast",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"category": "organisms",
|
|
6
|
+
"exportedNames": [
|
|
7
|
+
"Toast"
|
|
8
|
+
],
|
|
9
|
+
"files": [
|
|
10
|
+
{
|
|
11
|
+
"path": "organisms/Toast.tsx",
|
|
12
|
+
"content": "import React, { memo, useEffect, useRef } from 'react';\nimport { Animated, Pressable, Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\n\nexport type ToastVariant = 'success' | 'error' | 'warning' | 'info';\n\nexport interface ToastProps {\n title: string;\n message?: string;\n variant?: ToastVariant;\n /** Se dispara al tocar el toast (además de cerrarlo). */\n onPress?: () => void;\n onDismiss?: () => void;\n /** Oculta la X de cerrar. El toast igual se auto-descarta por tiempo. */\n hideCloseButton?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst ICON_BY_VARIANT: Record<ToastVariant, IconName> = {\n success: 'check',\n error: 'alert',\n warning: 'warning',\n info: 'info',\n};\n\nconst useStyles = createStyles((theme) => ({\n container: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.tokens.spacing.md,\n backgroundColor: theme.colors.surface,\n borderRadius: theme.tokens.radius.lg,\n borderWidth: 1,\n borderColor: theme.colors.border,\n paddingVertical: theme.tokens.spacing.md,\n paddingHorizontal: theme.tokens.spacing.lg,\n ...theme.tokens.shadow.lg,\n },\n accent: {\n position: 'absolute',\n left: 0,\n top: 0,\n bottom: 0,\n width: 4,\n borderTopLeftRadius: theme.tokens.radius.lg,\n borderBottomLeftRadius: theme.tokens.radius.lg,\n },\n text: { flex: 1, gap: 2 },\n title: {\n fontSize: theme.tokens.fontSize.md,\n fontWeight: theme.tokens.fontWeight.semibold,\n color: theme.colors.text,\n },\n message: { fontSize: theme.tokens.fontSize.sm, color: theme.colors.textLight, lineHeight: theme.tokens.lineHeight.sm },\n}));\n\n/**\n * Toast\n * Notificación efímera. Normalmente no se usa directo: `ToastProvider` +\n * `useToast()` la montan y la descartan por vos. Se expone igual por si\n * querés colocarla vos mismo dentro de una pantalla concreta.\n */\nexport const Toast = memo(\n ({ title, message, variant = 'info', onPress, onDismiss, hideCloseButton, style, testID }: ToastProps) => {\n const colors = useColors();\n const styles = useStyles();\n const allowFontScaling = useAllowFontScaling();\n\n const accentColor: Record<ToastVariant, string> = {\n success: colors.success,\n error: colors.error,\n warning: colors.warning,\n info: colors.secondary,\n };\n\n const body = (\n <View style={[styles.container, style]} testID={testID} accessibilityRole=\"alert\">\n <View style={[styles.accent, { backgroundColor: accentColor[variant] }]} />\n <Icon name={ICON_BY_VARIANT[variant]} size={22} color={accentColor[variant]} />\n <View style={styles.text}>\n <Text allowFontScaling={allowFontScaling} style={styles.title}>\n {title}\n </Text>\n {!!message && (\n <Text allowFontScaling={allowFontScaling} style={styles.message}>\n {message}\n </Text>\n )}\n </View>\n {!hideCloseButton && !!onDismiss && (\n <Pressable onPress={onDismiss} accessibilityRole=\"button\" accessibilityLabel=\"Dismiss\" hitSlop={8}>\n <Icon name=\"close\" size={18} color={colors.textLight} />\n </Pressable>\n )}\n </View>\n );\n\n if (!onPress) return body;\n\n return (\n <Pressable onPress={onPress} accessibilityRole=\"button\" accessibilityLabel={title}>\n {body}\n </Pressable>\n );\n }\n);\nToast.displayName = 'Toast';\n\n/** Envuelve un Toast con su animación de entrada/salida. Uso interno del provider. */\nexport const AnimatedToast = memo(\n ({ children, onHeight }: { children: React.ReactNode; onHeight?: (h: number) => void }) => {\n const progress = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n Animated.spring(progress, { toValue: 1, useNativeDriver: true, speed: 14, bounciness: 4 }).start();\n }, [progress]);\n\n return (\n <Animated.View\n onLayout={(e) => onHeight?.(e.nativeEvent.layout.height)}\n style={{\n opacity: progress,\n transform: [{ translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [-16, 0] }) }],\n }}\n >\n {children}\n </Animated.View>\n );\n }\n);\nAnimatedToast.displayName = 'AnimatedToast';\n"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"registryDependencies": [
|
|
16
|
+
"core"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Wizard",
|
|
3
|
+
"slug": "wizard",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"category": "organisms",
|
|
6
|
+
"exportedNames": [
|
|
7
|
+
"Wizard"
|
|
8
|
+
],
|
|
9
|
+
"files": [
|
|
10
|
+
{
|
|
11
|
+
"path": "organisms/Wizard.tsx",
|
|
12
|
+
"content": "import React, { useCallback, useMemo, useState } from 'react';\nimport { ScrollView, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { Steps, type StepItem } from '../molecules/Steps';\nimport { Button } from '../atoms/Button';\n\nexport interface WizardStep extends StepItem {\n content: React.ReactNode;\n /** false bloquea el avance desde este paso (validación). @default true */\n canGoNext?: boolean;\n}\n\nexport interface WizardProps {\n steps: WizardStep[];\n /** Paso actual (controlado). Si se omite, el Wizard maneja su propio estado. */\n current?: number;\n onStepChange?: (index: number) => void;\n onFinish?: () => void;\n /** @default 'horizontal' */\n orientation?: 'horizontal' | 'vertical';\n completedIndicator?: 'check' | 'number';\n /** Permite volver a un paso ya completado tocándolo en el indicador. @default true */\n allowStepNavigation?: boolean;\n backLabel?: string;\n nextLabel?: string;\n finishLabel?: string;\n /**\n * El contenido del paso scrollea cuando no entra. Ponelo en false sólo si\n * un paso renderiza su propia lista virtualizada (anidar una\n * VirtualizedList dentro de un ScrollView rompe la virtualización).\n * @default true\n */\n scrollableContent?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: { flex: 1, gap: theme.tokens.spacing.xl },\n content: { flex: 1 },\n contentInner: { flexGrow: 1, paddingBottom: theme.tokens.spacing.sm },\n footer: { flexDirection: 'row', gap: theme.tokens.spacing.md },\n footerButton: { flex: 1 },\n}));\n\n/**\n * Wizard\n * Flujo multi-paso: indicador (Steps) + contenido del paso actual + barra de\n * navegación. Funciona controlado (`current` + `onStepChange`) o por su\n * cuenta. `canGoNext: false` en un paso deshabilita el avance, que es donde\n * se engancha la validación del formulario de ese paso.\n */\nexport function Wizard({\n steps,\n current: controlledCurrent,\n onStepChange,\n onFinish,\n orientation = 'horizontal',\n completedIndicator = 'check',\n allowStepNavigation = true,\n backLabel = 'Back',\n nextLabel = 'Next',\n finishLabel = 'Finish',\n scrollableContent = true,\n style,\n}: WizardProps) {\n const styles = useStyles();\n const [uncontrolled, setUncontrolled] = useState(0);\n const isControlled = controlledCurrent !== undefined;\n const current = isControlled ? controlledCurrent : uncontrolled;\n\n const goTo = useCallback(\n (index: number) => {\n const clamped = Math.max(0, Math.min(index, steps.length - 1));\n if (!isControlled) setUncontrolled(clamped);\n onStepChange?.(clamped);\n },\n [isControlled, onStepChange, steps.length]\n );\n\n const stepItems = useMemo(\n () => steps.map(({ label, description }) => ({ label, description })),\n [steps]\n );\n\n const activeStep = steps[current];\n const isFirst = current === 0;\n const isLast = current === steps.length - 1;\n const canGoNext = activeStep?.canGoNext !== false;\n\n const handleNext = () => {\n if (isLast) onFinish?.();\n else goTo(current + 1);\n };\n\n return (\n <View style={[styles.container, style]}>\n <Steps\n steps={stepItems}\n current={current}\n orientation={orientation}\n completedIndicator={completedIndicator}\n // Sólo se puede volver atrás tocando el indicador; saltar hacia\n // adelante se saltearía la validación de los pasos intermedios.\n onStepPress={allowStepNavigation ? (index) => index < current && goTo(index) : undefined}\n />\n\n {/* El contenido scrollea en vez de desbordar: con el teclado abierto un\n form largo se comía el footer y los botones quedaban pintados encima\n de los campos. */}\n {scrollableContent ? (\n <ScrollView\n style={styles.content}\n contentContainerStyle={styles.contentInner}\n keyboardShouldPersistTaps=\"handled\"\n automaticallyAdjustKeyboardInsets\n showsVerticalScrollIndicator={false}\n >\n {activeStep?.content}\n </ScrollView>\n ) : (\n <View style={styles.content}>{activeStep?.content}</View>\n )}\n\n <View style={styles.footer}>\n {!isFirst && (\n <Button title={backLabel} variant=\"outline\" onPress={() => goTo(current - 1)} style={styles.footerButton} />\n )}\n <Button\n title={isLast ? finishLabel : nextLabel}\n onPress={handleNext}\n disabled={!canGoNext}\n style={styles.footerButton}\n />\n </View>\n </View>\n );\n}\n"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"registryDependencies": [
|
|
16
|
+
"core",
|
|
17
|
+
"steps",
|
|
18
|
+
"button"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import { useColors } from '../theme/UIProvider';
|
|
4
|
+
|
|
5
|
+
export interface AvatarProps {
|
|
6
|
+
size?: number;
|
|
7
|
+
backgroundColor?: string;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
style?: StyleProp<ViewStyle>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const styles = StyleSheet.create({
|
|
13
|
+
circle: { justifyContent: 'center', alignItems: 'center' },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Avatar
|
|
18
|
+
* Círculo con ícono/iniciales adentro. Usado a la izquierda de filas de
|
|
19
|
+
* lista o como header de detalle (junto a AvatarHeader).
|
|
20
|
+
*/
|
|
21
|
+
export const Avatar = memo(({ size = 44, backgroundColor, children, style }: AvatarProps) => {
|
|
22
|
+
const colors = useColors();
|
|
23
|
+
return (
|
|
24
|
+
<View
|
|
25
|
+
style={[
|
|
26
|
+
styles.circle,
|
|
27
|
+
{
|
|
28
|
+
width: size,
|
|
29
|
+
height: size,
|
|
30
|
+
borderRadius: size / 2,
|
|
31
|
+
backgroundColor: backgroundColor || colors.primary + '15',
|
|
32
|
+
},
|
|
33
|
+
style,
|
|
34
|
+
]}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
</View>
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
Avatar.displayName = 'Avatar';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { Text, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import { createStyles } from '../theme/createStyles';
|
|
4
|
+
import { useAllowFontScaling, useTheme } from '../theme/UIProvider';
|
|
5
|
+
|
|
6
|
+
export type BadgeVariant = 'success' | 'error' | 'warning' | 'info' | 'neutral';
|
|
7
|
+
|
|
8
|
+
export interface BadgeProps {
|
|
9
|
+
label: string;
|
|
10
|
+
variant?: BadgeVariant;
|
|
11
|
+
/** Override manual, gana sobre `variant`. */
|
|
12
|
+
backgroundColor?: string;
|
|
13
|
+
textColor?: string;
|
|
14
|
+
style?: StyleProp<ViewStyle>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const useStyles = createStyles((theme) => ({
|
|
18
|
+
badge: {
|
|
19
|
+
paddingHorizontal: theme.tokens.spacing.md - 2,
|
|
20
|
+
paddingVertical: theme.tokens.spacing.xs + 1,
|
|
21
|
+
borderRadius: theme.tokens.radius.full,
|
|
22
|
+
alignSelf: 'flex-start',
|
|
23
|
+
},
|
|
24
|
+
text: { fontSize: theme.tokens.fontSize.xs, fontWeight: theme.tokens.fontWeight.bold, letterSpacing: 0.3 },
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Badge
|
|
29
|
+
* Pill de estado. `variant` cubre los casos comunes (éxito/error/advertencia/
|
|
30
|
+
* info/neutral); `backgroundColor`/`textColor` quedan para casos que no
|
|
31
|
+
* encajan en el set semántico.
|
|
32
|
+
*/
|
|
33
|
+
export const Badge = memo(({ label, variant = 'neutral', backgroundColor, textColor, style }: BadgeProps) => {
|
|
34
|
+
const theme = useTheme();
|
|
35
|
+
const styles = useStyles();
|
|
36
|
+
const allowFontScaling = useAllowFontScaling();
|
|
37
|
+
const { colors } = theme;
|
|
38
|
+
|
|
39
|
+
const palette: Record<BadgeVariant, { bg: string; fg: string }> = {
|
|
40
|
+
success: { bg: colors.success + '20', fg: colors.success },
|
|
41
|
+
error: { bg: colors.error + '20', fg: colors.error },
|
|
42
|
+
warning: { bg: colors.warning + '20', fg: colors.warning },
|
|
43
|
+
info: { bg: colors.secondary + '20', fg: colors.secondary },
|
|
44
|
+
neutral: { bg: colors.inputBackground, fg: colors.textLight },
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<View style={[styles.badge, { backgroundColor: backgroundColor ?? palette[variant].bg }, style]}>
|
|
49
|
+
<Text allowFontScaling={allowFontScaling} style={[styles.text, { color: textColor ?? palette[variant].fg }]}>
|
|
50
|
+
{label}
|
|
51
|
+
</Text>
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
Badge.displayName = 'Badge';
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import React, { memo, useRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ActivityIndicator,
|
|
4
|
+
Animated,
|
|
5
|
+
Pressable,
|
|
6
|
+
Text,
|
|
7
|
+
type GestureResponderEvent,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type TextStyle,
|
|
10
|
+
type ViewStyle,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
import { createStyles } from '../theme/createStyles';
|
|
13
|
+
import { useAllowFontScaling, useColors, useTheme } from '../theme/UIProvider';
|
|
14
|
+
import { Icon } from '../icons/Icon';
|
|
15
|
+
import type { IconName } from '../icons/types';
|
|
16
|
+
|
|
17
|
+
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
|
|
18
|
+
|
|
19
|
+
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive';
|
|
20
|
+
export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
21
|
+
|
|
22
|
+
export interface ButtonProps {
|
|
23
|
+
title: string;
|
|
24
|
+
onPress: (e: GestureResponderEvent) => void;
|
|
25
|
+
variant?: ButtonVariant;
|
|
26
|
+
size?: ButtonSize;
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
style?: StyleProp<ViewStyle>;
|
|
30
|
+
textStyle?: StyleProp<TextStyle>;
|
|
31
|
+
leftIcon?: React.ReactNode;
|
|
32
|
+
rightIcon?: React.ReactNode;
|
|
33
|
+
leftIconName?: IconName;
|
|
34
|
+
rightIconName?: IconName;
|
|
35
|
+
iconColor?: string;
|
|
36
|
+
testID?: string;
|
|
37
|
+
accessibilityLabel?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const useStyles = createStyles((theme) => ({
|
|
41
|
+
base: {
|
|
42
|
+
flexDirection: 'row',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
justifyContent: 'center',
|
|
45
|
+
borderRadius: theme.tokens.radius.lg,
|
|
46
|
+
borderWidth: 0,
|
|
47
|
+
},
|
|
48
|
+
sm: { paddingVertical: theme.tokens.spacing.sm, paddingHorizontal: theme.tokens.spacing.md },
|
|
49
|
+
md: { paddingVertical: theme.tokens.spacing.lg - 1, paddingHorizontal: theme.tokens.spacing.xl },
|
|
50
|
+
lg: { paddingVertical: theme.tokens.spacing.lg + 2, paddingHorizontal: theme.tokens.spacing.xxl },
|
|
51
|
+
link: { paddingVertical: theme.tokens.spacing.xs, paddingHorizontal: 0, justifyContent: 'flex-start' },
|
|
52
|
+
outline: { borderWidth: 1 },
|
|
53
|
+
filledShadow: { ...theme.tokens.shadow.md, shadowColor: '#000' },
|
|
54
|
+
text: { fontWeight: theme.tokens.fontWeight.semibold, textAlign: 'center' },
|
|
55
|
+
textSm: { fontSize: theme.tokens.fontSize.sm },
|
|
56
|
+
textMd: { fontSize: theme.tokens.fontSize.lg },
|
|
57
|
+
textLg: { fontSize: theme.tokens.fontSize.xl },
|
|
58
|
+
underline: { textDecorationLine: 'underline' },
|
|
59
|
+
iconLeft: { marginRight: theme.tokens.spacing.sm },
|
|
60
|
+
iconRight: { marginLeft: theme.tokens.spacing.sm },
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
function resolveColors(variant: ButtonVariant, disabled: boolean, colors: ReturnType<typeof useColors>) {
|
|
64
|
+
if (disabled) {
|
|
65
|
+
return { background: colors.inputBackground, text: colors.textLight, border: 'transparent' };
|
|
66
|
+
}
|
|
67
|
+
switch (variant) {
|
|
68
|
+
case 'outline':
|
|
69
|
+
return { background: 'transparent', text: colors.primary, border: colors.primary };
|
|
70
|
+
case 'ghost':
|
|
71
|
+
return { background: 'transparent', text: colors.primary, border: 'transparent' };
|
|
72
|
+
case 'link':
|
|
73
|
+
return { background: 'transparent', text: colors.primary, border: 'transparent' };
|
|
74
|
+
case 'secondary':
|
|
75
|
+
return { background: colors.secondary, text: colors.white, border: 'transparent' };
|
|
76
|
+
case 'destructive':
|
|
77
|
+
return { background: colors.error, text: colors.white, border: 'transparent' };
|
|
78
|
+
case 'primary':
|
|
79
|
+
default:
|
|
80
|
+
return { background: colors.primary, text: colors.white, border: 'transparent' };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Button
|
|
86
|
+
* Único componente de botón de la librería. `variant` decide el estilo
|
|
87
|
+
* semántico, `size` la densidad. El feedback de "presionado" anima solo
|
|
88
|
+
* `transform` (Animated.spring, useNativeDriver) — nunca opacity ni layout.
|
|
89
|
+
*/
|
|
90
|
+
export const Button = memo(
|
|
91
|
+
({
|
|
92
|
+
title,
|
|
93
|
+
onPress,
|
|
94
|
+
variant = 'primary',
|
|
95
|
+
size = 'md',
|
|
96
|
+
loading = false,
|
|
97
|
+
disabled = false,
|
|
98
|
+
style,
|
|
99
|
+
textStyle,
|
|
100
|
+
leftIcon,
|
|
101
|
+
rightIcon,
|
|
102
|
+
leftIconName,
|
|
103
|
+
rightIconName,
|
|
104
|
+
iconColor,
|
|
105
|
+
testID,
|
|
106
|
+
accessibilityLabel,
|
|
107
|
+
}: ButtonProps) => {
|
|
108
|
+
const styles = useStyles();
|
|
109
|
+
const colors = useColors();
|
|
110
|
+
const theme = useTheme();
|
|
111
|
+
const allowFontScaling = useAllowFontScaling();
|
|
112
|
+
const scale = useRef(new Animated.Value(1)).current;
|
|
113
|
+
const isDisabled = disabled || loading;
|
|
114
|
+
const isFilled = variant === 'primary' || variant === 'secondary' || variant === 'destructive';
|
|
115
|
+
|
|
116
|
+
const resolved = resolveColors(variant, disabled, colors);
|
|
117
|
+
const effectiveIconColor = iconColor ?? resolved.text;
|
|
118
|
+
|
|
119
|
+
const animateTo = (value: number) => {
|
|
120
|
+
Animated.spring(scale, { toValue: value, useNativeDriver: true, speed: 50, bounciness: 6 }).start();
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const containerStyle: StyleProp<ViewStyle> = [
|
|
124
|
+
styles.base,
|
|
125
|
+
styles[size],
|
|
126
|
+
variant === 'link' && styles.link,
|
|
127
|
+
variant === 'outline' && [styles.outline, { borderColor: resolved.border }],
|
|
128
|
+
{ backgroundColor: resolved.background },
|
|
129
|
+
isFilled && !disabled && styles.filledShadow,
|
|
130
|
+
{ opacity: isDisabled ? 0.7 : 1 },
|
|
131
|
+
style,
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
const labelStyle: StyleProp<TextStyle> = [
|
|
135
|
+
styles.text,
|
|
136
|
+
size === 'sm' ? styles.textSm : size === 'lg' ? styles.textLg : styles.textMd,
|
|
137
|
+
{ color: resolved.text },
|
|
138
|
+
variant === 'link' && styles.underline,
|
|
139
|
+
textStyle,
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
const iconSize = size === 'sm' ? 16 : size === 'lg' ? 22 : 20;
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
// El Pressable ES el contenedor animado, no un wrapper alrededor de uno.
|
|
146
|
+
// Con un Animated.View interno, un `style` de layout (flex, width,
|
|
147
|
+
// margin) caía en el hijo mientras el Pressable — que es quien ocupa
|
|
148
|
+
// lugar en el layout del padre — quedaba sin él: el ancho colapsaba y
|
|
149
|
+
// el label desaparecía (visible en los botones en fila del Alert).
|
|
150
|
+
<AnimatedPressable
|
|
151
|
+
onPress={onPress}
|
|
152
|
+
onPressIn={() => {
|
|
153
|
+
if (!isDisabled) animateTo(0.95);
|
|
154
|
+
}}
|
|
155
|
+
onPressOut={() => {
|
|
156
|
+
if (!isDisabled) animateTo(1);
|
|
157
|
+
}}
|
|
158
|
+
disabled={isDisabled}
|
|
159
|
+
testID={testID}
|
|
160
|
+
accessibilityRole="button"
|
|
161
|
+
accessibilityLabel={accessibilityLabel ?? title}
|
|
162
|
+
accessibilityState={{ disabled: isDisabled, busy: loading }}
|
|
163
|
+
style={[containerStyle, { transform: [{ scale }] }]}
|
|
164
|
+
>
|
|
165
|
+
{loading ? (
|
|
166
|
+
<ActivityIndicator size="small" color={resolved.text} />
|
|
167
|
+
) : (
|
|
168
|
+
<>
|
|
169
|
+
{leftIcon}
|
|
170
|
+
{!leftIcon && leftIconName && (
|
|
171
|
+
<Icon name={leftIconName} size={iconSize} color={effectiveIconColor} style={styles.iconLeft} />
|
|
172
|
+
)}
|
|
173
|
+
<Text allowFontScaling={allowFontScaling} style={labelStyle}>
|
|
174
|
+
{title}
|
|
175
|
+
</Text>
|
|
176
|
+
{rightIcon}
|
|
177
|
+
{!rightIcon && rightIconName && (
|
|
178
|
+
<Icon name={rightIconName} size={iconSize} color={effectiveIconColor} style={styles.iconRight} />
|
|
179
|
+
)}
|
|
180
|
+
</>
|
|
181
|
+
)}
|
|
182
|
+
</AnimatedPressable>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
Button.displayName = 'Button';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { View, type ViewProps, type ViewStyle } from 'react-native';
|
|
3
|
+
import { createStyles } from '../theme/createStyles';
|
|
4
|
+
|
|
5
|
+
export interface CardProps extends ViewProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
style?: ViewStyle;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const useStyles = createStyles((theme) => ({
|
|
11
|
+
card: {
|
|
12
|
+
backgroundColor: theme.colors.surface,
|
|
13
|
+
borderRadius: theme.tokens.radius.xl,
|
|
14
|
+
padding: theme.tokens.spacing.lg,
|
|
15
|
+
...theme.tokens.shadow.sm,
|
|
16
|
+
},
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
export const Card = memo(({ children, style, ...props }: CardProps) => {
|
|
20
|
+
const styles = useStyles();
|
|
21
|
+
return (
|
|
22
|
+
<View style={[styles.card, style]} {...props}>
|
|
23
|
+
{children}
|
|
24
|
+
</View>
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
Card.displayName = 'Card';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React, { memo, useEffect, useRef } from 'react';
|
|
2
|
+
import { Animated, Text, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { createStyles } from '../theme/createStyles';
|
|
4
|
+
import { useAllowFontScaling, useColors } from '../theme/UIProvider';
|
|
5
|
+
import { Icon } from '../icons/Icon';
|
|
6
|
+
|
|
7
|
+
export interface CheckboxProps {
|
|
8
|
+
value: boolean;
|
|
9
|
+
onValueChange: (value: boolean) => void;
|
|
10
|
+
label?: string;
|
|
11
|
+
color?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
testID?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const useStyles = createStyles((theme) => ({
|
|
17
|
+
container: { flexDirection: 'row', alignItems: 'center', marginBottom: theme.tokens.spacing.sm },
|
|
18
|
+
box: {
|
|
19
|
+
width: 24,
|
|
20
|
+
height: 24,
|
|
21
|
+
borderRadius: theme.tokens.radius.sm,
|
|
22
|
+
borderWidth: 2,
|
|
23
|
+
justifyContent: 'center',
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
marginRight: theme.tokens.spacing.sm + 2,
|
|
26
|
+
},
|
|
27
|
+
label: { fontSize: theme.tokens.fontSize.md, color: theme.colors.text },
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
export const Checkbox = memo(({ value, onValueChange, label, color, disabled, testID }: CheckboxProps) => {
|
|
31
|
+
const colors = useColors();
|
|
32
|
+
const styles = useStyles();
|
|
33
|
+
const allowFontScaling = useAllowFontScaling();
|
|
34
|
+
const activeColor = color || colors.primary;
|
|
35
|
+
const progress = useRef(new Animated.Value(value ? 1 : 0)).current;
|
|
36
|
+
const checkScale = useRef(new Animated.Value(value ? 1 : 0)).current;
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
Animated.timing(progress, { toValue: value ? 1 : 0, duration: 150, useNativeDriver: false }).start();
|
|
40
|
+
Animated.spring(checkScale, {
|
|
41
|
+
toValue: value ? 1 : 0,
|
|
42
|
+
useNativeDriver: true,
|
|
43
|
+
speed: 40,
|
|
44
|
+
bounciness: 10,
|
|
45
|
+
}).start();
|
|
46
|
+
}, [value]);
|
|
47
|
+
|
|
48
|
+
const backgroundColor = progress.interpolate({ inputRange: [0, 1], outputRange: [colors.surface, activeColor] });
|
|
49
|
+
const borderColor = progress.interpolate({ inputRange: [0, 1], outputRange: [colors.border, activeColor] });
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<TouchableOpacity
|
|
53
|
+
style={styles.container}
|
|
54
|
+
activeOpacity={0.7}
|
|
55
|
+
disabled={disabled}
|
|
56
|
+
onPress={() => onValueChange(!value)}
|
|
57
|
+
accessibilityRole="checkbox"
|
|
58
|
+
accessibilityState={{ checked: value, disabled: !!disabled }}
|
|
59
|
+
accessibilityLabel={label}
|
|
60
|
+
testID={testID}
|
|
61
|
+
>
|
|
62
|
+
<Animated.View style={[styles.box, { backgroundColor, borderColor, opacity: disabled ? 0.5 : 1 }]}>
|
|
63
|
+
<Animated.View style={{ transform: [{ scale: checkScale }] }}>
|
|
64
|
+
<Icon name="check" size={16} color={colors.white} />
|
|
65
|
+
</Animated.View>
|
|
66
|
+
</Animated.View>
|
|
67
|
+
{label && (
|
|
68
|
+
<Text allowFontScaling={allowFontScaling} style={styles.label}>
|
|
69
|
+
{label}
|
|
70
|
+
</Text>
|
|
71
|
+
)}
|
|
72
|
+
</TouchableOpacity>
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
Checkbox.displayName = 'Checkbox';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import { useColors } from '../theme/UIProvider';
|
|
4
|
+
|
|
5
|
+
export interface DividerProps {
|
|
6
|
+
style?: StyleProp<ViewStyle>;
|
|
7
|
+
testID?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Divider = memo(({ style, testID }: DividerProps) => {
|
|
11
|
+
const colors = useColors();
|
|
12
|
+
return <View testID={testID} style={[{ height: 1, backgroundColor: colors.border, width: '100%' }, style]} />;
|
|
13
|
+
});
|
|
14
|
+
Divider.displayName = 'Divider';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { Text, View, type ViewStyle } from 'react-native';
|
|
3
|
+
import { createStyles } from '../theme/createStyles';
|
|
4
|
+
import { useAllowFontScaling, useColors, useLabels } from '../theme/UIProvider';
|
|
5
|
+
import { Icon } from '../icons/Icon';
|
|
6
|
+
import type { IconName } from '../icons/types';
|
|
7
|
+
|
|
8
|
+
export interface EmptyStateProps {
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
iconName?: IconName;
|
|
12
|
+
icon?: React.ReactNode;
|
|
13
|
+
style?: ViewStyle;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const useStyles = createStyles((theme) => ({
|
|
17
|
+
container: {
|
|
18
|
+
flex: 1,
|
|
19
|
+
justifyContent: 'center',
|
|
20
|
+
alignItems: 'center',
|
|
21
|
+
padding: theme.tokens.spacing.xxl - 4,
|
|
22
|
+
minHeight: 200,
|
|
23
|
+
},
|
|
24
|
+
title: {
|
|
25
|
+
fontSize: theme.tokens.fontSize.lg,
|
|
26
|
+
fontWeight: theme.tokens.fontWeight.semibold,
|
|
27
|
+
color: theme.colors.textLight,
|
|
28
|
+
marginTop: theme.tokens.spacing.md - 2,
|
|
29
|
+
textAlign: 'center',
|
|
30
|
+
},
|
|
31
|
+
description: {
|
|
32
|
+
fontSize: theme.tokens.fontSize.md,
|
|
33
|
+
color: theme.colors.textLight,
|
|
34
|
+
marginTop: theme.tokens.spacing.xs + 1,
|
|
35
|
+
textAlign: 'center',
|
|
36
|
+
},
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
export const EmptyState = memo(
|
|
40
|
+
({ title, description, iconName = 'search', icon, style }: EmptyStateProps) => {
|
|
41
|
+
const colors = useColors();
|
|
42
|
+
const styles = useStyles();
|
|
43
|
+
const labels = useLabels();
|
|
44
|
+
const allowFontScaling = useAllowFontScaling();
|
|
45
|
+
const resolvedTitle = title ?? labels.noResultsTitle;
|
|
46
|
+
const resolvedDescription = description ?? labels.noResultsDescription;
|
|
47
|
+
return (
|
|
48
|
+
<View style={[styles.container, style]} accessible accessibilityRole="text">
|
|
49
|
+
{icon ?? <Icon name={iconName} size={48} color={colors.textLight} />}
|
|
50
|
+
<Text allowFontScaling={allowFontScaling} style={styles.title}>
|
|
51
|
+
{resolvedTitle}
|
|
52
|
+
</Text>
|
|
53
|
+
<Text allowFontScaling={allowFontScaling} style={styles.description}>
|
|
54
|
+
{resolvedDescription}
|
|
55
|
+
</Text>
|
|
56
|
+
</View>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
EmptyState.displayName = 'EmptyState';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { type GestureResponderEvent, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import { PressableScale } from './PressableScale';
|
|
4
|
+
import { Icon } from '../icons/Icon';
|
|
5
|
+
import type { IconName } from '../icons/types';
|
|
6
|
+
import { createStyles } from '../theme/createStyles';
|
|
7
|
+
import { useColors } from '../theme/UIProvider';
|
|
8
|
+
|
|
9
|
+
export interface FabProps {
|
|
10
|
+
onPress: (e: GestureResponderEvent) => void;
|
|
11
|
+
iconName?: IconName;
|
|
12
|
+
icon?: React.ReactNode;
|
|
13
|
+
size?: number;
|
|
14
|
+
bottom?: number;
|
|
15
|
+
right?: number;
|
|
16
|
+
color?: string;
|
|
17
|
+
style?: StyleProp<ViewStyle>;
|
|
18
|
+
accessibilityLabel: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const useStyles = createStyles((theme) => ({
|
|
22
|
+
fab: {
|
|
23
|
+
position: 'absolute',
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
...theme.tokens.shadow.lg,
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Fab
|
|
32
|
+
* Botón flotante circular. `bottom`/`right` en vez del `tabBarHeight`
|
|
33
|
+
* obligatorio de la versión original — el consumidor decide su propio
|
|
34
|
+
* offset (con o sin tab bar).
|
|
35
|
+
*/
|
|
36
|
+
export const Fab = memo(
|
|
37
|
+
({ onPress, iconName = 'plus', icon, size = 60, bottom = 20, right = 20, color, style, accessibilityLabel }: FabProps) => {
|
|
38
|
+
const styles = useStyles();
|
|
39
|
+
const colors = useColors();
|
|
40
|
+
return (
|
|
41
|
+
<PressableScale
|
|
42
|
+
onPress={onPress}
|
|
43
|
+
style={[
|
|
44
|
+
styles.fab,
|
|
45
|
+
{
|
|
46
|
+
width: size,
|
|
47
|
+
height: size,
|
|
48
|
+
borderRadius: size / 2,
|
|
49
|
+
bottom,
|
|
50
|
+
right,
|
|
51
|
+
backgroundColor: color ?? colors.primary,
|
|
52
|
+
},
|
|
53
|
+
style,
|
|
54
|
+
]}
|
|
55
|
+
accessibilityRole="button"
|
|
56
|
+
accessibilityLabel={accessibilityLabel}
|
|
57
|
+
>
|
|
58
|
+
{icon ?? <Icon name={iconName} size={size * 0.5} color={colors.white} />}
|
|
59
|
+
</PressableScale>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
Fab.displayName = 'Fab';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { type GestureResponderEvent, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import { PressableScale } from './PressableScale';
|
|
4
|
+
import { Icon } from '../icons/Icon';
|
|
5
|
+
import type { IconName } from '../icons/types';
|
|
6
|
+
import { createStyles } from '../theme/createStyles';
|
|
7
|
+
|
|
8
|
+
export interface IconButtonProps {
|
|
9
|
+
iconName?: IconName;
|
|
10
|
+
icon?: React.ReactNode;
|
|
11
|
+
onPress?: (e: GestureResponderEvent) => void;
|
|
12
|
+
size?: number;
|
|
13
|
+
color?: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
style?: StyleProp<ViewStyle>;
|
|
16
|
+
accessibilityLabel: string;
|
|
17
|
+
testID?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const useStyles = createStyles((theme) => ({
|
|
21
|
+
hitArea: {
|
|
22
|
+
padding: theme.tokens.spacing.sm,
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* IconButton
|
|
30
|
+
* Botón de solo ícono con área táctil mínima de 44x44 garantizada por el
|
|
31
|
+
* padding (icon 24 + padding 8*2 = 40; sube a 44 con size>=24 default RN).
|
|
32
|
+
* `accessibilityLabel` es obligatorio: sin texto visible, es la única forma
|
|
33
|
+
* de que un lector de pantalla anuncie qué hace el botón.
|
|
34
|
+
*/
|
|
35
|
+
export const IconButton = memo(
|
|
36
|
+
({ iconName, icon, onPress, size = 24, color, disabled, style, accessibilityLabel, testID }: IconButtonProps) => {
|
|
37
|
+
const styles = useStyles();
|
|
38
|
+
return (
|
|
39
|
+
<PressableScale
|
|
40
|
+
onPress={onPress}
|
|
41
|
+
disabled={disabled}
|
|
42
|
+
style={[styles.hitArea, { opacity: disabled ? 0.5 : 1 }, style]}
|
|
43
|
+
accessibilityRole="button"
|
|
44
|
+
accessibilityLabel={accessibilityLabel}
|
|
45
|
+
accessibilityState={{ disabled: !!disabled }}
|
|
46
|
+
testID={testID}
|
|
47
|
+
>
|
|
48
|
+
{icon ?? (iconName && <Icon name={iconName} size={size} color={color} />)}
|
|
49
|
+
</PressableScale>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
IconButton.displayName = 'IconButton';
|