From b86c3ee2f5be64fca9bae5f2a042e5de31bc90ac Mon Sep 17 00:00:00 2001 From: Vitalii Yehorov Date: Sun, 21 May 2023 14:37:38 +0200 Subject: [PATCH] feat: refactored available values progress coloring through theme --- app/game/index.tsx | 10 +++- .../available-values-item.styles.ts | 7 ++- components/cell/cell.tsx | 51 ---------------- .../{cell => field-cell}/cell.styles.ts | 3 - components/field-cell/cell.tsx | 58 +++++++++++++++++++ components/field/field.styles.ts | 3 + components/field/field.tsx | 22 ++++++- components/row/row.styles.ts | 7 --- components/row/row.tsx | 25 -------- components/theme.ts | 12 ++++ utils/cell/is-cell-highlighted.util.ts | 4 +- utils/cell/is-same-cell-value.util.ts | 7 +++ utils/cell/is-same-cell.util.ts | 4 +- 13 files changed, 114 insertions(+), 99 deletions(-) delete mode 100644 components/cell/cell.tsx rename components/{cell => field-cell}/cell.styles.ts (94%) create mode 100644 components/field-cell/cell.tsx delete mode 100644 components/row/row.styles.ts delete mode 100644 components/row/row.tsx create mode 100644 utils/cell/is-same-cell-value.util.ts diff --git a/app/game/index.tsx b/app/game/index.tsx index 33e02e5..b3b9496 100644 --- a/app/game/index.tsx +++ b/app/game/index.tsx @@ -2,7 +2,7 @@ import * as Haptics from 'expo-haptics'; import { ImpactFeedbackStyle } from 'expo-haptics'; import { useRouter } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; -import { useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { Alert, Text, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -10,7 +10,9 @@ import { AvailableValues } from '../../components/available-values/available-val import { BlackButton } from '../../components/black-button/black-button'; import { Field } from '../../components/field/field'; import { MaxMistakesConstant } from '../../constants/max-mistakes.constant'; -import { useAppSelector } from '../../hooks/redux.hook'; +import { useAppDispatch, useAppSelector } from '../../hooks/redux.hook'; +import { type CellInterface } from '../../interfaces/cell.interface'; +import { appRootSelectCellAction } from '../../store/app-root/app-root.actions'; import { appRootFieldSelector, appRootMistakesSelector, appRootSelectedCellSelector } from '../../store/app-root/app-root.selectors'; import { hasBlankCells } from '../../utils/field/has-blank-cells.util'; @@ -19,6 +21,7 @@ import { GameStyles as styles } from './game.styles'; export default function Game() { const router = useRouter(); + const dispatch = useAppDispatch(); const field = useAppSelector(appRootFieldSelector); const selectedCell = useAppSelector(appRootSelectedCellSelector); const mistakes = useAppSelector(appRootMistakesSelector); @@ -45,6 +48,7 @@ export default function Game() { { text: 'OK', onPress: () => void router.push('/') } ]); }; + const handleSelectCell = useCallback((cell: CellInterface | undefined) => void dispatch(appRootSelectCellAction(cell)), []); return ( @@ -55,7 +59,7 @@ export default function Game() { - + ); diff --git a/components/available-values-item/available-values-item.styles.ts b/components/available-values-item/available-values-item.styles.ts index 24eda15..cf27d02 100644 --- a/components/available-values-item/available-values-item.styles.ts +++ b/components/available-values-item/available-values-item.styles.ts @@ -8,8 +8,9 @@ const progressHeight = 2; export const AvailableValuesItemStyles = StyleSheet.create({ button: { alignItems: 'center', - borderBottomColor: Colors.black, - borderColor: Colors.cell.highlighted, + borderBottomColor: Colors.value.progress, + borderBottomWidth: progressHeight, + borderColor: Colors.value.border, borderWidth: 1, height: CellSizeConstant, justifyContent: 'center', @@ -25,7 +26,7 @@ export const AvailableValuesItemStyles = StyleSheet.create({ position: 'absolute', top: CellSizeConstant - progressHeight }, - text: { color: Colors.black }, + text: { color: Colors.value.text }, textActive: { color: Colors.cell.activeValueText }, diff --git a/components/cell/cell.tsx b/components/cell/cell.tsx deleted file mode 100644 index b95536a..0000000 --- a/components/cell/cell.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { cs } from '@rnw-community/shared'; -import { memo } from 'react'; -import { Pressable, Text } from 'react-native'; - -import { BlankCellValueContant } from '../../constants/blank-cell-value.contant'; -import { useAppDispatch } from '../../hooks/redux.hook'; -import { type CellInterface } from '../../interfaces/cell.interface'; -import { appRootSelectCellAction } from '../../store/app-root/app-root.actions'; -import { isCellHighlighted } from '../../utils/cell/is-cell-highlighted.util'; -import { isGroupEnd } from '../../utils/cell/is-group-end.util'; -import { isSameCell } from '../../utils/cell/is-same-cell.util'; - -import { CellStyles as styles } from './cell.styles'; - -interface Props { - cell: CellInterface; - selectedCell?: CellInterface; -} - -const CellComponent = ({ cell, selectedCell }: Props) => { - const dispatch = useAppDispatch(); - - const isLastRow = cell.y === 8; - const isLastCol = cell.x === 8; - const isActive = isSameCell(cell, selectedCell); - const isActiveValue = cell.value === selectedCell?.value && cell.value !== BlankCellValueContant; - - const value = cell.value === BlankCellValueContant ? '' : cell.value.toString(); - - const handlePress = () => void dispatch(appRootSelectCellAction(isActive ? undefined : cell)); - - const cellStyles = [ - styles.cell, - cs(isGroupEnd(cell.x), styles.cellGroupXEnd), - cs(isGroupEnd(cell.y), styles.cellGroupYEnd), - cs(isCellHighlighted(cell, selectedCell), styles.cellHighlighted), - cs(isActiveValue, styles.cellHighlightedValue), - cs(isActive, styles.cellActive), - cs(isLastRow, styles.cellLastRow), - cs(isLastCol, styles.cellLastCol) - ]; - const textStyles = [styles.cellText, cs(isActiveValue, styles.cellHighlightedText), cs(isActive, styles.cellActiveText)]; - - return ( - - {value} - - ); -}; - -export const Cell = memo(CellComponent); diff --git a/components/cell/cell.styles.ts b/components/field-cell/cell.styles.ts similarity index 94% rename from components/cell/cell.styles.ts rename to components/field-cell/cell.styles.ts index 53d4b15..2278075 100644 --- a/components/cell/cell.styles.ts +++ b/components/field-cell/cell.styles.ts @@ -17,9 +17,6 @@ export const CellStyles = StyleSheet.create({ justifyContent: 'center', width: CellSizeConstant }, - cellActive: { - backgroundColor: Colors.cell.active - }, cellActiveText: { color: Colors.white }, diff --git a/components/field-cell/cell.tsx b/components/field-cell/cell.tsx new file mode 100644 index 0000000..000a5be --- /dev/null +++ b/components/field-cell/cell.tsx @@ -0,0 +1,58 @@ +import { cs, type OnEventFn } from '@rnw-community/shared'; +import { memo } from 'react'; +import { Pressable, Text } from 'react-native'; +import Reanimated, { interpolateColor, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; + +import { BlankCellValueContant } from '../../constants/blank-cell-value.contant'; +import { type CellInterface } from '../../interfaces/cell.interface'; +import { isGroupEnd } from '../../utils/cell/is-group-end.util'; +import { Colors } from '../theme'; + +import { CellStyles as styles } from './cell.styles'; + +const ReanimatedPressable = Reanimated.createAnimatedComponent(Pressable); + +interface Props { + cell: CellInterface; + onSelect: OnEventFn; + isActive: boolean; + isActiveValue: boolean; + isCellHighlighted: boolean; +} + +const CellComponent = ({ cell, onSelect, isActive, isActiveValue, isCellHighlighted }: Props) => { + const value = cell.value === BlankCellValueContant ? '' : cell.value.toString(); + const isLastRow = cell.y === 8; + const isLastCol = cell.x === 8; + + const progress = useSharedValue(0); + const animatedStyles = useAnimatedStyle(() => ({ + backgroundColor: interpolateColor(progress.value, [0, 1], [Colors.white, Colors.cell.active]) + })); + + const handlePress = () => { + progress.value = withTiming(isActive ? 0 : 1, { duration: 500 }); + + onSelect(isActive ? undefined : cell); + }; + + const cellStyles = [ + styles.cell, + cs(isGroupEnd(cell.x), styles.cellGroupXEnd), + cs(isGroupEnd(cell.y), styles.cellGroupYEnd), + cs(isCellHighlighted, styles.cellHighlighted), + cs(isLastRow, styles.cellLastRow), + cs(isLastCol, styles.cellLastCol), + cs(isActiveValue, styles.cellHighlightedValue), + cs(isActive, animatedStyles) + ]; + const textStyles = [styles.cellText, cs(isActiveValue, styles.cellHighlightedText), cs(isActive, styles.cellActiveText)]; + + return ( + + {value} + + ); +}; + +export const Cell = memo(CellComponent); diff --git a/components/field/field.styles.ts b/components/field/field.styles.ts index ba79220..465e857 100644 --- a/components/field/field.styles.ts +++ b/components/field/field.styles.ts @@ -1,6 +1,9 @@ import { StyleSheet } from 'react-native'; export const FieldStyles = StyleSheet.create({ + row: { + flexDirection: 'row' + }, wrapper: { alignItems: 'center', flex: 5, diff --git a/components/field/field.tsx b/components/field/field.tsx index d512d4f..a535ec5 100644 --- a/components/field/field.tsx +++ b/components/field/field.tsx @@ -1,20 +1,36 @@ +import { type OnEventFn } from '@rnw-community/shared'; import { View } from 'react-native'; import { type CellInterface } from '../../interfaces/cell.interface'; -import { Row } from '../row/row'; +import { isCellHighlighted } from '../../utils/cell/is-cell-highlighted.util'; +import { isSameCellValue } from '../../utils/cell/is-same-cell-value.util'; +import { isSameCell } from '../../utils/cell/is-same-cell.util'; +import { Cell } from '../field-cell/cell'; import { FieldStyles as styles } from './field.styles'; interface Props { field: CellInterface[][]; selectedCell?: CellInterface; + onSelect: OnEventFn; } -export const Field = ({ field, selectedCell }: Props) => { +export const Field = ({ field, selectedCell, onSelect }: Props) => { return ( {field.map((row, i) => ( - + + {row.map(cell => ( + + ))} + ))} ); diff --git a/components/row/row.styles.ts b/components/row/row.styles.ts deleted file mode 100644 index 8f06074..0000000 --- a/components/row/row.styles.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { StyleSheet } from 'react-native'; - -export const RowStyles = StyleSheet.create({ - row: { - flexDirection: 'row' - } -}); diff --git a/components/row/row.tsx b/components/row/row.tsx deleted file mode 100644 index f4bee91..0000000 --- a/components/row/row.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { memo } from 'react'; -import { View } from 'react-native'; - -import { type CellInterface } from '../../interfaces/cell.interface'; -import { Cell } from '../cell/cell'; - -import { RowStyles as styles } from './row.styles'; - -interface Props { - cells: CellInterface[]; - selectedCell?: CellInterface; -} - -// TODO: Do we need rows? -const RowComponent = ({ cells, selectedCell }: Props) => { - return ( - - {cells.map(cell => ( - - ))} - - ); -}; - -export const Row = memo(RowComponent); diff --git a/components/theme.ts b/components/theme.ts index a435c9d..aa34d74 100644 --- a/components/theme.ts +++ b/components/theme.ts @@ -12,6 +12,12 @@ export const WhiteTheme = { highlighted: 'rgba(0,0,0,0.05)', highlightedText: 'rgba(0, 255, 0, 1)', activeValueText: 'rgba(201, 242, 155, 0.5)' + }, + value: { + border: 'rgba(0,0,0,0.15)', + progress: 'rgba(0, 255, 0, 0.3)', + progressActive: 'rgba(0, 255, 0, 0.7)', + text: 'rgba(0, 0, 0,1)' } } }; @@ -27,6 +33,12 @@ export const BlackTheme = { highlighted: 'rgba(255,255,255,0.15)', highlightedText: 'rgba(0, 255, 0, 1)', activeValueText: 'rgba(201, 242, 155, 0.5)' + }, + value: { + border: 'rgba(255,255,255,0.15)', + progress: 'rgba(0, 255, 0, 0.3)', + progressActive: 'rgba(0, 255, 0, 0.7)', + text: 'rgba(255, 255, 255,1)' } } }; diff --git a/utils/cell/is-cell-highlighted.util.ts b/utils/cell/is-cell-highlighted.util.ts index 4999759..4b96e9f 100644 --- a/utils/cell/is-cell-highlighted.util.ts +++ b/utils/cell/is-cell-highlighted.util.ts @@ -2,6 +2,6 @@ import { isDefined } from '@rnw-community/shared'; import { type CellInterface } from '../../interfaces/cell.interface'; -export const isCellHighlighted = (cell: CellInterface, activeCell?: CellInterface): boolean => { - return isDefined(activeCell) && (activeCell.x === cell.x || activeCell.y === cell.y || activeCell.group === cell.group); +export const isCellHighlighted = (cell: CellInterface, selectedCell?: CellInterface): boolean => { + return isDefined(selectedCell) && (selectedCell.x === cell.x || selectedCell.y === cell.y || selectedCell.group === cell.group); }; diff --git a/utils/cell/is-same-cell-value.util.ts b/utils/cell/is-same-cell-value.util.ts new file mode 100644 index 0000000..ab336a9 --- /dev/null +++ b/utils/cell/is-same-cell-value.util.ts @@ -0,0 +1,7 @@ +import { isDefined } from '@rnw-community/shared'; + +import { BlankCellValueContant } from '../../constants/blank-cell-value.contant'; +import { type CellInterface } from '../../interfaces/cell.interface'; + +export const isSameCellValue = (cell: CellInterface, selectedCell?: CellInterface): boolean => + isDefined(selectedCell) && cell.value === selectedCell?.value && cell.value !== BlankCellValueContant; diff --git a/utils/cell/is-same-cell.util.ts b/utils/cell/is-same-cell.util.ts index 2bb5ab4..afb714a 100644 --- a/utils/cell/is-same-cell.util.ts +++ b/utils/cell/is-same-cell.util.ts @@ -2,5 +2,5 @@ import { isDefined } from '@rnw-community/shared'; import { type CellInterface } from '../../interfaces/cell.interface'; -export const isSameCell = (cell: CellInterface, comparedCell?: CellInterface): boolean => - isDefined(comparedCell) && cell.x === comparedCell.x && cell.y === comparedCell.y; +export const isSameCell = (cell: CellInterface, selectedCell?: CellInterface): boolean => + isDefined(selectedCell) && cell.x === selectedCell.x && cell.y === selectedCell.y;