Skip to content

Commit

Permalink
fix: creates a hook to wrap words larger than 15 characters
Browse files Browse the repository at this point in the history
  • Loading branch information
ramirlm committed Jan 15, 2025
1 parent 015473d commit 8c6f921
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 51 deletions.
75 changes: 26 additions & 49 deletions src/entities/activity/ui/items/RadioItem/RegularGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { useMemo } from 'react';

import RadioGroup from '@mui/material/RadioGroup';

import { RegularRadioOption } from './RegularRadioOption';
import { RadioItem } from '../../../lib';

import { Box } from '~/shared/ui';
import { splitList, useCustomMediaQuery } from '~/shared/utils';
import { useCustomMediaQuery } from '~/shared/utils';

type Props = {
options: RadioItem['responseValues']['options'];
Expand All @@ -28,54 +26,33 @@ export const RegularGrid = ({
}: Props) => {
const { lessThanSM } = useCustomMediaQuery();

const [evenColumn, oddColumn] = useMemo(() => {
return splitList(options);
}, [options]);

return (
<RadioGroup name={`${itemId}-radio regular-mode`}>
<Box display="flex" flex={1} gap="16px" flexDirection={lessThanSM ? 'column' : 'row'}>
<Box display="flex" flex={1} gap="16px" flexDirection="column">
{evenColumn.map((option) => {
return (
<RegularRadioOption
key={option.id}
id={option.id}
name={itemId}
value={option.value}
label={option.text}
onChange={() => onValueChange(String(option.value))}
description={option.tooltip}
image={option.image}
disabled={isDisabled}
defaultChecked={String(option.value) === value}
color={option.color}
replaceText={replaceText}
/>
);
})}
</Box>

<Box display="flex" flex={1} gap="16px" flexDirection="column">
{oddColumn.map((option) => {
return (
<RegularRadioOption
key={option.id}
id={option.id}
name={itemId}
value={option.value}
label={option.text}
onChange={() => onValueChange(String(option.value))}
description={option.tooltip}
image={option.image}
disabled={isDisabled}
defaultChecked={String(option.value) === value}
color={option.color}
replaceText={replaceText}
/>
);
})}
</Box>
<Box
display={lessThanSM ? 'flex' : 'grid'}
gridTemplateColumns="1fr 1fr"
gridAutoRows="1fr"
gap="16px"
flexDirection={'column'}
>
{options.map((option) => {
return (
<RegularRadioOption
key={option.id}
id={option.id}
name={itemId}
value={option.value}
label={option.text}
onChange={() => onValueChange(String(option.value))}
description={option.tooltip}
image={option.image}
disabled={isDisabled}
defaultChecked={String(option.value) === value}
color={option.color}
replaceText={replaceText}
/>
);
})}
</Box>
</RadioGroup>
);
Expand Down
18 changes: 16 additions & 2 deletions src/shared/ui/Items/SelectBase/SelectBaseText.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Theme } from '~/shared/constants';
import Text from '~/shared/ui/Text';
import { useCustomWordWrap } from '~/shared/utils/hooks/useCustomWordWrap';

type Props = {
text: string;
};

export const SelectBaseText = (props: Props) => {
const { processedWords } = useCustomWordWrap(props.text);

return (
<Text
variant="body1"
Expand All @@ -17,7 +20,7 @@ export const SelectBaseText = (props: Props) => {
sx={{
wordBreak: 'none',
cursor: 'pointer',
hyphens: 'none',
lineBreak: 'normal',
display: '-webkit-box',

// Using kebab-case (i.e. `-webkit-some-things`) would cause warnings
Expand All @@ -27,7 +30,18 @@ export const SelectBaseText = (props: Props) => {
webkitBoxOrient: 'vertical',
}}
>
{props.text}
{processedWords.map(({ word, needsWrap, ref }, index) => {
if (needsWrap)
return (
<span ref={ref} style={{ wordBreak: 'break-word' }} key={index}>
{word.split('').map((letter: string, index) => (
<span key={index}>{letter}</span>
))}
</span>
);

return `${word} `;
})}
</Text>
);
};
76 changes: 76 additions & 0 deletions src/shared/utils/hooks/useCustomWordWrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useEffect, useRef, useState } from 'react';

export const useCustomWordWrap = (text: string) => {
const textAsArray = text.split(' ');
const mustBreakWord = useRef<HTMLElement>(null);
const originalWord = useRef<string>('');
const [resize, setResize] = useState(0);
const debounceTimeout = useRef<NodeJS.Timeout>();

useEffect(() => {
const handleResize = () => {
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}

debounceTimeout.current = setTimeout(() => {
setResize((prev) => prev + 1);
}, 300);
};

window.addEventListener('resize', handleResize);

return () => {
window.removeEventListener('resize', handleResize);
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
};
}, []);

useEffect(() => {
if (mustBreakWord.current) {
if (!originalWord.current && mustBreakWord.current.textContent) {
originalWord.current = mustBreakWord.current.textContent;
}

if (originalWord.current) {
const characters = originalWord.current.split('');
mustBreakWord.current.innerHTML = characters
.map((letter) => `<span>${letter}</span>`)
.join('');
}

const mustBreakWordCharacters = Array.from(mustBreakWord.current.children);
for (let i = 0; i < mustBreakWordCharacters.length; i++) {
const currentCharacter = mustBreakWordCharacters[i] as HTMLElement;
const previousCharacter = i > 0 ? (mustBreakWordCharacters[i - 1] as HTMLElement) : null;

if (
previousCharacter &&
!previousCharacter.innerText.includes('-') &&
currentCharacter.offsetTop > previousCharacter?.offsetTop
) {
(mustBreakWordCharacters[i - 3] as HTMLElement).innerText += '-\n';
}
}
}
}, [resize]);

const processedWords = textAsArray.map((word: string, index) => {

Check warning on line 60 in src/shared/utils/hooks/useCustomWordWrap.ts

View workflow job for this annotation

GitHub Actions / Lint

'index' is defined but never used. Allowed unused args must match /^_/u
if (word.length > 15)
return {
word,
needsWrap: true,
ref: mustBreakWord,
};

return {
word,
needsWrap: false,
ref: null,
};
});

return { processedWords };
};

0 comments on commit 8c6f921

Please sign in to comment.