mycrossword
TypeScript icon, indicating that this package has built-in type declarations

1.4.0 • Public • Published

MyCrossword

React crossword component built to work with Guardian crossword data.

image

Features

  • Displays crossword grid and list of clues
  • Displays separators in the grid for hyphenated and multi-word solutions
  • Responsive to different screen sizes
  • Clue selection highlights relevant cells
  • Grouped clues span multiple columns or rows
  • Tab key cycles between clues
  • Arrow keys navigate between cells
  • Displays attempted clues as greyed out
  • Autosaves progress to local storage
  • Smart clearing only removes cells not part of other completed solutions
  • Check and reveal solution functions (provided solutionsAvailable: true )
  • Anagram helper

Install

npm

npm install mycrossword --save

yarn

yarn add mycrossword

Usage

import MyCrossword from 'mycrossword';
import 'mycrossword/dist/index.css';
import React from 'react';

const data = {
  /* ... crossword data (see below) ... */
};

export default function MyPage() {
  return <MyCrossword id="crossword-1" data={data} />;
}

Props

Property Description
allowedHtmlTags string[] = ['b', 'strong', 'i', 'em', 'sub', 'sup']
Optional list of HTML tags allowed within the clues. Use [] to prevent all HTML tags. Defaults to ['b', 'strong', 'i', 'em', 'sub', 'sup'].
allowMissingSolutions boolean = false
Optional flag to relax grid validation. This will allow the data prop to have missing or incomplete solutions in its entries.
cellMatcher RegExp = '/[A-Z]/'
Optional regular expression to match against entered cell characters. Defaults to /[A-Z]/.
className string
Optional string to apply a space-delimited list of class names.
data GuardianCrossword
Required object that contains crossword clues, solutions and other information needed to draw the grid. See crossword data below for more information.
id string
Required string to uniquely identify the crossword.
loadGrid GuessGrid | undefined
Optional object to override storage mechanism. Called when the component is initialized with the ID of the crossword. Should return an array-based representation of the crossword grid. See guess grid below for more information.
onCellChange (cellChange: CellChange) => void | undefined
Optional function. Called after a grid cell has changed its guess. The object contains the properties pos, guess and previousGuess.
onCellFocus (cellFocus: CellFocus) => void | undefined
Optional function. Called after the focus switches to a new cell. The object returned contains the properties pos and clueId.
saveGrid (value: GuessGrid | ((val: GuessGrid) => GuessGrid)) => void | undefined
Optional function to override storage mechanism. Called after the grid has changed with the ID of the crossword and array-based representation of the grid. See guess grid below for more information.
stickyClue 'always' | 'never' | 'auto' = 'auto'
Optional value to define when to show the sticky clue above the grid. Defaults to 'auto' (shown on xs and sm breakpoints).
theme 'red' | 'pink' | 'purple' | 'deepPurple' | 'indigo' | 'blue' | 'lightBlue' | 'cyan' | 'teal' | 'green' | 'deepOrange' | 'blueGrey' = 'blue'
Optional value to override the main colour applied to the highlighted cells and clues. Defaults to 'blue'.

For more type information, see interfaces.

Crossword data

This is an example of the JSON data required to create the crossword shown above. Its structure is defined by the GuardianCrossword interface.

{
  id: 'simple/1',
  number: 1,
  name: 'Simple Crossword #1',
  date: 1542326400000,
  entries: [
    {
      id: '1-across',
      number: 1,
      humanNumber: '1',
      clue: 'Toy on a string (2-2)',
      direction: 'across',
      length: 4,
      group: ['1-across'],
      position: { x: 0, y: 0 },
      separatorLocations: {
        '-': [2],
      },
      solution: 'YOYO',
    },
    {
      id: '4-across',
      number: 4,
      humanNumber: '4',
      clue: 'Have a rest (3,4)',
      direction: 'across',
      length: 7,
      group: ['4-across'],
      position: { x: 0, y: 2 },
      separatorLocations: {
        ',': [3],
      },
      solution: 'LIEDOWN',
    },
    {
      id: '1-down',
      number: 1,
      humanNumber: '1',
      clue: 'Colour (6)',
      direction: 'down',
      length: 6,
      group: ['1-down'],
      position: { x: 0, y: 0 },
      separatorLocations: {},
      solution: 'YELLOW',
    },
    {
      id: '2-down',
      number: 2,
      humanNumber: '2',
      clue: 'Bits and bobs (4,3,4)',
      direction: 'down',
      length: 7,
      group: ['2-down', '3-down'],
      position: { x: 3, y: 0 },
      separatorLocations: {
        ',': [4, 7],
      },
      solution: 'ODDSAND',
    },
    {
      id: '3-down',
      number: 3,
      humanNumber: '3',
      clue: 'See 2',
      direction: 'down',
      length: 4,
      group: ['2-down', '3-down'],
      position: {
        x: 6,
        y: 1,
      },
      separatorLocations: {},
      solution: 'ENDS',
    },
  ],
  solutionAvailable: true,
  dateSolutionAvailable: 1542326400000,
  dimensions: {
    cols: 13,
    rows: 13,
  },
  crosswordType: 'quick',
};

Guess grid

Some functions require or return the state of the crossword grid. This is a 2-dimensional array holding the user's guess for each cell. Incomplete cells or cells that are not part of any answer are represented as the empty string (""). Note that it follows the convention of indexing by column first (x) and then row (y) so the printed array is transposed compared to how the crossword grid appears. For example, the following crossword...

D
O
A G E

...would be represented as...

{
  "value": [
    ["",  "",  "A"],
    ["D", "O", "G"],
    ["",  "",  "E"]
  ]
}

What's new?

While the project has been created from scratch, it should be considered as a continuation of @guardian/react-crossword. With that in mind, here's a list of improvements when compared to the original:

Technical improvements

  • Use of TypeScript
  • Switched to functional components
  • Better performance (memoization, useCallback etc.)
  • Simplified global state management using RTK
  • More modular component hierarchy
  • Scoped CSS
  • Full unit test coverage
  • CI using GitHub actions

Functional improvements

  • Theme styles
  • Combined buttons to use dropdown menus
  • Check/Reveal letter
  • Arrow keys can skip over dark cells
  • Backspace key moves backwards without the cell having to be empty
  • Anagram helper
    • Click clue words to add to anagram
    • Added letter to centre of word wheel
    • Character counter
    • Escape key to clear/close
    • Show missing letters from grid
  • Removed hidden input and implicitly fixed the window resize bug
  • Fixed HTML display in StickyClue and AnagramHelper
  • Better loading and error state display

Working with this project

yarn to install dependencies

yarn test to run unit tests

yarn build to package for publishing

yarn build:watch to package and watch for changes

cd example && yarn start to run the example application

Licence

MIT, © 2021 Tom Blackwell

Package Sidebar

Install

npm i mycrossword

Weekly Downloads

5

Version

1.4.0

License

MIT

Unpacked Size

479 kB

Total Files

299

Last publish

Collaborators

  • t-blackwell