Files
community-rule/app/(app)/create/components/ModalTextAreaField.tsx
T

153 lines
4.7 KiB
TypeScript

"use client";
/**
* Shared labelled text area for create-flow modal sections.
* Figma: "Step Modal" Text Area Section (23746:49780) when `generateDraftText`
* is set — tall compose field + Generate draft control. Otherwise the
* embedded Control / Text Area pattern.
*/
import { memo, useId } from "react";
import TextArea from "../../../components/controls/TextArea";
import InputLabel from "../../../components/type/InputLabel";
export interface ModalTextAreaFieldProps {
/** Label rendered above the text area. */
label: string;
/** Show the help "?" icon next to the label. Off until tooltip behavior ships. */
helpIcon?: boolean;
/** Hide the label when the modal title already asks the question. */
hideLabel?: boolean;
/** Current text value. */
value: string;
/** Fired on every change with the new value (no event). */
onChange: (_value: string) => void;
/** Optional rows for the underlying `<textarea>` (default 4). */
rows?: number;
/** Optional placeholder. */
placeholder?: string;
/** Disable the field. */
disabled?: boolean;
/**
* Catalog/preset copy inserted by Generate draft. When a non-empty string
* is passed, the Figma compose field + ghost control are shown.
*/
generateDraftText?: string;
/** Visible label for the Generate draft control. */
generateDraftLabel?: string;
/** Hint beside Generate draft. */
generateDraftHint?: string;
/**
* Shown under a filled field instead of Generate draft (picked value
* field view).
*/
filledHint?: string;
autoFocus?: boolean;
className?: string;
}
function PlusGlyph() {
return (
<svg
aria-hidden
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="shrink-0 text-[var(--color-content-default-brand-primary,#fefcc9)]"
>
<path
d="M7 3V11M3 7H11"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
function ModalTextAreaFieldComponent({
label,
helpIcon = false,
hideLabel = false,
value,
onChange,
rows = 4,
placeholder,
disabled = false,
generateDraftText,
generateDraftLabel,
generateDraftHint,
filledHint,
autoFocus = false,
className = "",
}: ModalTextAreaFieldProps) {
const labelId = useId();
const draftSource = generateDraftText?.trim() ?? "";
const filled = value.trim().length > 0;
const showFilledHint = filled && Boolean(filledHint) && !disabled;
const showGenerateDraft =
!showFilledHint &&
draftSource.length > 0 &&
Boolean(generateDraftLabel);
const compose = showGenerateDraft || showFilledHint || hideLabel;
return (
<div
className={`flex flex-col ${compose ? "gap-[12px]" : "gap-2"} ${className}`.trim()}
>
{hideLabel ? null : (
<div id={labelId}>
<InputLabel
label={label}
helpIcon={helpIcon}
size="s"
palette="default"
/>
</div>
)}
<TextArea
formHeader={false}
value={value}
onChange={(e) => onChange(e.target.value)}
size="large"
rows={compose ? 12 : rows}
appearance={compose ? "compose" : "embedded"}
placeholder={placeholder}
disabled={disabled}
autoFocus={autoFocus}
aria-label={hideLabel ? label : undefined}
aria-labelledby={hideLabel ? undefined : labelId}
/>
{showGenerateDraft && !disabled ? (
<div className="flex items-center">
<button
type="button"
onClick={() => onChange(draftSource)}
className="flex shrink-0 items-center justify-center gap-[var(--space-050)] overflow-clip rounded-[var(--radius-full)] px-[var(--space-250)] py-[var(--space-200)] text-x-small-label text-[color:var(--color-content-default-primary,white)] hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-default-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-transparent"
>
<PlusGlyph />
{generateDraftLabel}
</button>
{generateDraftHint ? (
<p className="text-[length:var(--sizing-300)] leading-normal text-[color:var(--color-content-default-tertiary,#b4b4b4)]">
{generateDraftHint}
</p>
) : null}
</div>
) : null}
{showFilledHint ? (
<p className="text-[length:var(--sizing-300)] leading-normal text-[color:var(--color-content-default-tertiary,#b4b4b4)]">
{filledHint}
</p>
) : null}
</div>
);
}
ModalTextAreaFieldComponent.displayName = "ModalTextAreaField";
export default memo(ModalTextAreaFieldComponent);