Side Projects: React Digital Display - Part 2
Kamil Trusiak
Posted on April 7, 2022
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.
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.
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.
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:
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".
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.
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.
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.
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.
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:
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.
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:
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:
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 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.
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.
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:
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.
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. Stay tuned because in the next part, we will add some customization options to our component.
See you next time!
Photo by Donna Elliot on Unsplash
Posted on April 7, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.