Skip to content

Commit

Permalink
feat: Improves EditableText width
Browse files Browse the repository at this point in the history
  • Loading branch information
amir-zahedi committed Oct 13, 2023
1 parent 2a2e4d8 commit f77fa37
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 25 deletions.
38 changes: 17 additions & 21 deletions packages/overdrive/lib/components/EditableText/EditableText.tsx
Original file line number Diff line number Diff line change
@@ -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<ComponentProps<typeof Text>, 'display'>;
type TextProps = Pick<
ComponentProps<typeof Text>,
Expand Down Expand Up @@ -44,20 +40,19 @@ export const EditableText = forwardRef<HTMLAnchorElement, Props>(
},
ref,
) => {
const textRef = useRef<HTMLSpanElement>(null);
const [isEditing, setIsEditing] = useState(false);
const onRequestEdit = () => setIsEditing(true);
const textStyles = useTextStyles({
is,
colour,
size,
});
const width = value ? `${value.toString().length}ch` : void 0;
return (
<Box
ref={ref}
display={display}
className={styles.root}
style={{ maxWidth: width }}
onClick={onRequestEdit}
onFocus={onRequestEdit}
onBlur={() => setIsEditing(false)}
Expand All @@ -67,7 +62,7 @@ export const EditableText = forwardRef<HTMLAnchorElement, Props>(
}
}}
>
{isEditing ? (
{isEditing && (
<Box
is="input"
{...inputProps}
Expand All @@ -77,19 +72,20 @@ export const EditableText = forwardRef<HTMLAnchorElement, Props>(
textStyles,
inputStyles.input.itself.root,
)}
style={{ width }}
style={{ width: textRef.current?.offsetWidth }}
/>
) : (
<Text
noWrap
is={is}
colour={colour}
className={clsx(textStyles, styles.text)}
style={{ maxWidth: width }}
>
{value}
</Text>
)}
<Text
noWrap
ref={textRef}
is={is}
colour={colour}
className={clsx(textStyles, styles.text, useBoxStyles({
display: isEditing ? 'none' : display,
}))}
>
{value}
</Text>
</Box>
);
},
Expand Down
7 changes: 7 additions & 0 deletions packages/overdrive/lib/components/EditableText/stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,18 @@ const dateProps: ComponentProps<typeof EditableText> = {
value: todayStr,
type: 'date',
};
const narrowCharactersProps: ComponentProps<typeof EditableText> = {
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;
13 changes: 9 additions & 4 deletions packages/overdrive/lib/components/Text/Text.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -18,7 +19,8 @@ export interface Props extends TextStyleProps {
style?: CSSProperties;
}

export const Text: FunctionComponent<Props> = ({
export const Text = forwardRef<HTMLElement, Props>(
({
children,
className = '',
is: Component = 'span',
Expand All @@ -32,9 +34,12 @@ export const Text: FunctionComponent<Props> = ({
size = '4',
strong = false,
style,
}) => (
},
ref,
) => (
<Box
is={Component}
ref={ref}
display={display}
textAlign={align}
className={[
Expand All @@ -53,6 +58,6 @@ export const Text: FunctionComponent<Props> = ({
>
{children}
</Box>
);
));

export default Text;
17 changes: 17 additions & 0 deletions packages/overdrive/lib/utils/estimateTextWidth.ts
Original file line number Diff line number Diff line change
@@ -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';
};

0 comments on commit f77fa37

Please sign in to comment.