Side Projects: React Digital Display - Part 2
Tags: react, typescript, javascript, sideprojects
Published: 2022-04-07 14:20:00

**In the first part of this series, we addressed the requirements and delved a bit into theory. After that, we looked at implementing the 7-segment display module component. Hold on tight this time, because we are going to use it and display some text.**

First, we need to create a `ReactDigitalDisplay` component. It will use the props interface that we declared earlier.

```tsx
export const ReactDigitalDisplay = ({
  text,
  module,
  size,
  height,
  unknownCharacterMode = 'omit',
}: ReactDigitalDisplayProps) => {
  return (
    <div>
      <h1>Welcome to ReactDigitalDisplay!</h1>
    </div>
  );
}
```

As you may recall, our `text` is entered as `string | string[]`. Let's normalize it. We'll use array, which will enable us to easily map the characters.

```tsx
const textArray = useMemo(
  () => (typeof text === 'string' ? text.split('') : text),
  [text]
);
```

Now we are going to render characters. Since JSX requires the component names to be capitalized, we will make a small change in the component definition.

```tsx
export const ReactDigitalDisplay = ({
  // ...
  module: ModuleComponent,
  // ...
}: ReactDigitalDisplayProps) => {
``` 

We have a component, and text, so we are able to render it. Our `ReactDigitalDisplay` will have the following `return` statement:

```tsx
return (
  <div className={styles.displayContainer}>
    <div className={styles.content}>
      {textArray.map((char) => (
        <ModuleComponent char={char} />
      ))}
    </div>
  </div>
);
```

Let's use it. For the first test, we're going to display a simple number - "1994". 

| ![Result of first test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmtehs50ih1rzllaa049.png) |
| :--: |
| *Component displaying "1994"* |

This is pretty impressive, but it has a problem. If we try to use characters that are not supported by the module, the entire component crashes. We need to fix that.

--- 

## Unsupported character handling

To do this, we're going to use the module's `charset` property to check for unsupported characters and filter them out. 

```tsx
const textToDisplay = useMemo(() => {
  const mappedArray = textArray
    .map((char) => {
      const isUnknownChar = !ModuleComponent.charset[char];

      if (isUnknownChar) {
        if (unknownCharacterMode === 'empty') {
          return SpecialChar.EMPTY;
        }
        if (unknownCharacterMode === 'omit') {
          return null;
        }
      }
      return char;
    })
    .reduce<string[]>(
      (arr, char) => [...arr, ...(char !== null ? [char] : [])],
      []
    );

  return Array.from(
    { ...mappedArray, length: size },
    (v) => v ?? SpecialChar.EMPTY
  );
}, [ModuleComponent.charset, textArray, size, unknownCharacterMode]);
```

First, we iterate through the text, and check each character. If it's not included in the `charset`, we replace it with `null` or `EMPTY`, based on `unknownCharacterMode`. At the end, we are filtering out the `null`s from our array and fill the array with `EMPTY` characters, so that its length is equal to `size`. 

I will now show you how it works. This time, I am going to use "S01E07" as the input. I also set the component `size` to 6.

| ![Result of second test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/34fp6ejqtc824e35f22c.png) |
| :--: |
| *Rendered text, with `unknownCharacterMode` set to `empty` on left and `omit` on right* |

---

## Component size

Next, we will scale our text to the specified height. For this we will use the dimensions of the display module.

```tsx
const dimensions = useMemo(() => {
  const containerHeight = height ?? ModuleComponent.height;
  const scale = containerHeight / ModuleComponent.height;
  const containerWidth = size * ModuleComponent.width * scale;

  return {
    width: containerWidth,
    height: containerHeight,
    scale,
  };
}, [height, ModuleComponent.width, ModuleComponent.height]);
```

Basically, we determine the dimensions for container with `n` modules (where `n` is our `size` prop), and then calculate the scale factor for the custom height. Next, we use the CSS `transform` property to apply the proper `scale`. Finally, we need to set the `width` and `height` of the container.

```tsx
return (
  <div
    className={styles.displayContainer}
    style={{ width: dimensions.width, height: dimensions.height }}
  >
    <div
      className={styles.content}
      style={{ transform: `scale(${dimensions.scale})` }}
    >
      {textToDisplay.map((char) => (
        <ModuleComponent char={char} />
      ))}
    </div>
  </div>
);
```

This is the result:

| ![Example of component scaling](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/718uq783wcbobux7md8w.png) |
| :--: |
| *Component original size (top) and scaled (bottom)* |

We have completed our main component. We will create another display module to show the customizability of display.

---

## Second module

To better highlight our best feature, which is the ability to use different components, we will create a dot matrix module. Please note that is supports some letters in addition to digits.

To begin, we are going to define the `DataType` of the module. It will be a 2-dimensional array of `boolean`s. 

```tsx
type Module5x7MatrixDataType = boolean[][];
```

We can now start implementing the module, the process of which is similar to the first one. Let's start with an empty component:

```tsx
export const Module5x7Matrix: DisplayModule<Module5x7MatrixDataType> = ({
  char,
}) => {
  return (
    <div>
      <h1>Welcome to Module5x7Matrix!</h1>
    </div>
  );
};
```

Our module will consist of 35 dots. We are going to generate `div`s and use the CSS grid for positioning. This is how the markup looks like:

```tsx
return (
  <div className={styles.module}>
    {[...Array(7)].map((row, rowIndex) =>
      [...Array(5)].map((column, columnIndex) => (
        <div
          className={clsx(
            styles.dot,
            Module5x7Matrix.charset?.[char]?.[rowIndex]?.[columnIndex] &&
              styles.active
          )}
        />
      ))
    )}
  </div>
);
```

[Here](https://github.com/kamil7x/side-project/blob/rdd-blog-part-2/libs/react-digital-display/src/lib/module-5x7-matrix/module-5x7-matrix.module.scss) you can see the full CSS code for this module.

Next, we are going to define the module's charset. As I mentioned earlier, it'll be an array of `boolean`s. For demonstration purposes, I have added the digits and letters A-F, so that we can display hexadecimal codes.

```tsx
export const Module5x7MatrixCharset: Charset<Module5x7MatrixDataType> = {
  [SpecialChar.EMPTY]: [],
  '0': [
    [false, true, true, true, false],
    [true, false, false, false, true],
    [true, false, false, true, true],
    [true, false, true, false, true],
    [true, true, false, false, true],
    [true, false, false, false, true],
    [false, true, true, true, false],
  ],
  // ...
  F: [
    [true, true, true, true, true],
    [true, false, false, false, false],
    [true, false, false, false, false],
    [true, true, true, true, false],
    [true, false, false, false, false],
    [true, false, false, false, false],
    [true, false, false, false, false],
  ],
};
```

Finally, we assign the charset and dimensions to the module.

```tsx
Module5x7Matrix.charset = Module5x7MatrixCharset;
Module5x7Matrix.width = 144;
Module5x7Matrix.height = 196;
```

Let's take a look at the final result. This time the input reads as "9BB901", which is the hex code of the module's background color:
 
| ![Final view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lwhd25zwddrxc4gqp91e.png) |
| :--: |
| *Text displayed using 5x7 dot matrix module* |

And here goes the same text with 7-segments module for comparison. Note that this module does not support letters and instead displays empty fields.

| ![Another view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgkliijsusuioqpsiwtm.png) |
| :--: |
| *Text display using 7-segments module* |

---

## Wrap up

That's it for today. We have created the main component and another display module. The full code is available [here](https://github.com/kamil7x/side-project/tree/rdd-blog-part-2). Stay tuned because in the next part, we will add some customization options to our component.

See you next time!

--- 

*Photo by [Donna Elliot](https://unsplash.com/@peakdancer) on [Unsplash](https://unsplash.com/)*