diff --git a/example/package-lock.json b/example/package-lock.json index 08b1bae..cc20397 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -25,7 +25,7 @@ }, "..": { "name": "@jaredreisinger/react-crossword", - "version": "4.3.2", + "version": "5.2.0", "license": "MIT", "dependencies": { "immer": "^9.0.16", @@ -50,10 +50,10 @@ "@types/react-test-renderer": "^18.0.0", "@types/styled-components": "^5.1.26", "@types/testing-library__jest-dom": "^5.14.5", - "@typescript-eslint/eslint-plugin": "^5.45.0", - "@typescript-eslint/parser": "^5.45.0", + "@typescript-eslint/eslint-plugin": "^5.46.1", + "@typescript-eslint/parser": "^5.46.1", "all-contributors-cli": "^6.24.0", - "commitizen": "^4.2.5", + "commitizen": "^4.2.6", "eslint": "^8.29.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-base": "^15.0.0", @@ -67,8 +67,8 @@ "is-ci": "^3.0.1", "jest": "^29.3.1", "jest-environment-jsdom": "^29.3.1", - "lint-staged": "^13.0.4", - "prettier": "^2.8.0", + "lint-staged": "^13.1.0", + "prettier": "^2.8.1", "pretty-quick": "^3.1.3", "react": "^18.2.0", "react-docgen-typescript": "^2.2.2", @@ -77,7 +77,7 @@ "react-test-renderer": "^18.2.0", "rimraf": "^3.0.2", "semantic-release": "^19.0.5", - "snyk": "^1.1066.0", + "snyk": "^1.1069.0", "stylelint": "^14.16.0", "stylelint-config-prettier": "^9.0.4", "stylelint-config-recommended": "^9.0.0", @@ -86,7 +86,7 @@ "stylelint-prettier": "^2.0.0", "stylelint-processor-styled-components": "^1.10.0", "ts-loader": "^9.4.2", - "typescript": "^4.9.3", + "typescript": "^4.9.4", "webpack": "^5.75.0" }, "engines": { @@ -5685,9 +5685,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001436", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz", - "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==", + "version": "1.0.30001636", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", + "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==", "dev": true, "funding": [ { @@ -5697,6 +5697,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -19290,10 +19294,10 @@ "@types/react-test-renderer": "^18.0.0", "@types/styled-components": "^5.1.26", "@types/testing-library__jest-dom": "^5.14.5", - "@typescript-eslint/eslint-plugin": "^5.45.0", - "@typescript-eslint/parser": "^5.45.0", + "@typescript-eslint/eslint-plugin": "^5.46.1", + "@typescript-eslint/parser": "^5.46.1", "all-contributors-cli": "^6.24.0", - "commitizen": "^4.2.5", + "commitizen": "^4.2.6", "eslint": "^8.29.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-base": "^15.0.0", @@ -19308,8 +19312,8 @@ "is-ci": "^3.0.1", "jest": "^29.3.1", "jest-environment-jsdom": "^29.3.1", - "lint-staged": "^13.0.4", - "prettier": "^2.8.0", + "lint-staged": "^13.1.0", + "prettier": "^2.8.1", "pretty-quick": "^3.1.3", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -19319,7 +19323,7 @@ "react-test-renderer": "^18.2.0", "rimraf": "^3.0.2", "semantic-release": "^19.0.5", - "snyk": "^1.1066.0", + "snyk": "^1.1069.0", "styled-components": "^5.3.6", "stylelint": "^14.16.0", "stylelint-config-prettier": "^9.0.4", @@ -19329,7 +19333,7 @@ "stylelint-prettier": "^2.0.0", "stylelint-processor-styled-components": "^1.10.0", "ts-loader": "^9.4.2", - "typescript": "^4.9.3", + "typescript": "^4.9.4", "webpack": "^5.75.0" }, "dependencies": { @@ -21974,9 +21978,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001436", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz", - "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==", + "version": "1.0.30001636", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", + "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==", "dev": true }, "case-sensitive-paths-webpack-plugin": { diff --git a/package-lock.json b/package-lock.json index 86b157c..1734119 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "@jaredreisinger/react-crossword", + "name": "@lit17/react-crossword", "version": "5.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "@jaredreisinger/react-crossword", - "version": "5.2.0", + "name": "@lit17/react-crossword", + "version": "5.2.1", "license": "MIT", "dependencies": { "immer": "^9.0.16", diff --git a/package.json b/package.json index da62f5d..eb7c014 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/JaredReisinger/react-crossword" + "url": "https://github.com/JaredReisinger/react-crossword.git" }, "license": "MIT", "engines": { diff --git a/src/CrosswordProvider.tsx b/src/CrosswordProvider.tsx index 8d8633c..3710c84 100644 --- a/src/CrosswordProvider.tsx +++ b/src/CrosswordProvider.tsx @@ -161,6 +161,15 @@ export const crosswordProviderPropTypes = { */ onCellChange: PropTypes.func, + /** + * callback function called when a cell is selected(e.g. when the user selects a + * cell); called with `(direction, number, row, cols)` arguments, where the `direction` + * is the current direction, `number` is the index of the clue and `row` and `col` are + * the 0-based position of the cell + */ + + onCellSelected: PropTypes.func, + /** * callback function called when a clue is selected */ @@ -265,6 +274,19 @@ export type CrosswordProviderProps = EnhancedProps< */ onCellChange?: (row: number, col: number, char: string) => void; + /** + * callback function called when a cell is selected(e.g. when the user selects a + * cell); called with `(direction, number, row, cols)` arguments, where the `direction` + * is the current direction, `number` is the index of the clue and `row` and `col` are + * the 0-based position of the cell + */ + onCellSelected?: ( + direction: Direction, + number: string | undefined, + row: number, + col: number + ) => void; + /** * callback function called when a clue is selected */ @@ -339,6 +361,7 @@ const CrosswordProvider = React.forwardRef< onCrosswordComplete, onCrosswordCorrect, onCellChange, + onCellSelected, onClueSelected, useStorage, storageKey, @@ -626,9 +649,13 @@ const CrosswordProvider = React.forwardRef< setCurrentDirection(direction); setCurrentNumber(candidate[direction] ?? ''); + if (onCellSelected) { + onCellSelected(direction, candidate[direction] ?? '', row, col); + } + return candidate; }, - [currentDirection, getCellData] + [currentDirection, getCellData, onCellSelected] ); const moveRelative = useCallback( @@ -714,6 +741,15 @@ const CrosswordProvider = React.forwardRef< setCurrentDirection(other); setCurrentNumber(cellData[other] ?? ''); } + + if (onCellSelected) { + onCellSelected( + other, + cellData[other] ?? '', + focusedRow, + focusedCol + ); + } break; } @@ -778,6 +814,7 @@ const CrosswordProvider = React.forwardRef< data, currentNumber, moveTo, + onCellSelected, ] ); @@ -886,11 +923,15 @@ const CrosswordProvider = React.forwardRef< } setCurrentNumber(cellData[direction] ?? ''); + + if (onCellSelected) { + onCellSelected(direction, cellData[direction] ?? '', row, col); + } } focus(); }, - [currentDirection, focus, focused, focusedCol, focusedRow] + [currentDirection, focus, focused, focusedCol, focusedRow, onCellSelected] ); const handleInputClick = useCallback< @@ -915,8 +956,25 @@ const CrosswordProvider = React.forwardRef< setCurrentNumber(cellData[direction] ?? ''); focus(); + + if (onCellSelected) { + onCellSelected( + direction, + cellData[direction] ?? '', + focusedCol, + focusedRow + ); + } }, - [currentDirection, focus, focused, focusedCol, focusedRow, getCellData] + [ + currentDirection, + focus, + focused, + focusedCol, + focusedRow, + getCellData, + onCellSelected, + ] ); const handleClueSelected = useCallback( @@ -1132,6 +1190,7 @@ CrosswordProvider.defaultProps = { onCrosswordComplete: undefined, onCrosswordCorrect: undefined, onCellChange: undefined, + onCellSelected: undefined, onClueSelected: undefined, children: undefined, }; diff --git a/src/context.ts b/src/context.ts index cd7bd83..23a9c64 100644 --- a/src/context.ts +++ b/src/context.ts @@ -35,6 +35,7 @@ export interface CrosswordContextType { handleInputClick: React.MouseEventHandler; /** A handler for clue selection. */ handleClueSelected: (direction: Direction, number: string) => void; + /** Provides registration for focus actions */ registerFocusHandler: (focusHandler: FocusHandler | null) => void;