diff --git a/example/.rnstorybook/stories/Organisms/Toaster/Toaster.stories.tsx b/example/.rnstorybook/stories/Organisms/Toaster/Toaster.stories.tsx index e95bf80..083a327 100644 --- a/example/.rnstorybook/stories/Organisms/Toaster/Toaster.stories.tsx +++ b/example/.rnstorybook/stories/Organisms/Toaster/Toaster.stories.tsx @@ -116,6 +116,35 @@ const InsideModalDemo = () => ( ); +const LoadingDemo = () => ( + + + ToasterApi.show({ + type: 'info', + title: 'Loading...', + loading: true, + }) + } + /> + + ToasterApi.show({ + type: 'success', + title: 'Done!', + description: 'Action completed.', + }) + } + /> + ToasterApi.hide()} + /> + +); + const meta = { title: 'Organisms/Toaster', component: View, @@ -136,3 +165,7 @@ export const Interactive: Story = { export const InsideModal: Story = { render: () => , }; + +export const Loading: Story = { + render: () => , +}; diff --git a/src/components/Atoms/Icon/index.tsx b/src/components/Atoms/Icon/index.tsx index 72d1797..2373455 100644 --- a/src/components/Atoms/Icon/index.tsx +++ b/src/components/Atoms/Icon/index.tsx @@ -6,11 +6,19 @@ import { useRubberDuckStore } from '../../../store'; import { buildSize } from './library'; export const Icon: React.FC = (props) => { - const { icon, size, color = "textPrimary" } = props; + const { + icon, + size, + color = "textPrimary", + hexColor, + } = props; const colors = useRubberDuckStore((s) => s.colors); const iconSize = buildSize(size); const IconComponent = Icons[icon] as LucideIcon; + const resolvedColor = hexColor ?? colors[color]; - return ; + return ; }; + +export type { IconName } from './types'; diff --git a/src/components/Atoms/Icon/types/IIconProps.ts b/src/components/Atoms/Icon/types/IIconProps.ts index 0e91def..2e586c8 100644 --- a/src/components/Atoms/Icon/types/IIconProps.ts +++ b/src/components/Atoms/Icon/types/IIconProps.ts @@ -6,4 +6,5 @@ export type IIconProps = { icon: IconName; size?: IconSize; color?: keyof IColors; + hexColor?: string; }; diff --git a/src/components/Atoms/Switch/index.tsx b/src/components/Atoms/Switch/index.tsx index 69a665d..d4ae04a 100644 --- a/src/components/Atoms/Switch/index.tsx +++ b/src/components/Atoms/Switch/index.tsx @@ -11,7 +11,7 @@ export const Switch: React.FC = (props) => { const { isOn, disabled } = props; const colors = useRubberDuckStore((s) => s.colors); - const styles = useStyles(); + const styles = useStyles(!!disabled); const reanimatedStyles = useReanimatedStyles({ isOn: !!isOn, accentColor: colors.accent, diff --git a/src/components/Atoms/Switch/styles.ts b/src/components/Atoms/Switch/styles.ts index a09d9e9..aa755c4 100644 --- a/src/components/Atoms/Switch/styles.ts +++ b/src/components/Atoms/Switch/styles.ts @@ -3,7 +3,7 @@ import { useRubberDuckStore } from '../../../store'; import { Tokens } from '../../../tokens/Tokens.class'; import { SWITCH_TRACK_WIDTH, SWITCH_TRACK_HEIGHT, SWITCH_THUMB_SIZE, SWITCH_PADDING } from './constants'; -export const useStyles = () => { +export const useStyles = (disabled: boolean) => { const colors = useRubberDuckStore((s) => s.colors); return StyleSheet.create({ @@ -13,6 +13,7 @@ export const useStyles = () => { borderRadius: Tokens.radii({ key: 'full' }), padding: SWITCH_PADDING, justifyContent: 'center', + opacity: disabled ? 0.8 : 1, }, thumb: { diff --git a/src/components/Molecules/Inputs/MoneyField/index.tsx b/src/components/Molecules/Inputs/MoneyField/index.tsx index 6136fb6..1f5e5a9 100644 --- a/src/components/Molecules/Inputs/MoneyField/index.tsx +++ b/src/components/Molecules/Inputs/MoneyField/index.tsx @@ -33,7 +33,7 @@ export const MoneyField: React.FC = (props) => { const reanimatedStyles = useReanimatedStyles({ isFocused, accentColor: colors.accent, - borderDefaultColor: colors.borderDefault, + borderDefaultColor: colors.borderSubtle, }); return ( diff --git a/src/components/Molecules/Inputs/MoneyField/styles.ts b/src/components/Molecules/Inputs/MoneyField/styles.ts index 76eab75..922d3ad 100644 --- a/src/components/Molecules/Inputs/MoneyField/styles.ts +++ b/src/components/Molecules/Inputs/MoneyField/styles.ts @@ -22,7 +22,7 @@ export const useStyles = (props: IMoneyFieldProps) => { inputWrapper: { borderWidth: 2, - borderColor: colors.borderDefault, + borderColor: colors.borderSubtle, borderRadius: Tokens.radii({ key: 'lg' }), backgroundColor: colors.background, }, diff --git a/src/components/Molecules/Inputs/MultilineText/index.tsx b/src/components/Molecules/Inputs/MultilineText/index.tsx index 0fed200..875a076 100644 --- a/src/components/Molecules/Inputs/MultilineText/index.tsx +++ b/src/components/Molecules/Inputs/MultilineText/index.tsx @@ -32,7 +32,7 @@ export const MultilineText: React.FC = (props) => { const reanimatedStyles = useReanimatedStyles({ isFocused, accentColor: colors.accent, - borderDefaultColor: colors.borderDefault, + borderDefaultColor: colors.borderSubtle, }); return ( diff --git a/src/components/Molecules/Inputs/MultilineText/styles.ts b/src/components/Molecules/Inputs/MultilineText/styles.ts index e8d3cb1..d938a63 100644 --- a/src/components/Molecules/Inputs/MultilineText/styles.ts +++ b/src/components/Molecules/Inputs/MultilineText/styles.ts @@ -22,7 +22,7 @@ export const useStyles = (props: IMultilineTextProps) => { inputWrapper: { borderWidth: 2, - borderColor: colors.borderDefault, + borderColor: colors.borderSubtle, borderRadius: Tokens.radii({ key: 'lg' }), backgroundColor: colors.background, minHeight: 24 * numberOfLines, diff --git a/src/components/Molecules/Inputs/NumberField/index.tsx b/src/components/Molecules/Inputs/NumberField/index.tsx index 898d536..a91b586 100644 --- a/src/components/Molecules/Inputs/NumberField/index.tsx +++ b/src/components/Molecules/Inputs/NumberField/index.tsx @@ -32,7 +32,7 @@ export const NumberField: React.FC = (props) => { const reanimatedStyles = useReanimatedStyles({ isFocused, accentColor: colors.accent, - borderDefaultColor: colors.borderDefault, + borderDefaultColor: colors.borderSubtle, }); return ( diff --git a/src/components/Molecules/Inputs/NumberField/styles.ts b/src/components/Molecules/Inputs/NumberField/styles.ts index 7f95f5d..4312fd3 100644 --- a/src/components/Molecules/Inputs/NumberField/styles.ts +++ b/src/components/Molecules/Inputs/NumberField/styles.ts @@ -24,7 +24,7 @@ export const useStyles = (props: INumberFieldProps) => { flexDirection: 'row', alignItems: 'center', borderWidth: 2, - borderColor: colors.borderDefault, + borderColor: colors.borderSubtle, borderRadius: Tokens.radii({ key: 'lg' }), backgroundColor: colors.background, }, diff --git a/src/components/Molecules/Inputs/SimpleText/index.tsx b/src/components/Molecules/Inputs/SimpleText/index.tsx index 6635242..5d33c2e 100644 --- a/src/components/Molecules/Inputs/SimpleText/index.tsx +++ b/src/components/Molecules/Inputs/SimpleText/index.tsx @@ -23,18 +23,18 @@ export const SimpleText: React.FC = (props) => { const colors = useRubberDuckStore((s) => s.colors); const styles = useStyles(props); - + const { isFocused, onFocus, onBlur, onChangeText, } = useSimpleTextViewModel(props); - + const reanimatedStyles = useReanimatedStyles({ isFocused, accentColor: colors.accent, - borderDefaultColor: colors.borderDefault, + borderDefaultColor: colors.borderSubtle, }); return ( diff --git a/src/components/Molecules/Inputs/SimpleText/styles.ts b/src/components/Molecules/Inputs/SimpleText/styles.ts index bb7a2bf..18b2ae6 100644 --- a/src/components/Molecules/Inputs/SimpleText/styles.ts +++ b/src/components/Molecules/Inputs/SimpleText/styles.ts @@ -24,7 +24,7 @@ export const useStyles = (props: ISimpleTextProps) => { flexDirection: 'row', alignItems: 'center', borderWidth: 2, - borderColor: colors.borderDefault, + borderColor: colors.borderSubtle, borderRadius: Tokens.radii({ key: 'lg' }), backgroundColor: colors.background, }, diff --git a/src/components/Organisms/BottomListModal/components/ModalList/library/mountRenderItem.tsx b/src/components/Organisms/BottomListModal/components/ModalList/library/mountRenderItem.tsx index 451ff2c..cd0790b 100644 --- a/src/components/Organisms/BottomListModal/components/ModalList/library/mountRenderItem.tsx +++ b/src/components/Organisms/BottomListModal/components/ModalList/library/mountRenderItem.tsx @@ -31,26 +31,32 @@ export const mountRenderItem = (params: IMountRenderItemParams): ListRenderItem< switch (item.type) { case 'checkbox': return ( - + - + ); case 'radio': return ( - + - + ); case 'avatar': diff --git a/src/components/Organisms/BottomModal/components/ModalBackground/index.tsx b/src/components/Organisms/BottomModal/components/ModalBackground/index.tsx new file mode 100644 index 0000000..186cfb4 --- /dev/null +++ b/src/components/Organisms/BottomModal/components/ModalBackground/index.tsx @@ -0,0 +1,13 @@ +import type { BottomSheetBackgroundProps } from '@gorhom/bottom-sheet'; +import React from 'react'; +import Animated from 'react-native-reanimated'; +import { useStyles } from './styles'; + +export const ModalBackground: React.FC = (props) => { + const { style } = props; + const styles = useStyles(); + + return ( + + ); +}; diff --git a/src/components/Organisms/BottomModal/components/ModalBackground/styles.ts b/src/components/Organisms/BottomModal/components/ModalBackground/styles.ts new file mode 100644 index 0000000..4097754 --- /dev/null +++ b/src/components/Organisms/BottomModal/components/ModalBackground/styles.ts @@ -0,0 +1,15 @@ +import { StyleSheet } from 'react-native'; +import { useRubberDuckStore } from '../../../../../store'; +import { Tokens } from '../../../../../tokens/Tokens.class'; + +export const useStyles = () => { + const colors = useRubberDuckStore((s) => s.colors); + + return StyleSheet.create({ + background: { + borderTopRightRadius: Tokens.radii({ key: 'lg' }), + borderTopLeftRadius: Tokens.radii({ key: 'lg' }), + backgroundColor: colors.surface, + }, + }); +}; diff --git a/src/components/Organisms/BottomModal/components/index.ts b/src/components/Organisms/BottomModal/components/index.ts new file mode 100644 index 0000000..fb9ea79 --- /dev/null +++ b/src/components/Organisms/BottomModal/components/index.ts @@ -0,0 +1 @@ +export * from './ModalBackground'; diff --git a/src/components/Organisms/BottomModal/index.tsx b/src/components/Organisms/BottomModal/index.tsx index 01e6779..e432cb7 100644 --- a/src/components/Organisms/BottomModal/index.tsx +++ b/src/components/Organisms/BottomModal/index.tsx @@ -8,6 +8,7 @@ import { } from '@gorhom/bottom-sheet'; import { useStyles } from './styles'; import { useBottomModalViewModel } from './hooks'; +import { ModalBackground } from './components'; export const BottomModal: React.FC = () => { const { height: screenHeight } = useWindowDimensions(); @@ -33,8 +34,8 @@ export const BottomModal: React.FC = () => { enableDynamicSizing maxDynamicContentSize={screenHeight * 0.85} backdropComponent={renderBackdrop} + backgroundComponent={ModalBackground} handleIndicatorStyle={styles.handleIndicator} - backgroundStyle={styles.background} enablePanDownToClose> {bottomModalProps?.content ?? null} diff --git a/src/components/Organisms/BottomModal/styles.ts b/src/components/Organisms/BottomModal/styles.ts index 5375534..6368db9 100644 --- a/src/components/Organisms/BottomModal/styles.ts +++ b/src/components/Organisms/BottomModal/styles.ts @@ -12,9 +12,5 @@ export const useStyles = () => { height: 4, borderRadius: Tokens.radii({ key: 'full' }), }, - - background: { - backgroundColor: colors.surface, - }, }); }; diff --git a/src/components/Organisms/Toaster/hooks/useToasterViewModel/index.ts b/src/components/Organisms/Toaster/hooks/useToasterViewModel/index.ts index 5e9931c..f234e0a 100644 --- a/src/components/Organisms/Toaster/hooks/useToasterViewModel/index.ts +++ b/src/components/Organisms/Toaster/hooks/useToasterViewModel/index.ts @@ -24,7 +24,9 @@ export const useToasterViewModel = (ref: Ref | null) => { clearTimer(); setToasterPropsState(props); setIsVisible(true); - timeoutRef.current = setTimeout(() => setIsVisible(false), props.duration ?? DEFAULT_TOAST_DURATION_MS); + if (!props.loading) { + timeoutRef.current = setTimeout(() => setIsVisible(false), props.duration ?? DEFAULT_TOAST_DURATION_MS); + } }; const hide = () => { diff --git a/src/components/Organisms/Toaster/index.tsx b/src/components/Organisms/Toaster/index.tsx index 57c1d65..26faca2 100644 --- a/src/components/Organisms/Toaster/index.tsx +++ b/src/components/Organisms/Toaster/index.tsx @@ -8,45 +8,62 @@ import { useToasterViewModel, useReanimatedStyles } from './hooks'; import { TYPE_ICON_MAP } from './constants'; import { Text } from '../../Atoms/Text'; import { Icon } from '../../Atoms/Icon'; +import { Loading } from '../../Atoms/Loading'; export const Toaster: React.FC = ({ ref }) => { const { toasterProps, isVisible, hide } = useToasterViewModel(ref ?? null); const styles = useStyles(toasterProps?.type); const reanimatedStyles = useReanimatedStyles(isVisible, hide); + const isLoading = toasterProps?.loading ?? false; + return ( + style={[isLoading ? styles.wrapperLoading : styles.wrapper, reanimatedStyles.wrapper]}> {toasterProps ? <> - - - - {toasterProps.title} - - {toasterProps.description - ? - - {toasterProps.description} + {isLoading + ? + <> + + + + {toasterProps.title} - : null - } - + + : + <> + + + + {toasterProps.title} + + + {toasterProps.description + ? + + {toasterProps.description} + + : null + } + - - - + + + + + } : null } diff --git a/src/components/Organisms/Toaster/styles.ts b/src/components/Organisms/Toaster/styles.ts index 7d18a3e..bf36a75 100644 --- a/src/components/Organisms/Toaster/styles.ts +++ b/src/components/Organisms/Toaster/styles.ts @@ -30,6 +30,20 @@ export const useStyles = (type: ToasterType | undefined) => { zIndex: 999, }, + wrapperLoading: { + position: 'absolute', + bottom: Tokens.spacer({ key: 'lg' }), + alignSelf: 'center', + paddingVertical: Tokens.spacer({ key: 'sm' }), + paddingHorizontal: Tokens.spacer({ key: 'md' }), + borderRadius: Tokens.radii({ key: 'full' }), + flexDirection: 'row', + alignItems: 'center', + gap: Tokens.spacer({ key: 'xs' }), + backgroundColor, + zIndex: 999, + }, + textWrapper: { flex: 1, }, diff --git a/src/components/Organisms/Toaster/types/IToasterRefProps.ts b/src/components/Organisms/Toaster/types/IToasterRefProps.ts index 85f5bed..ccbfda1 100644 --- a/src/components/Organisms/Toaster/types/IToasterRefProps.ts +++ b/src/components/Organisms/Toaster/types/IToasterRefProps.ts @@ -5,4 +5,5 @@ export interface IToasterRefProps { title: string; description?: string; duration?: number; + loading?: boolean; } diff --git a/src/index.ts b/src/index.ts index e5db9e2..8c075cc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * from './RubberDuckUI.class'; export * from './components'; export * from './hooks'; export * from './provider'; +export * from './tokens'; diff --git a/src/tokens/colors/dark.ts b/src/tokens/colors/dark.ts index 3c8ca17..1c00339 100644 --- a/src/tokens/colors/dark.ts +++ b/src/tokens/colors/dark.ts @@ -5,17 +5,17 @@ export const dark: IColors = { accentLight: '#FFE033', accentDark: '#CCB000', accentMuted: '#FFD60021', - background: '#0A0A0A', - surface: '#111111', - surfaceRaised: '#1A1A1A', - surfaceOverlay: '#222222', + background: '#080808', + surface: '#1C1C1E', + surfaceRaised: '#2C2C2E', + surfaceOverlay: '#3A3A3C', textPrimary: '#F5F5F5', textSecondary: '#A0A0A0', textDisabled: '#555555', textInverse: '#0A0A0A', - borderSubtle: '#2A2A2A', - borderDefault: '#3A3A3A', - borderStrong: '#555555', + borderSubtle: '#3A3A3C', + borderDefault: '#505052', + borderStrong: '#6E6E70', success: '#22C55E', error: '#EF4444', warning: '#F97316', diff --git a/src/tokens/colors/light.ts b/src/tokens/colors/light.ts index 7194af1..abf2735 100644 --- a/src/tokens/colors/light.ts +++ b/src/tokens/colors/light.ts @@ -5,10 +5,10 @@ export const light: IColors = { accentLight: '#FFE033', accentDark: '#CCB000', accentMuted: '#FFD60026', - background: '#FFFFFF', - surface: '#F5F5F5', - surfaceRaised: '#EFEFEF', - surfaceOverlay: '#E5E5E5', + background: '#F5F5F5', + surface: '#FFFFFF', + surfaceRaised: '#E5E5EA', + surfaceOverlay: '#EBEBEB', textPrimary: '#0A0A0A', textSecondary: '#555555', textDisabled: '#AAAAAA', diff --git a/src/tokens/index.ts b/src/tokens/index.ts new file mode 100644 index 0000000..84a8c07 --- /dev/null +++ b/src/tokens/index.ts @@ -0,0 +1 @@ +export type { IColors } from './colors/types';