From f77fa3778302b35fcf314f990cd2ae2129e9f907 Mon Sep 17 00:00:00 2001 From: Amir Zahedi Date: Fri, 13 Oct 2023 10:19:43 +0800 Subject: [PATCH] feat: Improves EditableText width --- .../components/EditableText/EditableText.tsx | 38 +++++++++---------- .../lib/components/EditableText/stories.tsx | 7 ++++ .../overdrive/lib/components/Text/Text.tsx | 13 +++++-- .../overdrive/lib/utils/estimateTextWidth.ts | 17 +++++++++ 4 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 packages/overdrive/lib/utils/estimateTextWidth.ts diff --git a/packages/overdrive/lib/components/EditableText/EditableText.tsx b/packages/overdrive/lib/components/EditableText/EditableText.tsx index 936e0ac4f..e3ea9dfbf 100644 --- a/packages/overdrive/lib/components/EditableText/EditableText.tsx +++ b/packages/overdrive/lib/components/EditableText/EditableText.tsx @@ -1,17 +1,13 @@ import clsx from 'clsx'; import * as React from 'react'; -import { - ComponentProps, - forwardRef, - InputHTMLAttributes, - useState, -} from 'react'; +import { ComponentProps, forwardRef, InputHTMLAttributes, useRef, useState } from 'react'; -import { Box } from '../Box'; +import { Box, useBoxStyles } from '../Box'; import { Text, useTextStyles } from '../Text'; import * as inputStyles from '../private/InputBase/withEnhancedInput.css'; import * as styles from './EditableText.css'; + type BoxProps = Pick, 'display'>; type TextProps = Pick< ComponentProps, @@ -44,6 +40,7 @@ export const EditableText = forwardRef( }, ref, ) => { + const textRef = useRef(null); const [isEditing, setIsEditing] = useState(false); const onRequestEdit = () => setIsEditing(true); const textStyles = useTextStyles({ @@ -51,13 +48,11 @@ export const EditableText = forwardRef( colour, size, }); - const width = value ? `${value.toString().length}ch` : void 0; return ( setIsEditing(false)} @@ -67,7 +62,7 @@ export const EditableText = forwardRef( } }} > - {isEditing ? ( + {isEditing && ( ( textStyles, inputStyles.input.itself.root, )} - style={{ width }} + style={{ width: textRef.current?.offsetWidth }} /> - ) : ( - - {value} - )} + + {value} + ); }, diff --git a/packages/overdrive/lib/components/EditableText/stories.tsx b/packages/overdrive/lib/components/EditableText/stories.tsx index f3bf141ee..3eb817266 100644 --- a/packages/overdrive/lib/components/EditableText/stories.tsx +++ b/packages/overdrive/lib/components/EditableText/stories.tsx @@ -50,11 +50,18 @@ const dateProps: ComponentProps = { value: todayStr, type: 'date', }; +const narrowCharactersProps: ComponentProps = { + colour: 'muted', + value: 'Price is $111.01', + type: 'text', +}; export const text = template.bind(textProps); export const number = template.bind(numberProps); export const date = template.bind(dateProps); +export const narrowCharacters = template.bind(narrowCharactersProps); text.args = textProps; number.args = numberProps; date.args = dateProps; +narrowCharacters.args = narrowCharactersProps; diff --git a/packages/overdrive/lib/components/Text/Text.tsx b/packages/overdrive/lib/components/Text/Text.tsx index 094b2b38e..f09971ba3 100644 --- a/packages/overdrive/lib/components/Text/Text.tsx +++ b/packages/overdrive/lib/components/Text/Text.tsx @@ -1,5 +1,6 @@ -import type { FunctionComponent, ReactNode, CSSProperties } from 'react'; +import type { CSSProperties, ReactNode } from 'react'; import * as React from 'react'; +import { forwardRef } from 'react'; import type { BoxStyleProps } from '../Box'; import { Box } from '../Box'; @@ -18,7 +19,8 @@ export interface Props extends TextStyleProps { style?: CSSProperties; } -export const Text: FunctionComponent = ({ +export const Text = forwardRef( + ({ children, className = '', is: Component = 'span', @@ -32,9 +34,12 @@ export const Text: FunctionComponent = ({ size = '4', strong = false, style, -}) => ( + }, + ref, + ) => ( = ({ > {children} -); +)); export default Text; diff --git a/packages/overdrive/lib/utils/estimateTextWidth.ts b/packages/overdrive/lib/utils/estimateTextWidth.ts new file mode 100644 index 000000000..a9a2b460f --- /dev/null +++ b/packages/overdrive/lib/utils/estimateTextWidth.ts @@ -0,0 +1,17 @@ +// Estimate input width based on the width of each character in the value prop +const narrowChars = /[!"'()*+,./:;<=>?I[\\\]^_`b-vx-z{|}~\-]/g; +const wideChars = /[\dA-HJ-Z]/g; +const narrowCharWidth = 0.5; // Fraction of ch to assign to narrow characters +const defaultCharWidth = 0.8; // Fraction of ch to assign to narrow characters +export const estimateTextWidth = (value: string) => { + if (!value) return '0ch'; + const charWidths = value + .split('') + .map((char) => { + if (narrowChars.test(char)) return narrowCharWidth; + if (wideChars.test(char)) return 1; + return defaultCharWidth; + }, + ); + return charWidths.reduce((sum, width) => sum + width, 0) + 'ch'; +};