Create Community stage implemented

This commit is contained in:
adilallo
2026-04-14 09:22:03 -06:00
parent a0de78c020
commit f8255bc2c7
73 changed files with 1105 additions and 392 deletions
@@ -92,7 +92,7 @@ export interface TextAreaViewProps {
handleChange: (_e: React.ChangeEvent<HTMLTextAreaElement>) => void;
handleFocus: (_e: React.FocusEvent<HTMLTextAreaElement>) => void;
handleBlur: (_e: React.FocusEvent<HTMLTextAreaElement>) => void;
textHint?: boolean;
textHint?: boolean | string;
formHeader?: boolean;
showHelpIcon?: boolean;
appearance?: "default" | "embedded";
@@ -78,13 +78,13 @@ export const TextAreaView = forwardRef<HTMLTextAreaElement, TextAreaViewProps>(
{...props}
/>
</div>
{textHint && (
{textHint ? (
<div className="flex items-start relative shrink-0 w-full">
<p className="flex-[1_0_0] font-inter font-normal leading-[16px] min-h-px min-w-px relative text-[color:var(--color-content-default-tertiary,#b4b4b4)] text-[length:var(--sizing-300,12px)]">
Hint text here
{typeof textHint === "string" ? textHint : "Hint text here"}
</p>
</div>
)}
) : null}
</div>
);
},
@@ -5,12 +5,20 @@ import UploadView from "./Upload.view";
import type { UploadProps } from "./Upload.types";
const UploadContainer = memo<UploadProps>(
({ active = true, label, showHelpIcon = true, onClick, className = "" }) => {
({
active = true,
label,
showHelpIcon = true,
hintText = "Add image from your device",
onClick,
className = "",
}) => {
return (
<UploadView
active={active}
label={label}
showHelpIcon={showHelpIcon}
hintText={hintText}
onClick={onClick}
className={className}
/>
@@ -15,6 +15,11 @@ export interface UploadProps {
* @default true
*/
showHelpIcon?: boolean;
/**
* Copy beside the upload button (Figma Flow — Upload `20094:41524`).
* @default "Add image from your device"
*/
hintText?: string;
/**
* Callback when upload button is clicked
*/
@@ -29,6 +34,7 @@ export interface UploadViewProps {
active: boolean;
label?: string;
showHelpIcon: boolean;
hintText: string;
onClick?: () => void;
className: string;
}
@@ -8,6 +8,7 @@ function UploadView({
active = true,
label,
showHelpIcon = true,
hintText,
onClick,
className = "",
}: UploadViewProps) {
@@ -54,7 +55,7 @@ function UploadView({
<button
type="button"
onClick={onClick}
className={`${buttonBgClass} flex gap-[var(--measures-spacing-150,6px)] items-center justify-center overflow-clip p-[var(--measures-spacing-300,12px)] rounded-[var(--measures-radius-full,9999px)] shrink-0 hover:opacity-80 transition-opacity`}
className={`${buttonBgClass} flex gap-[var(--measures-spacing-150,6px)] items-center justify-center overflow-clip px-[var(--space-400,16px)] py-[var(--measures-spacing-300,12px)] rounded-[var(--measures-radius-full,9999px)] shrink-0 hover:opacity-80 transition-opacity`}
aria-label="Upload"
>
{/* Upload icon */}
@@ -105,9 +106,7 @@ function UploadView({
<div
className={`flex flex-[1_0_0] flex-col font-inter font-normal h-[32px] justify-center leading-[0] min-h-px min-w-px relative text-[length:var(--sizing-350,14px)] ${descriptionTextColor}`}
>
<p className="leading-[20px] whitespace-pre-wrap">
Add images, PDFs, and other files to the policy
</p>
<p className="leading-[20px] whitespace-pre-wrap">{hintText}</p>
</div>
</div>
</div>
@@ -1,11 +1,13 @@
"use client";
import { memo } from "react";
import { normalizeProportionBarVariant } from "../../../../lib/propNormalization";
import { ProportionBarView } from "./ProportionBar.view";
import type { ProportionBarProps } from "./ProportionBar.types";
const ProportionBarContainer = memo<ProportionBarProps>(
({ progress = "3-2", className = "" }) => {
({ progress = "3-2", className = "", variant: variantProp }) => {
const variant = normalizeProportionBarVariant(variantProp);
const barClasses = `h-[8px] relative w-full`;
return (
@@ -13,6 +15,7 @@ const ProportionBarContainer = memo<ProportionBarProps>(
progress={progress}
className={className}
barClasses={barClasses}
variant={variant}
/>
);
},
@@ -1,3 +1,5 @@
import type { ProportionBarVariantValue } from "../../../../lib/propNormalization";
export type ProportionBarState =
| "1-0"
| "1-1"
@@ -12,13 +14,20 @@ export type ProportionBarState =
| "3-1"
| "3-2";
export type ProportionBarVariant = ProportionBarVariantValue;
export interface ProportionBarProps {
progress?: ProportionBarState;
className?: string;
/**
* `segmented` (Figma: create-flow footer): pill-shaped partial fills inside each segment.
*/
variant?: ProportionBarVariant;
}
export interface ProportionBarViewProps {
progress: ProportionBarState;
className: string;
barClasses: string;
variant: "default" | "segmented";
}
@@ -4,9 +4,11 @@ export function ProportionBarView({
progress,
className,
barClasses,
variant,
}: ProportionBarViewProps) {
// Proportion bar type
const [fullSegments, partialSegment] = progress.split("-").map(Number);
const segmented = variant === "segmented";
// Calculate total progress:
// - For 1-X: first section is (X+1)/6 filled
// - For 2-X: first section full, second section X/3 filled
@@ -58,7 +60,11 @@ export function ProportionBarView({
<div className="flex-1 h-full relative">
{fullSegments === 1 ? (
<div
className="absolute inset-y-0 left-0 bg-[var(--color-content-default-brand-primary)] rounded-l-[var(--radius-full)]"
className={`absolute inset-y-0 left-0 bg-[var(--color-content-default-brand-primary)] rounded-l-[var(--radius-full)] ${
segmented && partialSegment < 5
? "rounded-r-[var(--radius-full)]"
: ""
}`.trim()}
style={{ width: `${((partialSegment + 1) / 6) * 100}%` }}
/>
) : fullSegments >= 2 ? (
@@ -70,7 +76,11 @@ export function ProportionBarView({
{fullSegments === 2 ? (
partialSegment > 0 ? (
<div
className="absolute inset-y-0 left-0 bg-[var(--color-content-default-brand-primary)]"
className={`absolute inset-y-0 left-0 bg-[var(--color-content-default-brand-primary)] ${
segmented
? "rounded-l-[var(--radius-full)] rounded-r-[var(--radius-full)]"
: ""
}`.trim()}
style={{ width: `${(partialSegment / 3) * 100}%` }}
/>
) : null
@@ -84,8 +94,12 @@ export function ProportionBarView({
{fullSegments === 3 && partialSegment > 0 ? (
<div
className={`absolute inset-y-0 left-0 bg-[var(--color-content-default-brand-primary)] ${
partialSegment >= 3 ? "rounded-r-[var(--radius-full)]" : ""
}`}
segmented
? "rounded-l-[var(--radius-full)] rounded-r-[var(--radius-full)]"
: partialSegment >= 3
? "rounded-r-[var(--radius-full)]"
: ""
}`.trim()}
style={{ width: `${Math.min((partialSegment / 3) * 100, 100)}%` }}
/>
) : null}
@@ -1,3 +1,5 @@
import type { ReactNode } from "react";
export type HeaderLockupJustificationValue =
| "left"
| "center"
@@ -16,9 +18,9 @@ export interface HeaderLockupProps {
*/
title: string;
/**
* Description text (optional)
* Description (optional). String for plain copy, or ReactNode for rich inline content (e.g. linked words).
*/
description?: string;
description?: ReactNode;
/**
* Text justification. Accepts both PascalCase (Figma) and lowercase (codebase).
* Figma uses PascalCase, codebase uses lowercase - both are supported.
@@ -38,7 +40,7 @@ export interface HeaderLockupProps {
export interface HeaderLockupViewProps {
title: string;
description?: string;
description?: ReactNode;
justification: "left" | "center";
size: "L" | "M";
palette: "default" | "inverse";
@@ -43,17 +43,18 @@ function HeaderLockupView({
</div>
{/* Description */}
{description && (
<p
className={`font-inter font-normal max-w-[640px] overflow-hidden relative shrink-0 ${descriptionColorClass} text-ellipsis w-full whitespace-pre-wrap ${
isLeft ? "" : "text-center"
} ${
isL ? "text-[18px] leading-[1.3]" : "text-[14px] leading-[20px]"
}`}
>
{description}
</p>
)}
{description != null &&
!(typeof description === "string" && description.length === 0) && (
<p
className={`font-inter font-normal max-w-[640px] overflow-hidden relative shrink-0 ${descriptionColorClass} text-ellipsis w-full whitespace-pre-wrap ${
isLeft ? "" : "text-center"
} ${
isL ? "text-[18px] leading-[1.3]" : "text-[14px] leading-[20px]"
}`}
>
{description}
</p>
)}
</div>
);
}
@@ -5,11 +5,20 @@ import { CreateFlowFooterView } from "./CreateFlowFooter.view";
import type { CreateFlowFooterProps } from "./CreateFlowFooter.types";
const CreateFlowFooterContainer = memo<CreateFlowFooterProps>(
({ secondButton, progressBar = true, onBackClick, className = "" }) => {
({
secondButton,
progressBar = true,
proportionBarProgress,
proportionBarVariant,
onBackClick,
className = "",
}) => {
return (
<CreateFlowFooterView
secondButton={secondButton}
progressBar={progressBar}
proportionBarProgress={proportionBarProgress}
proportionBarVariant={proportionBarVariant}
onBackClick={onBackClick}
className={className}
/>
@@ -1,3 +1,8 @@
import type {
ProportionBarState,
ProportionBarVariant,
} from "../../progress/ProportionBar/ProportionBar.types";
/**
* Type definitions for CreateFlowFooter component
*
@@ -13,6 +18,16 @@ export interface CreateFlowFooterProps {
* @default true
*/
progressBar?: boolean;
/**
* `ProportionBar` state when the bar is shown (driven by create-flow step).
* @default "1-0"
*/
proportionBarProgress?: ProportionBarState;
/**
* `ProportionBar` layout variant (Figma create-flow footer uses `segmented`).
* @default "default"
*/
proportionBarVariant?: ProportionBarVariant;
/**
* Callback function for Back button click
*/
@@ -1,3 +1,4 @@
import { normalizeProportionBarVariant } from "../../../../lib/propNormalization";
import ProportionBar from "../../progress/ProportionBar";
import Button from "../../buttons/Button";
import type { CreateFlowFooterProps } from "./CreateFlowFooter.types";
@@ -5,9 +6,14 @@ import type { CreateFlowFooterProps } from "./CreateFlowFooter.types";
export function CreateFlowFooterView({
secondButton,
progressBar = true,
proportionBarProgress = "1-0",
proportionBarVariant: proportionBarVariantProp,
onBackClick,
className = "",
}: CreateFlowFooterProps) {
const proportionBarVariant = normalizeProportionBarVariant(
proportionBarVariantProp,
);
return (
<footer
className={`bg-black w-full ${className}`}
@@ -17,7 +23,10 @@ export function CreateFlowFooterView({
{/* Progress Bar - Top */}
{progressBar && (
<div className="px-[var(--spacing-measures-spacing-500,20px)] md:px-[var(--spacing-measures-spacing-1200,48px)] pt-[var(--spacing-measures-spacing-300,12px)]">
<ProportionBar progress="1-0" />
<ProportionBar
progress={proportionBarProgress}
variant={proportionBarVariant}
/>
</div>
)}