-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
improvement: enrich the feedback experience (#426)
- Loading branch information
1 parent
3580d5f
commit 53c5b38
Showing
5 changed files
with
326 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import classNames from "classnames"; | ||
import { DetailedHTMLProps, FC, InputHTMLAttributes } from "react"; | ||
|
||
interface FernRadioProps extends DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> { | ||
onChecked?: (checked: boolean) => void; | ||
} | ||
export const FernRadio: FC<FernRadioProps> = ({ className, children, onChecked, onChange, tabIndex, ...props }) => { | ||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
onChange?.(e); | ||
onChecked?.(e.target.checked); | ||
}; | ||
return ( | ||
<label className={classNames(className, "inline-flex cursor-pointer items-start group")} tabIndex={tabIndex}> | ||
<input type="radio" className="peer h-0 w-0 opacity-0" {...props} onChange={handleChange} /> | ||
<span className="ring-border-default-light dark:ring-border-default-dark peer-checked:bg-accent-primary dark:peer-checked:bg-accent-primary-dark peer-checked:after:bg-background dark:peer-checked:after:bg-background-dark group-hover:bg-tag-primary dark:group-hover:bg-tag-primary-dark relative mt-0.5 inline-block h-4 w-4 rounded-lg ring-1 ring-inset peer-checked:after:absolute peer-checked:after:left-1 peer-checked:after:top-1 peer-checked:after:h-2 peer-checked:after:w-2 peer-checked:after:rounded peer-checked:after:content-['']"></span> | ||
<div className="ml-2 flex-1">{children}</div> | ||
</label> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import classNames from "classnames"; | ||
import { DetailedHTMLProps, forwardRef, InputHTMLAttributes, useEffect, useImperativeHandle, useRef } from "react"; | ||
|
||
interface FernTextareaProps extends DetailedHTMLProps<InputHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement> { | ||
onValueChange?: (value: string) => void; | ||
value?: string; | ||
minLines?: number; | ||
} | ||
export const FernTextarea = forwardRef<HTMLTextAreaElement, FernTextareaProps>(function FernTextarea( | ||
{ className, onValueChange, minLines = 2, ...props }, | ||
ref, | ||
) { | ||
const inputRef = useRef<HTMLTextAreaElement>(null); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
useImperativeHandle(ref, () => inputRef.current!); | ||
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { | ||
onValueChange?.(e.target.value); | ||
props.onChange?.(e); | ||
}; | ||
useAutosizeTextArea(inputRef.current, props.value ?? "", minLines); | ||
return ( | ||
<textarea | ||
{...props} | ||
ref={inputRef} | ||
className={classNames( | ||
className, | ||
"border-border-default-light dark:border-border-default-dark focus-visible:ring-tag-primary focus-visible:dark:ring-tag-primary-dark focus-visible:border-accent-primary caret-accent-primary focus-visible:dark:border-accent-primary-dark rounded-md border bg-white p-2 text-sm focus:outline-none focus-visible:ring-4 dark:bg-white/10 resize-none", | ||
)} | ||
onChange={handleChange} | ||
/> | ||
); | ||
}); | ||
|
||
// Updates the height of a <textarea> when the value changes. | ||
function useAutosizeTextArea(textAreaRef: HTMLTextAreaElement | null, value: string, minLines: number): void { | ||
const minHeight = minLines * 20 + 16; | ||
useEffect(() => { | ||
if (textAreaRef) { | ||
// We need to reset the height momentarily to get the correct scrollHeight for the textarea | ||
textAreaRef.style.height = "0px"; | ||
const scrollHeight = textAreaRef.scrollHeight; | ||
|
||
// We then set the height directly, outside of the render loop | ||
// Trying to set this with state or a ref will product an incorrect value. | ||
textAreaRef.style.height = Math.max(minHeight, scrollHeight) + "px"; | ||
} | ||
}, [minHeight, textAreaRef, value]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.