Name stays required; description is a labelled optional textarea. Email uses native validity and form submit. Drop the dead workshop link and fix a few copy errors. Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
4.8 KiB
TypeScript
83 lines
4.8 KiB
TypeScript
"use client";
|
|
|
|
import { useId } from "react";
|
|
import type { InputWithCounterProps } from "./InputWithCounter.types";
|
|
|
|
export function InputWithCounterView({
|
|
label,
|
|
placeholder,
|
|
value,
|
|
onChange,
|
|
maxLength,
|
|
showHelpIcon = false,
|
|
className = "",
|
|
inputClassName = "",
|
|
}: InputWithCounterProps) {
|
|
const inputId = useId();
|
|
return (
|
|
<div className={`space-y-[var(--spacing-scale-008)] ${className}`}>
|
|
{/* Label with help icon */}
|
|
{label && (
|
|
<div className="flex items-center gap-[var(--spacing-scale-002)]">
|
|
<label
|
|
htmlFor={inputId}
|
|
className="text-[14px] leading-[20px] font-medium text-[var(--color-content-default-primary)]"
|
|
>
|
|
{label}
|
|
</label>
|
|
{showHelpIcon && (
|
|
<svg
|
|
width="12"
|
|
height="12"
|
|
viewBox="0 0 12 12"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
className="shrink-0 w-[12px] h-[12px]"
|
|
>
|
|
<mask
|
|
id="mask0_21296_8257"
|
|
style={{ maskType: "alpha" }}
|
|
maskUnits="userSpaceOnUse"
|
|
x="0"
|
|
y="0"
|
|
width="12"
|
|
height="12"
|
|
>
|
|
<rect width="12" height="12" fill="#D9D9D9" />
|
|
</mask>
|
|
<g mask="url(#mask0_21296_8257)">
|
|
<path
|
|
d="M5.99449 8.80766C6.13725 8.80766 6.25784 8.75838 6.35624 8.6598C6.45463 8.56123 6.50383 8.44055 6.50383 8.29779C6.50383 8.15502 6.45454 8.03444 6.35596 7.93605C6.25739 7.83765 6.13672 7.78845 5.99395 7.78845C5.85118 7.78845 5.7306 7.83774 5.63221 7.93631C5.53381 8.03489 5.48461 8.15556 5.48461 8.29833C5.48461 8.44109 5.5339 8.56168 5.63248 8.66008C5.73105 8.75847 5.85172 8.80766 5.99449 8.80766ZM5.64038 7.01729H6.34421C6.35062 6.77114 6.38668 6.5745 6.45239 6.42739C6.51809 6.28028 6.67754 6.08525 6.93075 5.8423C7.15062 5.62243 7.31905 5.41938 7.43604 5.23316C7.55302 5.04695 7.61151 4.82703 7.61151 4.57343C7.61151 4.14307 7.45687 3.80689 7.14759 3.5649C6.8383 3.32292 6.47243 3.20193 6.04999 3.20193C5.63269 3.20193 5.28734 3.3133 5.01394 3.53606C4.74055 3.75881 4.54552 4.02115 4.42885 4.32306L5.07114 4.58075C5.13204 4.41473 5.2362 4.25303 5.38364 4.09566C5.53108 3.93829 5.74999 3.8596 6.04038 3.8596C6.33589 3.8596 6.55432 3.94053 6.69566 4.1024C6.83701 4.26426 6.90769 4.4423 6.90769 4.63654C6.90769 4.80641 6.85929 4.96185 6.76249 5.10288C6.6657 5.2439 6.5423 5.38012 6.3923 5.51154C6.0641 5.80769 5.85673 6.0439 5.77019 6.22019C5.68365 6.39647 5.64038 6.66217 5.64038 7.01729ZM6.00082 10.75C5.34386 10.75 4.72634 10.6253 4.14828 10.376C3.5702 10.1266 3.06736 9.78827 2.63975 9.36085C2.21213 8.93343 1.8736 8.43081 1.62416 7.85299C1.37472 7.27518 1.25 6.65779 1.25 6.00083C1.25 5.34386 1.37467 4.72634 1.624 4.14828C1.87333 3.5702 2.21171 3.06736 2.63913 2.63975C3.06655 2.21213 3.56917 1.8736 4.14699 1.62416C4.7248 1.37472 5.34218 1.25 5.99915 1.25C6.65612 1.25 7.27363 1.37467 7.8517 1.624C8.42978 1.87333 8.93262 2.21171 9.36023 2.63913C9.78784 3.06655 10.1264 3.56917 10.3758 4.14699C10.6253 4.7248 10.75 5.34218 10.75 5.99915C10.75 6.65612 10.6253 7.27363 10.376 7.8517C10.1266 8.42978 9.78827 8.93262 9.36085 9.36023C8.93343 9.78784 8.43081 10.1264 7.85299 10.3758C7.27518 10.6253 6.65779 10.75 6.00082 10.75ZM5.99999 9.99999C7.11665 9.99999 8.06249 9.61249 8.83749 8.83749C9.61249 8.06249 9.99999 7.11665 9.99999 5.99999C9.99999 4.88332 9.61249 3.93749 8.83749 3.16249C8.06249 2.38749 7.11665 1.99999 5.99999 1.99999C4.88332 1.99999 3.93749 2.38749 3.16249 3.16249C2.38749 3.93749 1.99999 4.88332 1.99999 5.99999C1.99999 7.11665 2.38749 8.06249 3.16249 8.83749C3.93749 9.61249 4.88332 9.99999 5.99999 9.99999Z"
|
|
fill="var(--color-content-brand-darker-accent-2)"
|
|
/>
|
|
</g>
|
|
</svg>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Input field */}
|
|
<div className="relative">
|
|
<input
|
|
id={inputId}
|
|
type="text"
|
|
placeholder={placeholder}
|
|
value={value}
|
|
onChange={(e) => {
|
|
if (e.target.value.length <= maxLength) {
|
|
onChange(e.target.value);
|
|
}
|
|
}}
|
|
maxLength={maxLength}
|
|
className={`w-full h-[36px] px-[12px] py-[8px] bg-[var(--color-surface-default-primary)] border-2 border-[var(--color-border-default-tertiary)] rounded-[var(--measures-radius-medium,8px)] text-medium-paragraph text-[var(--color-content-default-primary)] placeholder:text-[var(--color-content-default-tertiary)] focus:outline-none ${inputClassName}`}
|
|
/>
|
|
</div>
|
|
|
|
{/* Character counter */}
|
|
<p className="text-x-small-paragraph text-[var(--color-content-default-tertiary)]">
|
|
{value.length}/{maxLength}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|