Create Community stage implemented
This commit is contained in:
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user