@entropy-softworks/ui 2026.7.31
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/CHANGELOG.md +96 -0
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/lib/components/auth/LockOverlay.d.ts +22 -0
- package/lib/components/auth/LockOverlay.d.ts.map +1 -0
- package/lib/components/auth/LockOverlay.jsx +82 -0
- package/lib/components/auth/LockOverlay.jsx.map +1 -0
- package/lib/components/auth/PasswordStrengthMeter.d.ts +12 -0
- package/lib/components/auth/PasswordStrengthMeter.d.ts.map +1 -0
- package/lib/components/auth/PasswordStrengthMeter.jsx +81 -0
- package/lib/components/auth/PasswordStrengthMeter.jsx.map +1 -0
- package/lib/components/auth/ProtectedRoute.d.ts +24 -0
- package/lib/components/auth/ProtectedRoute.d.ts.map +1 -0
- package/lib/components/auth/ProtectedRoute.jsx +56 -0
- package/lib/components/auth/ProtectedRoute.jsx.map +1 -0
- package/lib/components/auth/PwnedPasswordWarning.d.ts +13 -0
- package/lib/components/auth/PwnedPasswordWarning.d.ts.map +1 -0
- package/lib/components/auth/PwnedPasswordWarning.jsx +29 -0
- package/lib/components/auth/PwnedPasswordWarning.jsx.map +1 -0
- package/lib/components/auth/index.d.ts +5 -0
- package/lib/components/auth/index.d.ts.map +1 -0
- package/lib/components/auth/index.js +5 -0
- package/lib/components/auth/index.js.map +1 -0
- package/lib/components/bottom-nav.d.ts +25 -0
- package/lib/components/bottom-nav.d.ts.map +1 -0
- package/lib/components/bottom-nav.jsx +51 -0
- package/lib/components/bottom-nav.jsx.map +1 -0
- package/lib/components/button.d.ts +16 -0
- package/lib/components/button.d.ts.map +1 -0
- package/lib/components/button.jsx +73 -0
- package/lib/components/button.jsx.map +1 -0
- package/lib/components/card.d.ts +22 -0
- package/lib/components/card.d.ts.map +1 -0
- package/lib/components/card.jsx +36 -0
- package/lib/components/card.jsx.map +1 -0
- package/lib/components/checkbox.d.ts +12 -0
- package/lib/components/checkbox.d.ts.map +1 -0
- package/lib/components/checkbox.jsx +16 -0
- package/lib/components/checkbox.jsx.map +1 -0
- package/lib/components/connection-settings.d.ts +41 -0
- package/lib/components/connection-settings.d.ts.map +1 -0
- package/lib/components/connection-settings.jsx +354 -0
- package/lib/components/connection-settings.jsx.map +1 -0
- package/lib/components/context-menu.d.ts +52 -0
- package/lib/components/context-menu.d.ts.map +1 -0
- package/lib/components/context-menu.jsx +24 -0
- package/lib/components/context-menu.jsx.map +1 -0
- package/lib/components/data-table.d.ts +59 -0
- package/lib/components/data-table.d.ts.map +1 -0
- package/lib/components/data-table.jsx +101 -0
- package/lib/components/data-table.jsx.map +1 -0
- package/lib/components/dialog.d.ts +58 -0
- package/lib/components/dialog.d.ts.map +1 -0
- package/lib/components/dialog.jsx +197 -0
- package/lib/components/dialog.jsx.map +1 -0
- package/lib/components/dropdown-menu.d.ts +55 -0
- package/lib/components/dropdown-menu.d.ts.map +1 -0
- package/lib/components/dropdown-menu.jsx +38 -0
- package/lib/components/dropdown-menu.jsx.map +1 -0
- package/lib/components/entropy-boot-splash.d.ts +24 -0
- package/lib/components/entropy-boot-splash.d.ts.map +1 -0
- package/lib/components/entropy-boot-splash.jsx +83 -0
- package/lib/components/entropy-boot-splash.jsx.map +1 -0
- package/lib/components/entropy-mark-animated.d.ts +20 -0
- package/lib/components/entropy-mark-animated.d.ts.map +1 -0
- package/lib/components/entropy-mark-animated.jsx +148 -0
- package/lib/components/entropy-mark-animated.jsx.map +1 -0
- package/lib/components/entropy-mark-geometry.d.ts +8 -0
- package/lib/components/entropy-mark-geometry.d.ts.map +1 -0
- package/lib/components/entropy-mark-geometry.js +10 -0
- package/lib/components/entropy-mark-geometry.js.map +1 -0
- package/lib/components/entropy-mark-sequential.d.ts +12 -0
- package/lib/components/entropy-mark-sequential.d.ts.map +1 -0
- package/lib/components/entropy-mark-sequential.jsx +53 -0
- package/lib/components/entropy-mark-sequential.jsx.map +1 -0
- package/lib/components/entropy-mark.d.ts +29 -0
- package/lib/components/entropy-mark.d.ts.map +1 -0
- package/lib/components/entropy-mark.jsx +66 -0
- package/lib/components/entropy-mark.jsx.map +1 -0
- package/lib/components/error-boundary.d.ts +31 -0
- package/lib/components/error-boundary.d.ts.map +1 -0
- package/lib/components/error-boundary.jsx +40 -0
- package/lib/components/error-boundary.jsx.map +1 -0
- package/lib/components/index.d.ts +41 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/components/index.js +43 -0
- package/lib/components/index.js.map +1 -0
- package/lib/components/input.d.ts +26 -0
- package/lib/components/input.d.ts.map +1 -0
- package/lib/components/input.jsx +95 -0
- package/lib/components/input.jsx.map +1 -0
- package/lib/components/item-icon.d.ts +32 -0
- package/lib/components/item-icon.d.ts.map +1 -0
- package/lib/components/item-icon.jsx +54 -0
- package/lib/components/item-icon.jsx.map +1 -0
- package/lib/components/label.d.ts +13 -0
- package/lib/components/label.d.ts.map +1 -0
- package/lib/components/label.jsx +9 -0
- package/lib/components/label.jsx.map +1 -0
- package/lib/components/legal-dialog.d.ts +18 -0
- package/lib/components/legal-dialog.d.ts.map +1 -0
- package/lib/components/legal-dialog.jsx +31 -0
- package/lib/components/legal-dialog.jsx.map +1 -0
- package/lib/components/license-gate.d.ts +17 -0
- package/lib/components/license-gate.d.ts.map +1 -0
- package/lib/components/license-gate.jsx +21 -0
- package/lib/components/license-gate.jsx.map +1 -0
- package/lib/components/logo.d.ts +15 -0
- package/lib/components/logo.d.ts.map +1 -0
- package/lib/components/logo.jsx +20 -0
- package/lib/components/logo.jsx.map +1 -0
- package/lib/components/onboarding-wizard.d.ts +89 -0
- package/lib/components/onboarding-wizard.d.ts.map +1 -0
- package/lib/components/onboarding-wizard.jsx +185 -0
- package/lib/components/onboarding-wizard.jsx.map +1 -0
- package/lib/components/pagination.d.ts +43 -0
- package/lib/components/pagination.d.ts.map +1 -0
- package/lib/components/pagination.jsx +72 -0
- package/lib/components/pagination.jsx.map +1 -0
- package/lib/components/password-generator.d.ts +15 -0
- package/lib/components/password-generator.d.ts.map +1 -0
- package/lib/components/password-generator.jsx +273 -0
- package/lib/components/password-generator.jsx.map +1 -0
- package/lib/components/pin-pad.d.ts +20 -0
- package/lib/components/pin-pad.d.ts.map +1 -0
- package/lib/components/pin-pad.jsx +93 -0
- package/lib/components/pin-pad.jsx.map +1 -0
- package/lib/components/popover.d.ts +27 -0
- package/lib/components/popover.d.ts.map +1 -0
- package/lib/components/popover.jsx +18 -0
- package/lib/components/popover.jsx.map +1 -0
- package/lib/components/profile/ProfileDialog.d.ts +25 -0
- package/lib/components/profile/ProfileDialog.d.ts.map +1 -0
- package/lib/components/profile/ProfileDialog.jsx +71 -0
- package/lib/components/profile/ProfileDialog.jsx.map +1 -0
- package/lib/components/profile/ProfileTab.d.ts +12 -0
- package/lib/components/profile/ProfileTab.d.ts.map +1 -0
- package/lib/components/profile/ProfileTab.jsx +112 -0
- package/lib/components/profile/ProfileTab.jsx.map +1 -0
- package/lib/components/profile/SecurityTab.d.ts +32 -0
- package/lib/components/profile/SecurityTab.d.ts.map +1 -0
- package/lib/components/profile/SecurityTab.jsx +145 -0
- package/lib/components/profile/SecurityTab.jsx.map +1 -0
- package/lib/components/profile/index.d.ts +4 -0
- package/lib/components/profile/index.d.ts.map +1 -0
- package/lib/components/profile/index.js +4 -0
- package/lib/components/profile/index.js.map +1 -0
- package/lib/components/segmented-toggle.d.ts +28 -0
- package/lib/components/segmented-toggle.d.ts.map +1 -0
- package/lib/components/segmented-toggle.jsx +64 -0
- package/lib/components/segmented-toggle.jsx.map +1 -0
- package/lib/components/separator.d.ts +13 -0
- package/lib/components/separator.d.ts.map +1 -0
- package/lib/components/separator.jsx +16 -0
- package/lib/components/separator.jsx.map +1 -0
- package/lib/components/server-settings.d.ts +27 -0
- package/lib/components/server-settings.d.ts.map +1 -0
- package/lib/components/server-settings.jsx +307 -0
- package/lib/components/server-settings.jsx.map +1 -0
- package/lib/components/settings/AccountSection.d.ts +26 -0
- package/lib/components/settings/AccountSection.d.ts.map +1 -0
- package/lib/components/settings/AccountSection.jsx +32 -0
- package/lib/components/settings/AccountSection.jsx.map +1 -0
- package/lib/components/settings/DangerZoneSection.d.ts +13 -0
- package/lib/components/settings/DangerZoneSection.d.ts.map +1 -0
- package/lib/components/settings/DangerZoneSection.jsx +16 -0
- package/lib/components/settings/DangerZoneSection.jsx.map +1 -0
- package/lib/components/settings/GeneralSection.d.ts +11 -0
- package/lib/components/settings/GeneralSection.d.ts.map +1 -0
- package/lib/components/settings/GeneralSection.jsx +12 -0
- package/lib/components/settings/GeneralSection.jsx.map +1 -0
- package/lib/components/settings/SupportSection.d.ts +11 -0
- package/lib/components/settings/SupportSection.d.ts.map +1 -0
- package/lib/components/settings/SupportSection.jsx +22 -0
- package/lib/components/settings/SupportSection.jsx.map +1 -0
- package/lib/components/settings/index.d.ts +5 -0
- package/lib/components/settings/index.d.ts.map +1 -0
- package/lib/components/settings/index.js +5 -0
- package/lib/components/settings/index.js.map +1 -0
- package/lib/components/settings-row.d.ts +28 -0
- package/lib/components/settings-row.d.ts.map +1 -0
- package/lib/components/settings-row.jsx +68 -0
- package/lib/components/settings-row.jsx.map +1 -0
- package/lib/components/skeleton.d.ts +8 -0
- package/lib/components/skeleton.d.ts.map +1 -0
- package/lib/components/skeleton.jsx +24 -0
- package/lib/components/skeleton.jsx.map +1 -0
- package/lib/components/social-connections.d.ts +15 -0
- package/lib/components/social-connections.d.ts.map +1 -0
- package/lib/components/social-connections.jsx +31 -0
- package/lib/components/social-connections.jsx.map +1 -0
- package/lib/components/stat-card.d.ts +29 -0
- package/lib/components/stat-card.d.ts.map +1 -0
- package/lib/components/stat-card.jsx +36 -0
- package/lib/components/stat-card.jsx.map +1 -0
- package/lib/components/tag-chip.d.ts +18 -0
- package/lib/components/tag-chip.d.ts.map +1 -0
- package/lib/components/tag-chip.jsx +54 -0
- package/lib/components/tag-chip.jsx.map +1 -0
- package/lib/components/tag-multi-select.d.ts +27 -0
- package/lib/components/tag-multi-select.d.ts.map +1 -0
- package/lib/components/tag-multi-select.jsx +65 -0
- package/lib/components/tag-multi-select.jsx.map +1 -0
- package/lib/components/text.d.ts +22 -0
- package/lib/components/text.d.ts.map +1 -0
- package/lib/components/text.jsx +43 -0
- package/lib/components/text.jsx.map +1 -0
- package/lib/components/toast/ToastPortal.d.ts +11 -0
- package/lib/components/toast/ToastPortal.d.ts.map +1 -0
- package/lib/components/toast/ToastPortal.jsx +15 -0
- package/lib/components/toast/ToastPortal.jsx.map +1 -0
- package/lib/components/toast/ToastPortal.web.d.ts +14 -0
- package/lib/components/toast/ToastPortal.web.d.ts.map +1 -0
- package/lib/components/toast/ToastPortal.web.jsx +39 -0
- package/lib/components/toast/ToastPortal.web.jsx.map +1 -0
- package/lib/components/toast/index.d.ts +3 -0
- package/lib/components/toast/index.d.ts.map +1 -0
- package/lib/components/toast/index.js +3 -0
- package/lib/components/toast/index.js.map +1 -0
- package/lib/components/toast/toast-config.d.ts +14 -0
- package/lib/components/toast/toast-config.d.ts.map +1 -0
- package/lib/components/toast/toast-config.jsx +24 -0
- package/lib/components/toast/toast-config.jsx.map +1 -0
- package/lib/components/tooltip.d.ts +30 -0
- package/lib/components/tooltip.d.ts.map +1 -0
- package/lib/components/tooltip.jsx +14 -0
- package/lib/components/tooltip.jsx.map +1 -0
- package/lib/components/totp-display.d.ts +23 -0
- package/lib/components/totp-display.d.ts.map +1 -0
- package/lib/components/totp-display.jsx +132 -0
- package/lib/components/totp-display.jsx.map +1 -0
- package/lib/components/user-avatar.d.ts +18 -0
- package/lib/components/user-avatar.d.ts.map +1 -0
- package/lib/components/user-avatar.jsx +25 -0
- package/lib/components/user-avatar.jsx.map +1 -0
- package/lib/context/AppConfigContext.d.ts +56 -0
- package/lib/context/AppConfigContext.d.ts.map +1 -0
- package/lib/context/AppConfigContext.jsx +14 -0
- package/lib/context/AppConfigContext.jsx.map +1 -0
- package/lib/context/NavigationContext.d.ts +42 -0
- package/lib/context/NavigationContext.d.ts.map +1 -0
- package/lib/context/NavigationContext.jsx +53 -0
- package/lib/context/NavigationContext.jsx.map +1 -0
- package/lib/context/NotificationContext.d.ts +32 -0
- package/lib/context/NotificationContext.d.ts.map +1 -0
- package/lib/context/NotificationContext.jsx +49 -0
- package/lib/context/NotificationContext.jsx.map +1 -0
- package/lib/context/ThemeContext.d.ts +12 -0
- package/lib/context/ThemeContext.d.ts.map +1 -0
- package/lib/context/ThemeContext.jsx +66 -0
- package/lib/context/ThemeContext.jsx.map +1 -0
- package/lib/context/index.d.ts +5 -0
- package/lib/context/index.d.ts.map +1 -0
- package/lib/context/index.js +5 -0
- package/lib/context/index.js.map +1 -0
- package/lib/flags.d.ts +9 -0
- package/lib/flags.d.ts.map +1 -0
- package/lib/flags.js +37 -0
- package/lib/flags.js.map +1 -0
- package/lib/forms/form-error.d.ts +12 -0
- package/lib/forms/form-error.d.ts.map +1 -0
- package/lib/forms/form-error.jsx +14 -0
- package/lib/forms/form-error.jsx.map +1 -0
- package/lib/forms/index.d.ts +3 -0
- package/lib/forms/index.d.ts.map +1 -0
- package/lib/forms/index.js +3 -0
- package/lib/forms/index.js.map +1 -0
- package/lib/forms/shake-field.d.ts +18 -0
- package/lib/forms/shake-field.d.ts.map +1 -0
- package/lib/forms/shake-field.jsx +30 -0
- package/lib/forms/shake-field.jsx.map +1 -0
- package/lib/hooks/index.d.ts +6 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/index.js +6 -0
- package/lib/hooks/index.js.map +1 -0
- package/lib/hooks/useAuth.d.ts +109 -0
- package/lib/hooks/useAuth.d.ts.map +1 -0
- package/lib/hooks/useAuth.jsx +404 -0
- package/lib/hooks/useAuth.jsx.map +1 -0
- package/lib/hooks/useAutoLock.d.ts +22 -0
- package/lib/hooks/useAutoLock.d.ts.map +1 -0
- package/lib/hooks/useAutoLock.js +85 -0
- package/lib/hooks/useAutoLock.js.map +1 -0
- package/lib/hooks/useDebouncedValue.d.ts +19 -0
- package/lib/hooks/useDebouncedValue.d.ts.map +1 -0
- package/lib/hooks/useDebouncedValue.js +29 -0
- package/lib/hooks/useDebouncedValue.js.map +1 -0
- package/lib/hooks/useShakeAnimation.d.ts +17 -0
- package/lib/hooks/useShakeAnimation.d.ts.map +1 -0
- package/lib/hooks/useShakeAnimation.js +22 -0
- package/lib/hooks/useShakeAnimation.js.map +1 -0
- package/lib/hooks/useUploadBlobUrl.d.ts +13 -0
- package/lib/hooks/useUploadBlobUrl.d.ts.map +1 -0
- package/lib/hooks/useUploadBlobUrl.js +60 -0
- package/lib/hooks/useUploadBlobUrl.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +47 -0
- package/lib/index.js.map +1 -0
- package/lib/screens/auth/AccountPickerScreen.d.ts +24 -0
- package/lib/screens/auth/AccountPickerScreen.d.ts.map +1 -0
- package/lib/screens/auth/AccountPickerScreen.jsx +47 -0
- package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -0
- package/lib/screens/auth/AuthScreen.d.ts +59 -0
- package/lib/screens/auth/AuthScreen.d.ts.map +1 -0
- package/lib/screens/auth/AuthScreen.jsx +594 -0
- package/lib/screens/auth/AuthScreen.jsx.map +1 -0
- package/lib/screens/auth/ConsentScreen.d.ts +27 -0
- package/lib/screens/auth/ConsentScreen.d.ts.map +1 -0
- package/lib/screens/auth/ConsentScreen.jsx +55 -0
- package/lib/screens/auth/ConsentScreen.jsx.map +1 -0
- package/lib/screens/auth/ErrorScreen.d.ts +20 -0
- package/lib/screens/auth/ErrorScreen.d.ts.map +1 -0
- package/lib/screens/auth/ErrorScreen.jsx +43 -0
- package/lib/screens/auth/ErrorScreen.jsx.map +1 -0
- package/lib/screens/auth/ExpiredLinkScreen.d.ts +19 -0
- package/lib/screens/auth/ExpiredLinkScreen.d.ts.map +1 -0
- package/lib/screens/auth/ExpiredLinkScreen.jsx +38 -0
- package/lib/screens/auth/ExpiredLinkScreen.jsx.map +1 -0
- package/lib/screens/auth/ForgotPasswordScreen.d.ts +4 -0
- package/lib/screens/auth/ForgotPasswordScreen.d.ts.map +1 -0
- package/lib/screens/auth/ForgotPasswordScreen.jsx +123 -0
- package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -0
- package/lib/screens/auth/ImportDataScreen.d.ts +17 -0
- package/lib/screens/auth/ImportDataScreen.d.ts.map +1 -0
- package/lib/screens/auth/ImportDataScreen.jsx +73 -0
- package/lib/screens/auth/ImportDataScreen.jsx.map +1 -0
- package/lib/screens/auth/LoginScreen.d.ts +38 -0
- package/lib/screens/auth/LoginScreen.d.ts.map +1 -0
- package/lib/screens/auth/LoginScreen.jsx +326 -0
- package/lib/screens/auth/LoginScreen.jsx.map +1 -0
- package/lib/screens/auth/MfaChallengeScreen.d.ts +4 -0
- package/lib/screens/auth/MfaChallengeScreen.d.ts.map +1 -0
- package/lib/screens/auth/MfaChallengeScreen.jsx +139 -0
- package/lib/screens/auth/MfaChallengeScreen.jsx.map +1 -0
- package/lib/screens/auth/MfaEnrollScreen.d.ts +12 -0
- package/lib/screens/auth/MfaEnrollScreen.d.ts.map +1 -0
- package/lib/screens/auth/MfaEnrollScreen.jsx +267 -0
- package/lib/screens/auth/MfaEnrollScreen.jsx.map +1 -0
- package/lib/screens/auth/PasskeyEnrollScreen.d.ts +18 -0
- package/lib/screens/auth/PasskeyEnrollScreen.d.ts.map +1 -0
- package/lib/screens/auth/PasskeyEnrollScreen.jsx +51 -0
- package/lib/screens/auth/PasskeyEnrollScreen.jsx.map +1 -0
- package/lib/screens/auth/PrivacyPolicyScreen.d.ts +19 -0
- package/lib/screens/auth/PrivacyPolicyScreen.d.ts.map +1 -0
- package/lib/screens/auth/PrivacyPolicyScreen.jsx +143 -0
- package/lib/screens/auth/PrivacyPolicyScreen.jsx.map +1 -0
- package/lib/screens/auth/RegisterScreen.d.ts +14 -0
- package/lib/screens/auth/RegisterScreen.d.ts.map +1 -0
- package/lib/screens/auth/RegisterScreen.jsx +296 -0
- package/lib/screens/auth/RegisterScreen.jsx.map +1 -0
- package/lib/screens/auth/ResetPasswordScreen.d.ts +12 -0
- package/lib/screens/auth/ResetPasswordScreen.d.ts.map +1 -0
- package/lib/screens/auth/ResetPasswordScreen.jsx +276 -0
- package/lib/screens/auth/ResetPasswordScreen.jsx.map +1 -0
- package/lib/screens/auth/ServerConfigScreen.d.ts +18 -0
- package/lib/screens/auth/ServerConfigScreen.d.ts.map +1 -0
- package/lib/screens/auth/ServerConfigScreen.jsx +259 -0
- package/lib/screens/auth/ServerConfigScreen.jsx.map +1 -0
- package/lib/screens/auth/TermsOfServiceScreen.d.ts +30 -0
- package/lib/screens/auth/TermsOfServiceScreen.d.ts.map +1 -0
- package/lib/screens/auth/TermsOfServiceScreen.jsx +130 -0
- package/lib/screens/auth/TermsOfServiceScreen.jsx.map +1 -0
- package/lib/screens/auth/VerifyEmailScreen.d.ts +4 -0
- package/lib/screens/auth/VerifyEmailScreen.d.ts.map +1 -0
- package/lib/screens/auth/VerifyEmailScreen.jsx +249 -0
- package/lib/screens/auth/VerifyEmailScreen.jsx.map +1 -0
- package/lib/screens/auth/WelcomeScreen.d.ts +17 -0
- package/lib/screens/auth/WelcomeScreen.d.ts.map +1 -0
- package/lib/screens/auth/WelcomeScreen.jsx +58 -0
- package/lib/screens/auth/WelcomeScreen.jsx.map +1 -0
- package/lib/screens/auth/index.d.ts +19 -0
- package/lib/screens/auth/index.d.ts.map +1 -0
- package/lib/screens/auth/index.js +19 -0
- package/lib/screens/auth/index.js.map +1 -0
- package/lib/screens/index.d.ts +2 -0
- package/lib/screens/index.d.ts.map +1 -0
- package/lib/screens/index.js +2 -0
- package/lib/screens/index.js.map +1 -0
- package/lib/services/api-config.d.ts +87 -0
- package/lib/services/api-config.d.ts.map +1 -0
- package/lib/services/api-config.js +202 -0
- package/lib/services/api-config.js.map +1 -0
- package/lib/services/auth.service.d.ts +140 -0
- package/lib/services/auth.service.d.ts.map +1 -0
- package/lib/services/auth.service.js +376 -0
- package/lib/services/auth.service.js.map +1 -0
- package/lib/services/favicon.d.ts +46 -0
- package/lib/services/favicon.d.ts.map +1 -0
- package/lib/services/favicon.js +84 -0
- package/lib/services/favicon.js.map +1 -0
- package/lib/services/hibp-cache.d.ts +23 -0
- package/lib/services/hibp-cache.d.ts.map +1 -0
- package/lib/services/hibp-cache.js +56 -0
- package/lib/services/hibp-cache.js.map +1 -0
- package/lib/services/hibp.d.ts +28 -0
- package/lib/services/hibp.d.ts.map +1 -0
- package/lib/services/hibp.js +92 -0
- package/lib/services/hibp.js.map +1 -0
- package/lib/services/index.d.ts +8 -0
- package/lib/services/index.d.ts.map +1 -0
- package/lib/services/index.js +7 -0
- package/lib/services/index.js.map +1 -0
- package/lib/services/passkey.d.ts +10 -0
- package/lib/services/passkey.d.ts.map +1 -0
- package/lib/services/passkey.js +192 -0
- package/lib/services/passkey.js.map +1 -0
- package/lib/utils/api-fetch.d.ts +11 -0
- package/lib/utils/api-fetch.d.ts.map +1 -0
- package/lib/utils/api-fetch.js +52 -0
- package/lib/utils/api-fetch.js.map +1 -0
- package/lib/utils/clipboard.d.ts +31 -0
- package/lib/utils/clipboard.d.ts.map +1 -0
- package/lib/utils/clipboard.js +66 -0
- package/lib/utils/clipboard.js.map +1 -0
- package/lib/utils/cn.d.ts +9 -0
- package/lib/utils/cn.d.ts.map +1 -0
- package/lib/utils/cn.js +12 -0
- package/lib/utils/cn.js.map +1 -0
- package/lib/utils/debounce.d.ts +13 -0
- package/lib/utils/debounce.d.ts.map +1 -0
- package/lib/utils/debounce.js +28 -0
- package/lib/utils/debounce.js.map +1 -0
- package/lib/utils/error.d.ts +9 -0
- package/lib/utils/error.d.ts.map +1 -0
- package/lib/utils/error.js +11 -0
- package/lib/utils/error.js.map +1 -0
- package/lib/utils/fetch.d.ts +17 -0
- package/lib/utils/fetch.d.ts.map +1 -0
- package/lib/utils/fetch.js +29 -0
- package/lib/utils/fetch.js.map +1 -0
- package/lib/utils/http-error.d.ts +41 -0
- package/lib/utils/http-error.d.ts.map +1 -0
- package/lib/utils/http-error.js +63 -0
- package/lib/utils/http-error.js.map +1 -0
- package/lib/utils/index.d.ts +11 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +11 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/palette.d.ts +46 -0
- package/lib/utils/palette.d.ts.map +1 -0
- package/lib/utils/palette.js +75 -0
- package/lib/utils/palette.js.map +1 -0
- package/lib/utils/password.d.ts +20 -0
- package/lib/utils/password.d.ts.map +1 -0
- package/lib/utils/password.js +51 -0
- package/lib/utils/password.js.map +1 -0
- package/lib/utils/settings-storage.d.ts +3 -0
- package/lib/utils/settings-storage.d.ts.map +1 -0
- package/lib/utils/settings-storage.js +62 -0
- package/lib/utils/settings-storage.js.map +1 -0
- package/lib/utils/validation.d.ts +29 -0
- package/lib/utils/validation.d.ts.map +1 -0
- package/lib/utils/validation.js +58 -0
- package/lib/utils/validation.js.map +1 -0
- package/package.json +204 -0
- package/src/components/__tests__/data-table.test.ts +94 -0
- package/src/components/__tests__/pagination.test.ts +73 -0
- package/src/components/__tests__/stat-card.test.ts +21 -0
- package/src/components/auth/LockOverlay.tsx +122 -0
- package/src/components/auth/PasswordStrengthMeter.tsx +96 -0
- package/src/components/auth/ProtectedRoute.tsx +91 -0
- package/src/components/auth/PwnedPasswordWarning.tsx +54 -0
- package/src/components/auth/index.ts +10 -0
- package/src/components/bottom-nav.tsx +99 -0
- package/src/components/button.tsx +144 -0
- package/src/components/card.tsx +82 -0
- package/src/components/checkbox.tsx +48 -0
- package/src/components/connection-settings.tsx +583 -0
- package/src/components/context-menu.tsx +95 -0
- package/src/components/data-table.tsx +223 -0
- package/src/components/dialog.tsx +376 -0
- package/src/components/dropdown-menu.tsx +120 -0
- package/src/components/entropy-boot-splash.tsx +114 -0
- package/src/components/entropy-mark-animated.tsx +260 -0
- package/src/components/entropy-mark-geometry.ts +11 -0
- package/src/components/entropy-mark-sequential.tsx +132 -0
- package/src/components/entropy-mark.tsx +135 -0
- package/src/components/error-boundary.tsx +70 -0
- package/src/components/index.ts +156 -0
- package/src/components/input.tsx +246 -0
- package/src/components/item-icon.tsx +111 -0
- package/src/components/label.tsx +25 -0
- package/src/components/legal-dialog.tsx +47 -0
- package/src/components/license-gate.tsx +28 -0
- package/src/components/logo.tsx +34 -0
- package/src/components/onboarding-wizard.tsx +353 -0
- package/src/components/pagination.tsx +154 -0
- package/src/components/password-generator.tsx +393 -0
- package/src/components/pin-pad.tsx +156 -0
- package/src/components/popover.tsx +38 -0
- package/src/components/profile/ProfileDialog.tsx +129 -0
- package/src/components/profile/ProfileTab.tsx +151 -0
- package/src/components/profile/SecurityTab.tsx +229 -0
- package/src/components/profile/index.ts +3 -0
- package/src/components/segmented-toggle.tsx +119 -0
- package/src/components/separator.tsx +41 -0
- package/src/components/server-settings.tsx +442 -0
- package/src/components/settings/AccountSection.tsx +76 -0
- package/src/components/settings/DangerZoneSection.tsx +37 -0
- package/src/components/settings/GeneralSection.tsx +39 -0
- package/src/components/settings/SupportSection.tsx +53 -0
- package/src/components/settings/index.ts +4 -0
- package/src/components/settings-row.tsx +126 -0
- package/src/components/skeleton.tsx +48 -0
- package/src/components/social-connections.tsx +55 -0
- package/src/components/stat-card.tsx +64 -0
- package/src/components/tag-chip.tsx +78 -0
- package/src/components/tag-multi-select.tsx +136 -0
- package/src/components/text.tsx +77 -0
- package/src/components/toast/ToastPortal.tsx +16 -0
- package/src/components/toast/ToastPortal.web.tsx +47 -0
- package/src/components/toast/index.ts +2 -0
- package/src/components/toast/toast-config.tsx +31 -0
- package/src/components/tooltip.tsx +30 -0
- package/src/components/totp-display.tsx +207 -0
- package/src/components/user-avatar.tsx +46 -0
- package/src/context/AppConfigContext.tsx +76 -0
- package/src/context/NavigationContext.tsx +75 -0
- package/src/context/NotificationContext.tsx +69 -0
- package/src/context/ThemeContext.tsx +83 -0
- package/src/context/__tests__/NavigationContext.test.tsx +53 -0
- package/src/context/index.ts +24 -0
- package/src/flags.ts +44 -0
- package/src/forms/form-error.tsx +20 -0
- package/src/forms/index.ts +2 -0
- package/src/forms/shake-field.tsx +52 -0
- package/src/hooks/__tests__/useUploadBlobUrl.test.tsx +77 -0
- package/src/hooks/index.ts +18 -0
- package/src/hooks/useAuth.tsx +567 -0
- package/src/hooks/useAutoLock.ts +110 -0
- package/src/hooks/useDebouncedValue.ts +32 -0
- package/src/hooks/useShakeAnimation.ts +42 -0
- package/src/hooks/useUploadBlobUrl.ts +75 -0
- package/src/index.ts +48 -0
- package/src/screens/auth/AccountPickerScreen.tsx +103 -0
- package/src/screens/auth/AuthScreen.tsx +941 -0
- package/src/screens/auth/ConsentScreen.tsx +118 -0
- package/src/screens/auth/ErrorScreen.tsx +96 -0
- package/src/screens/auth/ExpiredLinkScreen.tsx +87 -0
- package/src/screens/auth/ForgotPasswordScreen.tsx +182 -0
- package/src/screens/auth/ImportDataScreen.tsx +112 -0
- package/src/screens/auth/LoginScreen.tsx +484 -0
- package/src/screens/auth/MfaChallengeScreen.tsx +216 -0
- package/src/screens/auth/MfaEnrollScreen.tsx +388 -0
- package/src/screens/auth/PasskeyEnrollScreen.tsx +96 -0
- package/src/screens/auth/PrivacyPolicyScreen.tsx +249 -0
- package/src/screens/auth/RegisterScreen.tsx +430 -0
- package/src/screens/auth/ResetPasswordScreen.tsx +377 -0
- package/src/screens/auth/ServerConfigScreen.tsx +388 -0
- package/src/screens/auth/TermsOfServiceScreen.tsx +236 -0
- package/src/screens/auth/VerifyEmailScreen.tsx +310 -0
- package/src/screens/auth/WelcomeScreen.tsx +78 -0
- package/src/screens/auth/index.ts +36 -0
- package/src/screens/index.ts +1 -0
- package/src/services/__tests__/api-config.test.ts +67 -0
- package/src/services/__tests__/favicon.test.ts +88 -0
- package/src/services/__tests__/hibp-cache.test.ts +37 -0
- package/src/services/__tests__/hibp.test.ts +39 -0
- package/src/services/api-config.ts +260 -0
- package/src/services/auth.service.ts +498 -0
- package/src/services/favicon.ts +102 -0
- package/src/services/hibp-cache.ts +71 -0
- package/src/services/hibp.ts +120 -0
- package/src/services/index.ts +52 -0
- package/src/services/passkey.ts +213 -0
- package/src/types/external.d.ts +24 -0
- package/src/utils/__tests__/clipboard.test.ts +59 -0
- package/src/utils/__tests__/cn.test.ts +24 -0
- package/src/utils/__tests__/debounce.test.ts +42 -0
- package/src/utils/__tests__/error.test.ts +18 -0
- package/src/utils/__tests__/http-error.test.ts +33 -0
- package/src/utils/__tests__/palette.test.ts +22 -0
- package/src/utils/__tests__/password.test.ts +55 -0
- package/src/utils/__tests__/settings-storage.test.ts +15 -0
- package/src/utils/__tests__/validation.test.ts +52 -0
- package/src/utils/api-fetch.ts +61 -0
- package/src/utils/clipboard.ts +69 -0
- package/src/utils/cn.ts +12 -0
- package/src/utils/debounce.ts +33 -0
- package/src/utils/error.ts +10 -0
- package/src/utils/fetch.ts +33 -0
- package/src/utils/http-error.ts +81 -0
- package/src/utils/index.ts +23 -0
- package/src/utils/palette.ts +105 -0
- package/src/utils/password.ts +64 -0
- package/src/utils/settings-storage.ts +68 -0
- package/src/utils/validation.ts +71 -0
- package/styles/globals.css +262 -0
- package/tailwind-preset.js +163 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Pressable, Switch, View } from "react-native";
|
|
3
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
4
|
+
import { useColorScheme } from "nativewind";
|
|
5
|
+
import { cn } from "../utils/cn";
|
|
6
|
+
import { MutedText, Text } from "./text";
|
|
7
|
+
|
|
8
|
+
export interface SettingsRowProps {
|
|
9
|
+
icon: keyof typeof Ionicons.glyphMap;
|
|
10
|
+
label: string;
|
|
11
|
+
subtitle?: string;
|
|
12
|
+
onPress?: () => void;
|
|
13
|
+
showChevron?: boolean;
|
|
14
|
+
toggle?: boolean;
|
|
15
|
+
value?: boolean;
|
|
16
|
+
onValueChange?: (value: boolean) => void;
|
|
17
|
+
destructive?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Horizontal row used inside settings dialogs / panels. Combines an icon
|
|
22
|
+
* tile, label + optional subtitle, and a trailing switch *or* chevron.
|
|
23
|
+
* `destructive` recolors the label + icon to the destructive token.
|
|
24
|
+
*/
|
|
25
|
+
export function SettingsRow({
|
|
26
|
+
icon,
|
|
27
|
+
label,
|
|
28
|
+
subtitle,
|
|
29
|
+
onPress,
|
|
30
|
+
showChevron,
|
|
31
|
+
toggle,
|
|
32
|
+
value,
|
|
33
|
+
onValueChange,
|
|
34
|
+
destructive,
|
|
35
|
+
}: SettingsRowProps) {
|
|
36
|
+
const { colorScheme } = useColorScheme();
|
|
37
|
+
const isDark = colorScheme === "dark";
|
|
38
|
+
|
|
39
|
+
const Wrapper = onPress ? Pressable : View;
|
|
40
|
+
// Toggle-mode rows tap-to-switch and also surface the switch
|
|
41
|
+
// itself — both surfaces need the label as their accessible name
|
|
42
|
+
// so a screen reader doesn't announce "Switch, on" with no
|
|
43
|
+
// context. The Switch carries `accessibilityState` so AT can
|
|
44
|
+
// announce the on/off state.
|
|
45
|
+
const wrapperProps = onPress
|
|
46
|
+
? ({
|
|
47
|
+
onPress,
|
|
48
|
+
accessibilityRole: "button" as const,
|
|
49
|
+
accessibilityLabel: label,
|
|
50
|
+
...(subtitle ? { accessibilityHint: subtitle } : {}),
|
|
51
|
+
} as const)
|
|
52
|
+
: {};
|
|
53
|
+
|
|
54
|
+
let iconColor: string;
|
|
55
|
+
if (destructive) {
|
|
56
|
+
iconColor = "#EF4444";
|
|
57
|
+
} else if (isDark) {
|
|
58
|
+
iconColor = "#FFFFFF";
|
|
59
|
+
} else {
|
|
60
|
+
iconColor = "#000000";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Wrapper
|
|
65
|
+
className="flex-row items-center border-b border-border px-3 py-3 last:border-b-0 dark:border-neutral-800"
|
|
66
|
+
{...wrapperProps}
|
|
67
|
+
>
|
|
68
|
+
<View className="mr-3 h-8 w-8 items-center justify-center rounded-md bg-secondary dark:bg-neutral-800">
|
|
69
|
+
<Ionicons name={icon} size={16} color={iconColor} />
|
|
70
|
+
</View>
|
|
71
|
+
<View className="flex-1">
|
|
72
|
+
<Text
|
|
73
|
+
className={cn(
|
|
74
|
+
"text-sm font-medium",
|
|
75
|
+
destructive ? "text-destructive" : "text-foreground"
|
|
76
|
+
)}
|
|
77
|
+
>
|
|
78
|
+
{label}
|
|
79
|
+
</Text>
|
|
80
|
+
{subtitle ? <MutedText className="text-xs">{subtitle}</MutedText> : null}
|
|
81
|
+
</View>
|
|
82
|
+
{toggle ? (
|
|
83
|
+
<Switch
|
|
84
|
+
value={value}
|
|
85
|
+
onValueChange={onValueChange}
|
|
86
|
+
accessibilityLabel={label}
|
|
87
|
+
accessibilityState={{ checked: value === true }}
|
|
88
|
+
trackColor={{
|
|
89
|
+
false: isDark ? "#404040" : "#D4D4D4",
|
|
90
|
+
true: isDark ? "#FFFFFF" : "#000000",
|
|
91
|
+
}}
|
|
92
|
+
thumbColor={isDark ? "#000000" : "#FFFFFF"}
|
|
93
|
+
/>
|
|
94
|
+
) : null}
|
|
95
|
+
{showChevron ? (
|
|
96
|
+
<Ionicons name="chevron-forward" size={16} color={isDark ? "#525252" : "#A3A3A3"} />
|
|
97
|
+
) : null}
|
|
98
|
+
</Wrapper>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface SettingsSectionProps {
|
|
103
|
+
title: string;
|
|
104
|
+
destructive?: boolean;
|
|
105
|
+
children: React.ReactNode;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Group container for `SettingsRow`s. Renders the section title + a
|
|
109
|
+
* bordered card around its children. */
|
|
110
|
+
export function SettingsSection({ title, destructive, children }: SettingsSectionProps) {
|
|
111
|
+
return (
|
|
112
|
+
<View>
|
|
113
|
+
<Text
|
|
114
|
+
className={cn(
|
|
115
|
+
"mb-2 text-xs font-semibold tracking-wider",
|
|
116
|
+
destructive ? "text-destructive" : "text-muted-foreground"
|
|
117
|
+
)}
|
|
118
|
+
>
|
|
119
|
+
{title}
|
|
120
|
+
</Text>
|
|
121
|
+
<View className="rounded-lg border border-border bg-card dark:border-neutral-800 dark:bg-neutral-900/50">
|
|
122
|
+
{children}
|
|
123
|
+
</View>
|
|
124
|
+
</View>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import { type ViewProps } from "react-native";
|
|
3
|
+
import Animated, {
|
|
4
|
+
cancelAnimation,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
useSharedValue,
|
|
7
|
+
withRepeat,
|
|
8
|
+
withSequence,
|
|
9
|
+
withTiming,
|
|
10
|
+
} from "react-native-reanimated";
|
|
11
|
+
import { useColorScheme } from "nativewind";
|
|
12
|
+
import { cn } from "../utils/cn";
|
|
13
|
+
|
|
14
|
+
interface SkeletonProps extends ViewProps {
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const Skeleton = React.memo(function Skeleton({ className, ...props }: SkeletonProps) {
|
|
19
|
+
const { colorScheme } = useColorScheme();
|
|
20
|
+
const isDark = colorScheme === "dark";
|
|
21
|
+
const opacity = useSharedValue(0.4);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
opacity.value = withRepeat(
|
|
25
|
+
withSequence(withTiming(1, { duration: 750 }), withTiming(0.4, { duration: 750 })),
|
|
26
|
+
-1
|
|
27
|
+
);
|
|
28
|
+
// Cancel the infinite repeat on unmount. Without this the worklet
|
|
29
|
+
// keeps running until the shared value is GC'd, which in long
|
|
30
|
+
// FlashList re-cycles (skeleton → loaded item → skeleton) leaves
|
|
31
|
+
// orphan animations alive.
|
|
32
|
+
return () => {
|
|
33
|
+
cancelAnimation(opacity);
|
|
34
|
+
};
|
|
35
|
+
}, [opacity]);
|
|
36
|
+
|
|
37
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
38
|
+
opacity: opacity.value,
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Animated.View
|
|
43
|
+
className={cn("rounded-md", isDark ? "bg-neutral-800" : "bg-neutral-200", className)}
|
|
44
|
+
style={animatedStyle}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Pressable, View } from "react-native";
|
|
3
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
4
|
+
import { useColorScheme } from "nativewind";
|
|
5
|
+
|
|
6
|
+
export interface SocialConnectionsProps {
|
|
7
|
+
onApple?: () => void;
|
|
8
|
+
onGoogle?: () => void;
|
|
9
|
+
onMicrosoft?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const PROVIDERS = [
|
|
13
|
+
{ key: "apple", icon: "logo-apple" as const },
|
|
14
|
+
{ key: "google", icon: "logo-google" as const },
|
|
15
|
+
{ key: "microsoft", icon: "logo-microsoft" as const },
|
|
16
|
+
] as const;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Three-up button row for federated sign-in (Apple / Google / Microsoft).
|
|
20
|
+
* Each handler is optional — providers with no handler still render but
|
|
21
|
+
* fire no action; this matches the "coming soon" UX the consumer apps
|
|
22
|
+
* use during the staged rollout.
|
|
23
|
+
*/
|
|
24
|
+
export function SocialConnections({
|
|
25
|
+
onApple,
|
|
26
|
+
onGoogle,
|
|
27
|
+
onMicrosoft,
|
|
28
|
+
}: SocialConnectionsProps): React.JSX.Element {
|
|
29
|
+
const { colorScheme } = useColorScheme();
|
|
30
|
+
const isDark = colorScheme === "dark";
|
|
31
|
+
|
|
32
|
+
const handlers: Record<string, (() => void) | undefined> = {
|
|
33
|
+
apple: onApple,
|
|
34
|
+
google: onGoogle,
|
|
35
|
+
microsoft: onMicrosoft,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<View className="w-full flex-row items-center justify-center gap-3">
|
|
40
|
+
{PROVIDERS.map(({ key, icon }) => (
|
|
41
|
+
<Pressable
|
|
42
|
+
key={key}
|
|
43
|
+
onPress={handlers[key]}
|
|
44
|
+
className="h-12 w-12 items-center justify-center rounded-lg border border-border dark:border-neutral-600 active:opacity-70"
|
|
45
|
+
accessibilityRole="button"
|
|
46
|
+
accessibilityLabel={`Sign in with ${key}`}
|
|
47
|
+
>
|
|
48
|
+
<Ionicons name={icon} size={22} color={isDark ? "#FFFFFF" : "#000000"} />
|
|
49
|
+
</Pressable>
|
|
50
|
+
))}
|
|
51
|
+
</View>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default SocialConnections;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
import { Card } from "./card";
|
|
5
|
+
import { MutedText, Text } from "./text";
|
|
6
|
+
|
|
7
|
+
/** Direction of a stat's delta — drives the trend colour. */
|
|
8
|
+
export type StatTrend = "up" | "down" | "neutral";
|
|
9
|
+
|
|
10
|
+
export interface StatCardProps {
|
|
11
|
+
/** Short label describing the metric (e.g. "Active users"). */
|
|
12
|
+
label: string;
|
|
13
|
+
/** The headline value. Numbers are rendered as-is; format upstream. */
|
|
14
|
+
value: string | number;
|
|
15
|
+
/** Optional leading icon slot (e.g. a lucide / Ionicons element). */
|
|
16
|
+
icon?: React.ReactNode;
|
|
17
|
+
/** Optional change indicator text (e.g. "+12% this week"). */
|
|
18
|
+
delta?: string;
|
|
19
|
+
/** Trend direction — colours the delta. Defaults to "neutral". */
|
|
20
|
+
trend?: StatTrend;
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Trend → delta text colour. Tokens only (success/destructive/muted) so each
|
|
25
|
+
// consuming app's palette flows through; no hardcoded hex.
|
|
26
|
+
const trendClass: Record<StatTrend, string> = {
|
|
27
|
+
up: "text-success",
|
|
28
|
+
down: "text-destructive",
|
|
29
|
+
neutral: "text-muted-foreground dark:text-neutral-400",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Map a trend direction to its delta text colour class. Pure so the
|
|
34
|
+
* trend→colour contract is unit-testable without a render host.
|
|
35
|
+
*/
|
|
36
|
+
export function trendColorClass(trend: StatTrend): string {
|
|
37
|
+
return trendClass[trend];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Dashboard stat tile: a label, a prominent value, an optional leading icon,
|
|
42
|
+
* and an optional trend/delta line. Composes `Card` + `Text` so the surface,
|
|
43
|
+
* border, and typography stay in lockstep with the rest of the system rather
|
|
44
|
+
* than duplicating styles.
|
|
45
|
+
*/
|
|
46
|
+
export function StatCard({
|
|
47
|
+
label,
|
|
48
|
+
value,
|
|
49
|
+
icon,
|
|
50
|
+
delta,
|
|
51
|
+
trend = "neutral",
|
|
52
|
+
className,
|
|
53
|
+
}: StatCardProps) {
|
|
54
|
+
return (
|
|
55
|
+
<Card className={cn("p-5", className)} accessibilityRole="summary">
|
|
56
|
+
<View className="flex-row items-start justify-between">
|
|
57
|
+
<MutedText className="text-sm">{label}</MutedText>
|
|
58
|
+
{icon ? <View className="ml-2">{icon}</View> : null}
|
|
59
|
+
</View>
|
|
60
|
+
<Text className="mt-2 text-3xl font-bold text-foreground dark:text-white">{value}</Text>
|
|
61
|
+
{delta ? <Text className={cn("mt-1 text-xs", trendColorClass(trend))}>{delta}</Text> : null}
|
|
62
|
+
</Card>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Pressable, View } from "react-native";
|
|
3
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
4
|
+
import { useColorScheme } from "nativewind";
|
|
5
|
+
import { Text } from "./text";
|
|
6
|
+
|
|
7
|
+
export interface TagChipProps {
|
|
8
|
+
name: string;
|
|
9
|
+
/** Accent color for the tag. Accepts `rgb(r, g, b)` or `#rrggbb`. */
|
|
10
|
+
color?: string | null;
|
|
11
|
+
selected?: boolean;
|
|
12
|
+
onPress?: () => void;
|
|
13
|
+
size?: "sm" | "md";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Compact chip used to display a tag — icon + name with a soft accent
|
|
18
|
+
* border. When `selected` is true the border + background pick up the
|
|
19
|
+
* accent at full opacity / 18% fill respectively.
|
|
20
|
+
*/
|
|
21
|
+
export function TagChip({ name, color, selected, onPress, size = "sm" }: TagChipProps) {
|
|
22
|
+
const { colorScheme } = useColorScheme();
|
|
23
|
+
const isDark = colorScheme === "dark";
|
|
24
|
+
const accent = color ?? (isDark ? "rgb(163, 163, 163)" : "rgb(115, 115, 115)");
|
|
25
|
+
const dims =
|
|
26
|
+
size === "sm"
|
|
27
|
+
? { paddingH: 8, paddingV: 3, fontSize: 11, iconSize: 10 }
|
|
28
|
+
: { paddingH: 10, paddingV: 5, fontSize: 12, iconSize: 12 };
|
|
29
|
+
|
|
30
|
+
const inner = (
|
|
31
|
+
<View
|
|
32
|
+
style={{
|
|
33
|
+
flexDirection: "row",
|
|
34
|
+
alignItems: "center",
|
|
35
|
+
gap: 4,
|
|
36
|
+
paddingHorizontal: dims.paddingH,
|
|
37
|
+
paddingVertical: dims.paddingV,
|
|
38
|
+
borderRadius: 6,
|
|
39
|
+
borderWidth: 1,
|
|
40
|
+
borderColor: selected ? accent : withAlpha(accent, 0.33),
|
|
41
|
+
backgroundColor: selected ? withAlpha(accent, 0.18) : "transparent",
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<Ionicons name="pricetag-outline" size={dims.iconSize} color={accent} />
|
|
45
|
+
<Text className="text-foreground" style={{ fontSize: dims.fontSize }}>
|
|
46
|
+
{name}
|
|
47
|
+
</Text>
|
|
48
|
+
</View>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (!onPress) {
|
|
52
|
+
return inner;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Pressable
|
|
57
|
+
onPress={onPress}
|
|
58
|
+
hitSlop={4}
|
|
59
|
+
style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1 })}
|
|
60
|
+
>
|
|
61
|
+
{inner}
|
|
62
|
+
</Pressable>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Tints an `rgb(...)` or `#rrggbb` color with the given alpha 0..1. */
|
|
67
|
+
export function withAlpha(color: string, alpha: number): string {
|
|
68
|
+
const rgb = color.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i);
|
|
69
|
+
if (rgb) {
|
|
70
|
+
return `rgba(${rgb[1]}, ${rgb[2]}, ${rgb[3]}, ${alpha})`;
|
|
71
|
+
}
|
|
72
|
+
const hex = color.match(/^#([0-9a-f]{6})$/i);
|
|
73
|
+
if (hex) {
|
|
74
|
+
const h = hex[1] ?? "";
|
|
75
|
+
return `rgba(${parseInt(h.slice(0, 2), 16)}, ${parseInt(h.slice(2, 4), 16)}, ${parseInt(h.slice(4, 6), 16)}, ${alpha})`;
|
|
76
|
+
}
|
|
77
|
+
return color;
|
|
78
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React, { useMemo, useState } from "react";
|
|
2
|
+
import { Pressable, ScrollView, View } from "react-native";
|
|
3
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
4
|
+
import { useColorScheme } from "nativewind";
|
|
5
|
+
import { MutedText, Text } from "./text";
|
|
6
|
+
import { SearchInput } from "./input";
|
|
7
|
+
|
|
8
|
+
export interface TagOption {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
/** Accent color used for the chip border + icon (defaults to muted). */
|
|
12
|
+
color?: string | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TagMultiSelectProps<T extends TagOption> {
|
|
16
|
+
tags: T[];
|
|
17
|
+
selectedIds: string[];
|
|
18
|
+
onToggle: (tagId: string) => void;
|
|
19
|
+
/** Resolves the Ionicons glyph for a tag. Defaults to `pricetag`. */
|
|
20
|
+
iconFor?: (tag: T) => keyof typeof Ionicons.glyphMap;
|
|
21
|
+
/** Pixel cap on the scrollable list height. Defaults to 240. */
|
|
22
|
+
listMaxHeight?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Searchable multi-select picker for tags / labels. Designed for lists
|
|
27
|
+
* that can be in the thousands — the list scrolls, fuzzy filters by
|
|
28
|
+
* name, and selected items appear as removable chips above the search.
|
|
29
|
+
*
|
|
30
|
+
* Generic over the tag shape; pass `iconFor` to map your tag's icon
|
|
31
|
+
* value onto an Ionicons glyph.
|
|
32
|
+
*/
|
|
33
|
+
export function TagMultiSelect<T extends TagOption>({
|
|
34
|
+
tags,
|
|
35
|
+
selectedIds,
|
|
36
|
+
onToggle,
|
|
37
|
+
iconFor,
|
|
38
|
+
listMaxHeight = 240,
|
|
39
|
+
}: TagMultiSelectProps<T>) {
|
|
40
|
+
const { colorScheme } = useColorScheme();
|
|
41
|
+
const isDark = colorScheme === "dark";
|
|
42
|
+
const mutedColor = isDark ? "#A3A3A3" : "#737373";
|
|
43
|
+
|
|
44
|
+
const [query, setQuery] = useState("");
|
|
45
|
+
|
|
46
|
+
const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds]);
|
|
47
|
+
|
|
48
|
+
const selectedTags = useMemo(
|
|
49
|
+
() => selectedIds.map((id) => tags.find((t) => t.id === id)).filter((t): t is T => !!t),
|
|
50
|
+
[selectedIds, tags]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const filteredTags = useMemo(() => {
|
|
54
|
+
const q = query.trim().toLowerCase();
|
|
55
|
+
if (!q) {
|
|
56
|
+
return tags;
|
|
57
|
+
}
|
|
58
|
+
return tags.filter((t) => t.name.toLowerCase().includes(q));
|
|
59
|
+
}, [query, tags]);
|
|
60
|
+
|
|
61
|
+
const resolveIcon = (tag: T): keyof typeof Ionicons.glyphMap =>
|
|
62
|
+
iconFor ? iconFor(tag) : "pricetag";
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<View className="gap-2">
|
|
66
|
+
{selectedTags.length > 0 && (
|
|
67
|
+
<View className="flex-row flex-wrap gap-1.5">
|
|
68
|
+
{selectedTags.map((tag) => {
|
|
69
|
+
const accent = tag.color ?? mutedColor;
|
|
70
|
+
return (
|
|
71
|
+
<Pressable
|
|
72
|
+
key={tag.id}
|
|
73
|
+
onPress={() => onToggle(tag.id)}
|
|
74
|
+
className="flex-row items-center gap-1 rounded-md border px-2 py-1 active:opacity-70"
|
|
75
|
+
style={{ borderColor: `${accent}55` }}
|
|
76
|
+
accessibilityLabel={`Remove tag ${tag.name}`}
|
|
77
|
+
accessibilityRole="button"
|
|
78
|
+
>
|
|
79
|
+
<Ionicons name={resolveIcon(tag)} size={12} color={accent} />
|
|
80
|
+
<Text className="text-xs text-foreground">{tag.name}</Text>
|
|
81
|
+
<Ionicons name="close-circle" size={14} color={mutedColor} />
|
|
82
|
+
</Pressable>
|
|
83
|
+
);
|
|
84
|
+
})}
|
|
85
|
+
</View>
|
|
86
|
+
)}
|
|
87
|
+
|
|
88
|
+
<SearchInput
|
|
89
|
+
value={query}
|
|
90
|
+
onChangeText={setQuery}
|
|
91
|
+
placeholder={`Search ${tags.length} tag${tags.length === 1 ? "" : "s"}...`}
|
|
92
|
+
accessibilityLabel="Search tags"
|
|
93
|
+
/>
|
|
94
|
+
|
|
95
|
+
<View
|
|
96
|
+
className="rounded-lg border border-border dark:border-neutral-800"
|
|
97
|
+
style={{ maxHeight: listMaxHeight }}
|
|
98
|
+
>
|
|
99
|
+
{filteredTags.length === 0 ? (
|
|
100
|
+
<View className="p-4">
|
|
101
|
+
<MutedText className="text-center text-sm italic">
|
|
102
|
+
{query ? `No tags match "${query}"` : "No tags yet"}
|
|
103
|
+
</MutedText>
|
|
104
|
+
</View>
|
|
105
|
+
) : (
|
|
106
|
+
<ScrollView keyboardShouldPersistTaps="handled">
|
|
107
|
+
{filteredTags.map((tag) => {
|
|
108
|
+
const selected = selectedSet.has(tag.id);
|
|
109
|
+
const accent = tag.color ?? mutedColor;
|
|
110
|
+
const selectedColor = isDark ? "#FFFFFF" : "#000000";
|
|
111
|
+
const checkboxColor = selected ? selectedColor : mutedColor;
|
|
112
|
+
return (
|
|
113
|
+
<Pressable
|
|
114
|
+
key={tag.id}
|
|
115
|
+
onPress={() => onToggle(tag.id)}
|
|
116
|
+
accessibilityLabel={`${selected ? "Deselect" : "Select"} tag ${tag.name}`}
|
|
117
|
+
accessibilityRole="checkbox"
|
|
118
|
+
accessibilityState={{ checked: selected }}
|
|
119
|
+
className="flex-row items-center gap-2 px-3 py-2 active:bg-neutral-100 dark:active:bg-neutral-800"
|
|
120
|
+
>
|
|
121
|
+
<Ionicons
|
|
122
|
+
name={selected ? "checkbox" : "square-outline"}
|
|
123
|
+
size={18}
|
|
124
|
+
color={checkboxColor}
|
|
125
|
+
/>
|
|
126
|
+
<Ionicons name={resolveIcon(tag)} size={14} color={accent} />
|
|
127
|
+
<Text className="flex-1 text-sm text-foreground">{tag.name}</Text>
|
|
128
|
+
</Pressable>
|
|
129
|
+
);
|
|
130
|
+
})}
|
|
131
|
+
</ScrollView>
|
|
132
|
+
)}
|
|
133
|
+
</View>
|
|
134
|
+
</View>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text as RNText, type TextProps as RNTextProps } from "react-native";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
|
|
5
|
+
export interface TextProps extends RNTextProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Text component with NativeWind styling. Provides consistent typography
|
|
12
|
+
* across consumer apps with dark-mode support.
|
|
13
|
+
*/
|
|
14
|
+
export function Text({ className, children, ...props }: TextProps) {
|
|
15
|
+
return (
|
|
16
|
+
<RNText className={cn("text-base text-foreground dark:text-white", className)} {...props}>
|
|
17
|
+
{children}
|
|
18
|
+
</RNText>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Heading variant with larger size and bold weight. */
|
|
23
|
+
export function Heading({ className, children, ...props }: TextProps) {
|
|
24
|
+
return (
|
|
25
|
+
<RNText
|
|
26
|
+
className={cn("text-2xl font-bold text-foreground dark:text-white", className)}
|
|
27
|
+
{...props}
|
|
28
|
+
>
|
|
29
|
+
{children}
|
|
30
|
+
</RNText>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Label variant for form labels and small text. */
|
|
35
|
+
export function Label({ className, children, ...props }: TextProps) {
|
|
36
|
+
return (
|
|
37
|
+
<RNText
|
|
38
|
+
className={cn("text-sm text-muted-foreground dark:text-neutral-400", className)}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
</RNText>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Muted text variant for less prominent content. */
|
|
47
|
+
export function MutedText({ className, children, ...props }: TextProps) {
|
|
48
|
+
return (
|
|
49
|
+
<RNText
|
|
50
|
+
className={cn("text-base text-muted-foreground dark:text-neutral-400", className)}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
</RNText>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Monospace text variant for codes, passwords, and technical content. */
|
|
59
|
+
export function MonoText({ className, children, ...props }: TextProps) {
|
|
60
|
+
return (
|
|
61
|
+
<RNText
|
|
62
|
+
className={cn("font-mono text-base text-foreground dark:text-white", className)}
|
|
63
|
+
{...props}
|
|
64
|
+
>
|
|
65
|
+
{children}
|
|
66
|
+
</RNText>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Error text variant for validation messages. */
|
|
71
|
+
export function ErrorText({ className, children, ...props }: TextProps) {
|
|
72
|
+
return (
|
|
73
|
+
<RNText className={cn("text-sm text-destructive dark:text-red-400", className)} {...props}>
|
|
74
|
+
{children}
|
|
75
|
+
</RNText>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import Toast from "react-native-toast-message";
|
|
3
|
+
import { toastConfig } from "./toast-config";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Native toast portal — renders `Toast` directly in the component tree.
|
|
7
|
+
* Toast uses absolute positioning + a high zIndex to float above most
|
|
8
|
+
* content. Note: Toast will not appear above a React Native `Modal`
|
|
9
|
+
* (dialogs); wrapping in a Modal to force that would block all touches
|
|
10
|
+
* on the screen.
|
|
11
|
+
*/
|
|
12
|
+
export function ToastPortal(): React.JSX.Element {
|
|
13
|
+
return <Toast config={toastConfig} position="top" visibilityTime={3000} topOffset={50} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default ToastPortal;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
|
+
import Toast from "react-native-toast-message";
|
|
4
|
+
import { toastConfig } from "./toast-config";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Web toast portal — mounts `Toast` into a div appended directly to
|
|
8
|
+
* `document.body` so it renders above React Native Web's `Modal`
|
|
9
|
+
* portals (which also target body).
|
|
10
|
+
*
|
|
11
|
+
* The container div uses `pointer-events:none` so the transparent
|
|
12
|
+
* overlay never intercepts clicks. HTML `pointer-events` does NOT
|
|
13
|
+
* inherit to children (unlike SVG), so the visible toast pill itself
|
|
14
|
+
* remains fully interactive via its own event handlers.
|
|
15
|
+
*/
|
|
16
|
+
export function ToastPortal(): React.JSX.Element | null {
|
|
17
|
+
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
18
|
+
|
|
19
|
+
if (!containerRef.current) {
|
|
20
|
+
const div = document.createElement("div");
|
|
21
|
+
div.style.cssText =
|
|
22
|
+
"position:fixed;top:0;bottom:0;right:0;left:var(--sidebar-width,0px);z-index:999999;pointer-events:none;";
|
|
23
|
+
containerRef.current = div;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const container = containerRef.current;
|
|
28
|
+
if (!container) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
document.body.appendChild(container);
|
|
32
|
+
return () => {
|
|
33
|
+
document.body.removeChild(container);
|
|
34
|
+
};
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
if (!containerRef.current) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return createPortal(
|
|
42
|
+
<Toast config={toastConfig} position="top" visibilityTime={3000} topOffset={50} />,
|
|
43
|
+
containerRef.current
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default ToastPortal;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import type { BaseToastProps } from "react-native-toast-message";
|
|
4
|
+
import { Text } from "../text";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared toast renderer config for `react-native-toast-message`. Three
|
|
8
|
+
* variants (success / error / info) rendered as pill cards in the
|
|
9
|
+
* package's standard token palette. Used by `ToastPortal` on both
|
|
10
|
+
* native and web.
|
|
11
|
+
*/
|
|
12
|
+
export const toastConfig = {
|
|
13
|
+
success: ({ text1, text2 }: BaseToastProps) => (
|
|
14
|
+
<View className="self-center bg-green-500 dark:bg-green-600 px-4 py-3 rounded-lg mx-4 mt-4">
|
|
15
|
+
<Text className="text-white font-semibold">{text1}</Text>
|
|
16
|
+
{text2 && <Text className="text-white/80 text-sm mt-1">{text2}</Text>}
|
|
17
|
+
</View>
|
|
18
|
+
),
|
|
19
|
+
error: ({ text1, text2 }: BaseToastProps) => (
|
|
20
|
+
<View className="self-center bg-destructive dark:bg-red-600 px-4 py-3 rounded-lg mx-4 mt-4">
|
|
21
|
+
<Text className="text-white font-semibold">{text1}</Text>
|
|
22
|
+
{text2 && <Text className="text-white/80 text-sm mt-1">{text2}</Text>}
|
|
23
|
+
</View>
|
|
24
|
+
),
|
|
25
|
+
info: ({ text1, text2 }: BaseToastProps) => (
|
|
26
|
+
<View className="self-center bg-blue-500 dark:bg-blue-600 px-4 py-3 rounded-lg mx-4 mt-4">
|
|
27
|
+
<Text className="text-white font-semibold">{text1}</Text>
|
|
28
|
+
{text2 && <Text className="text-white/80 text-sm mt-1">{text2}</Text>}
|
|
29
|
+
</View>
|
|
30
|
+
),
|
|
31
|
+
};
|