Align props with figma
This commit is contained in:
@@ -3,16 +3,20 @@
|
||||
import { memo } from "react";
|
||||
import { AlertView } from "./Alert.view";
|
||||
import type { AlertProps } from "./Alert.types";
|
||||
import { normalizeAlertStatus, normalizeAlertType } from "../../../lib/propNormalization";
|
||||
|
||||
const AlertContainer = memo<AlertProps>(
|
||||
({
|
||||
title,
|
||||
description,
|
||||
status = "default",
|
||||
type = "toast",
|
||||
status: statusProp = "default",
|
||||
type: typeProp = "toast",
|
||||
onClose,
|
||||
className = "",
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const status = normalizeAlertStatus(statusProp);
|
||||
const type = normalizeAlertType(typeProp);
|
||||
// Determine background and border colors based on status and type
|
||||
const getStatusStyles = () => {
|
||||
switch (status) {
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
export type AlertStatusValue =
|
||||
| "default"
|
||||
| "positive"
|
||||
| "warning"
|
||||
| "danger"
|
||||
| "Default"
|
||||
| "Positive"
|
||||
| "Warning"
|
||||
| "Danger";
|
||||
|
||||
export type AlertTypeValue = "toast" | "banner" | "Toast" | "Banner";
|
||||
|
||||
export interface AlertProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
status?: "default" | "positive" | "warning" | "danger";
|
||||
type?: "toast" | "banner";
|
||||
/**
|
||||
* Alert status. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
status?: AlertStatusValue;
|
||||
/**
|
||||
* Alert type. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
type?: AlertTypeValue;
|
||||
onClose?: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
AskOrganizerProps,
|
||||
AskOrganizerVariant,
|
||||
} from "./AskOrganizer.types";
|
||||
import { normalizeAskOrganizerVariant } from "../../../lib/propNormalization";
|
||||
|
||||
const VARIANT_STYLES: Record<
|
||||
AskOrganizerVariant,
|
||||
@@ -39,9 +40,11 @@ const AskOrganizerContainer = memo<AskOrganizerProps>(
|
||||
buttonText,
|
||||
buttonHref,
|
||||
className = "",
|
||||
variant = "centered",
|
||||
variant: variantProp = "centered",
|
||||
onContactClick,
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const variant = normalizeAskOrganizerVariant(variantProp) as AskOrganizerVariant;
|
||||
const t = useTranslation();
|
||||
const defaultButtonText = buttonText ?? t("askOrganizer.buttonText");
|
||||
const defaultButtonHref = buttonHref ?? t("askOrganizer.buttonHref");
|
||||
|
||||
@@ -4,7 +4,11 @@ export type AskOrganizerVariant =
|
||||
| "centered"
|
||||
| "left-aligned"
|
||||
| "compact"
|
||||
| "inverse";
|
||||
| "inverse"
|
||||
| "Centered"
|
||||
| "Left-Aligned"
|
||||
| "Compact"
|
||||
| "Inverse";
|
||||
|
||||
export interface AskOrganizerProps {
|
||||
title?: string;
|
||||
@@ -13,6 +17,10 @@ export interface AskOrganizerProps {
|
||||
buttonText?: string;
|
||||
buttonHref?: string;
|
||||
className?: string;
|
||||
/**
|
||||
* Ask organizer variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
variant?: AskOrganizerVariant;
|
||||
onContactClick?: (_data: {
|
||||
event: string;
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import { memo } from "react";
|
||||
import { normalizeSize } from "../../lib/propNormalization";
|
||||
|
||||
export type AvatarSizeValue = "small" | "medium" | "large" | "xlarge" | "Small" | "Medium" | "Large" | "XLarge";
|
||||
|
||||
interface AvatarProps extends React.ImgHTMLAttributes<HTMLImageElement> {
|
||||
src: string;
|
||||
alt: string;
|
||||
size?: "small" | "medium" | "large" | "xlarge";
|
||||
/**
|
||||
* Avatar size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: AvatarSizeValue;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Avatar = memo<AvatarProps>(
|
||||
({ src, alt, size = "small", className = "", ...props }) => {
|
||||
({ src, alt, size: sizeProp = "small", className = "", ...props }) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const size = normalizeSize(sizeProp, "small");
|
||||
const sizeStyles: Record<string, string> = {
|
||||
small: "w-[var(--spacing-scale-016)] h-[var(--spacing-scale-016)]",
|
||||
medium: "w-[18px] h-[18px]",
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import { memo } from "react";
|
||||
import { normalizeSize } from "../../lib/propNormalization";
|
||||
|
||||
export type AvatarContainerSizeValue = "small" | "medium" | "large" | "xlarge" | "Small" | "Medium" | "Large" | "XLarge";
|
||||
|
||||
interface AvatarContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
size?: "small" | "medium" | "large" | "xlarge";
|
||||
/**
|
||||
* Avatar container size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: AvatarContainerSizeValue;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AvatarContainer = memo<AvatarContainerProps>(
|
||||
({ children, size = "small", className = "", ...props }) => {
|
||||
({ children, size: sizeProp = "small", className = "", ...props }) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const size = normalizeSize(sizeProp, "small");
|
||||
const sizeStyles: Record<string, string> = {
|
||||
small: "flex -space-x-[var(--spacing-scale-008)]",
|
||||
medium: "flex -space-x-[9px]",
|
||||
|
||||
+17
-12
@@ -1,17 +1,19 @@
|
||||
import { memo } from "react";
|
||||
import type { VariantValue, SizeValue } from "../../lib/propNormalization";
|
||||
import { normalizeVariant, normalizeSize } from "../../lib/propNormalization";
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: React.ReactNode;
|
||||
variant?:
|
||||
| "filled"
|
||||
| "filled-inverse"
|
||||
| "outline"
|
||||
| "outline-inverse"
|
||||
| "ghost"
|
||||
| "ghost-inverse"
|
||||
| "danger"
|
||||
| "danger-inverse";
|
||||
size?: "xsmall" | "small" | "medium" | "large" | "xlarge";
|
||||
/**
|
||||
* Button variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
variant?: VariantValue;
|
||||
/**
|
||||
* Button size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: SizeValue;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
type?: "button" | "submit" | "reset";
|
||||
@@ -27,8 +29,8 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
const Button = memo<ButtonProps>(
|
||||
({
|
||||
children,
|
||||
variant = "filled",
|
||||
size = "xsmall",
|
||||
variant: variantProp = "filled",
|
||||
size: sizeProp = "xsmall",
|
||||
className = "",
|
||||
disabled = false,
|
||||
type = "button",
|
||||
@@ -39,6 +41,9 @@ const Button = memo<ButtonProps>(
|
||||
ariaLabel,
|
||||
...props
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const variant = normalizeVariant(variantProp);
|
||||
const size = normalizeSize(sizeProp);
|
||||
const sizeStyles: Record<string, string> = {
|
||||
xsmall:
|
||||
"p-[var(--spacing-scale-006)] gap-[var(--spacing-scale-002)]",
|
||||
|
||||
@@ -4,12 +4,13 @@ import { memo } from "react";
|
||||
import { useComponentId } from "../../hooks";
|
||||
import { CheckboxView } from "./Checkbox.view";
|
||||
import type { CheckboxProps } from "./Checkbox.types";
|
||||
import { normalizeMode, normalizeState } from "../../../lib/propNormalization";
|
||||
|
||||
const CheckboxContainer = memo<CheckboxProps>(
|
||||
({
|
||||
checked = false,
|
||||
mode = "standard",
|
||||
state = "default",
|
||||
mode: modeProp = "standard",
|
||||
state: stateProp = "default",
|
||||
disabled = false,
|
||||
label,
|
||||
className = "",
|
||||
@@ -20,6 +21,10 @@ const CheckboxContainer = memo<CheckboxProps>(
|
||||
ariaLabel,
|
||||
...props
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const mode = normalizeMode(modeProp);
|
||||
const state = normalizeState(stateProp);
|
||||
|
||||
const isInverse = mode === "inverse";
|
||||
const isStandard = mode === "standard";
|
||||
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
import type { ModeValue, StateValue } from "../../../lib/propNormalization";
|
||||
|
||||
export interface CheckboxProps {
|
||||
checked?: boolean;
|
||||
mode?: "standard" | "inverse";
|
||||
state?: "default" | "hover" | "focus";
|
||||
/**
|
||||
* Mode variant. Accepts both "standard"/"Standard" and "inverse"/"Inverse" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
mode?: ModeValue;
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "hover"/"Hover", "focus"/"Focus" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: StateValue;
|
||||
disabled?: boolean;
|
||||
label?: string;
|
||||
className?: string;
|
||||
|
||||
@@ -3,17 +3,20 @@
|
||||
import { memo, useCallback, useId, useState } from "react";
|
||||
import { CheckboxGroupView } from "./CheckboxGroup.view";
|
||||
import type { CheckboxGroupProps } from "./CheckboxGroup.types";
|
||||
import { normalizeMode } from "../../../lib/propNormalization";
|
||||
|
||||
const CheckboxGroupContainer = ({
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
mode = "standard",
|
||||
mode: modeProp = "standard",
|
||||
disabled = false,
|
||||
options = [],
|
||||
className = "",
|
||||
...props
|
||||
}: CheckboxGroupProps) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const mode = normalizeMode(modeProp);
|
||||
// Generate unique ID for accessibility if not provided
|
||||
const generatedId = useId();
|
||||
const groupId = name || `checkbox-group-${generatedId}`;
|
||||
|
||||
@@ -5,11 +5,17 @@ export interface CheckboxOption {
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
import type { ModeValue } from "../../../lib/propNormalization";
|
||||
|
||||
export interface CheckboxGroupProps {
|
||||
name?: string;
|
||||
value?: string[];
|
||||
onChange?: (_data: { value: string[] }) => void;
|
||||
mode?: "standard" | "inverse";
|
||||
/**
|
||||
* Mode variant. Accepts both "standard"/"Standard" and "inverse"/"Inverse" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
mode?: ModeValue;
|
||||
disabled?: boolean;
|
||||
options?: CheckboxOption[];
|
||||
className?: string;
|
||||
|
||||
@@ -4,9 +4,12 @@ import { memo } from "react";
|
||||
import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
|
||||
import ContentContainerView from "./ContentContainer.view";
|
||||
import type { ContentContainerProps } from "./ContentContainer.types";
|
||||
import { normalizeContentContainerSize } from "../../../lib/propNormalization";
|
||||
|
||||
const ContentContainerContainer = memo<ContentContainerProps>(
|
||||
({ post, width = "200px", size = "responsive" }) => {
|
||||
({ post, width = "200px", size: sizeProp = "responsive" }) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const size = normalizeContentContainerSize(sizeProp);
|
||||
// Get the corresponding icon based on the same logic as background images
|
||||
const getIconImage = (slug: string): string => {
|
||||
const icons = [
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import type { BlogPost } from "../../../lib/content";
|
||||
|
||||
export type ContentContainerSizeValue = "xs" | "responsive" | "Xs" | "Responsive";
|
||||
|
||||
export interface ContentContainerProps {
|
||||
post: BlogPost;
|
||||
width?: string;
|
||||
size?: "xs" | "responsive";
|
||||
/**
|
||||
* Content container size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: ContentContainerSizeValue;
|
||||
}
|
||||
|
||||
export interface ContentContainerViewProps {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { memo } from "react";
|
||||
import ContentLockupView from "./ContentLockup.view";
|
||||
import type { ContentLockupProps, VariantStyle } from "./ContentLockup.types";
|
||||
import { normalizeContentLockupVariant, normalizeAlignment } from "../../../lib/propNormalization";
|
||||
|
||||
const ContentLockupContainer = memo<ContentLockupProps>(
|
||||
({
|
||||
@@ -11,12 +12,15 @@ const ContentLockupContainer = memo<ContentLockupProps>(
|
||||
description,
|
||||
ctaText,
|
||||
buttonClassName = "",
|
||||
variant = "hero",
|
||||
variant: variantProp = "hero",
|
||||
linkText,
|
||||
linkHref,
|
||||
alignment = "center",
|
||||
alignment: alignmentProp = "center",
|
||||
titleId,
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const variant = normalizeContentLockupVariant(variantProp);
|
||||
const alignment = normalizeAlignment(alignmentProp);
|
||||
// Variant-specific styling
|
||||
const variantStyles: Record<string, VariantStyle> = {
|
||||
hero: {
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
export type ContentLockupVariantValue =
|
||||
| "hero"
|
||||
| "feature"
|
||||
| "learn"
|
||||
| "ask"
|
||||
| "ask-inverse"
|
||||
| "modal"
|
||||
| "Hero"
|
||||
| "Feature"
|
||||
| "Learn"
|
||||
| "Ask"
|
||||
| "Ask-Inverse"
|
||||
| "Modal";
|
||||
|
||||
export type ContentLockupAlignmentValue = "center" | "left" | "Center" | "Left";
|
||||
|
||||
export interface ContentLockupProps {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
@@ -5,10 +21,18 @@ export interface ContentLockupProps {
|
||||
ctaText?: string;
|
||||
ctaHref?: string;
|
||||
buttonClassName?: string;
|
||||
variant?: "hero" | "feature" | "learn" | "ask" | "ask-inverse" | "modal";
|
||||
/**
|
||||
* Content lockup variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
variant?: ContentLockupVariantValue;
|
||||
linkText?: string;
|
||||
linkHref?: string;
|
||||
alignment?: "center" | "left";
|
||||
/**
|
||||
* Text alignment. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
alignment?: ContentLockupAlignmentValue;
|
||||
/**
|
||||
* Optional id to attach to the primary title heading.
|
||||
* Useful when a parent section uses aria-labelledby.
|
||||
|
||||
@@ -4,9 +4,12 @@ import { memo } from "react";
|
||||
import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
|
||||
import ContentThumbnailTemplateView from "./ContentThumbnailTemplate.view";
|
||||
import type { ContentThumbnailTemplateProps } from "./ContentThumbnailTemplate.types";
|
||||
import { normalizeContentThumbnailVariant } from "../../../lib/propNormalization";
|
||||
|
||||
const ContentThumbnailTemplateContainer = memo<ContentThumbnailTemplateProps>(
|
||||
({ post, className = "", variant = "vertical" }) => {
|
||||
({ post, className = "", variant: variantProp = "vertical" }) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const variant = normalizeContentThumbnailVariant(variantProp);
|
||||
// Get article-specific background image from frontmatter
|
||||
const getBackgroundImage = (
|
||||
post: ContentThumbnailTemplateProps["post"],
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import type { BlogPost } from "../../../lib/content";
|
||||
|
||||
export type ContentThumbnailTemplateVariantValue = "vertical" | "horizontal" | "Vertical" | "Horizontal";
|
||||
|
||||
export interface ContentThumbnailTemplateProps {
|
||||
post: BlogPost;
|
||||
className?: string;
|
||||
variant?: "vertical" | "horizontal";
|
||||
/**
|
||||
* Content thumbnail variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
variant?: ContentThumbnailTemplateVariantValue;
|
||||
slugOrder?: string[];
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { forwardRef, memo, useCallback } from "react";
|
||||
import { ContextMenuItemView } from "./ContextMenuItem.view";
|
||||
import type { ContextMenuItemProps } from "./ContextMenuItem.types";
|
||||
import { normalizeContextMenuItemSize } from "../../../lib/propNormalization";
|
||||
|
||||
const ContextMenuItemContainer = forwardRef<
|
||||
HTMLDivElement,
|
||||
@@ -16,11 +17,13 @@ const ContextMenuItemContainer = forwardRef<
|
||||
disabled = false,
|
||||
className = "",
|
||||
onClick,
|
||||
size = "medium",
|
||||
size: sizeProp = "medium",
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const size = normalizeContextMenuItemSize(sizeProp);
|
||||
const getTextSize = (): string => {
|
||||
switch (size) {
|
||||
case "small":
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export type ContextMenuItemSizeValue = "small" | "medium" | "large" | "Small" | "Medium" | "Large";
|
||||
|
||||
export interface ContextMenuItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
selected?: boolean;
|
||||
@@ -7,7 +9,11 @@ export interface ContextMenuItemProps extends React.HTMLAttributes<HTMLDivElemen
|
||||
onClick?: (
|
||||
_e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>,
|
||||
) => void;
|
||||
size?: "small" | "medium" | "large";
|
||||
/**
|
||||
* Context menu item size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: ContextMenuItemSizeValue;
|
||||
}
|
||||
|
||||
export interface ContextMenuItemViewProps {
|
||||
|
||||
@@ -1,12 +1,31 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import { normalizeImagePlaceholderColor } from "../../lib/propNormalization";
|
||||
|
||||
export type ImagePlaceholderColorValue =
|
||||
| "blue"
|
||||
| "green"
|
||||
| "purple"
|
||||
| "red"
|
||||
| "orange"
|
||||
| "teal"
|
||||
| "Blue"
|
||||
| "Green"
|
||||
| "Purple"
|
||||
| "Red"
|
||||
| "Orange"
|
||||
| "Teal";
|
||||
|
||||
interface ImagePlaceholderProps {
|
||||
width?: number;
|
||||
height?: number;
|
||||
text?: string;
|
||||
color?: "blue" | "green" | "purple" | "red" | "orange" | "teal";
|
||||
/**
|
||||
* Image placeholder color. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
color?: ImagePlaceholderColorValue;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -19,9 +38,11 @@ const ImagePlaceholder = memo<ImagePlaceholderProps>(
|
||||
width = 260,
|
||||
height = 390,
|
||||
text = "Blog Image",
|
||||
color = "blue",
|
||||
color: colorProp = "blue",
|
||||
className = "",
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const color = normalizeImagePlaceholderColor(colorProp);
|
||||
const colors: Record<string, string> = {
|
||||
blue: "bg-blue-500",
|
||||
green: "bg-green-500",
|
||||
|
||||
@@ -2,15 +2,32 @@
|
||||
|
||||
import { memo } from "react";
|
||||
import { useTranslation } from "../contexts/MessagesContext";
|
||||
import { normalizeMenuBarSize } from "../../lib/propNormalization";
|
||||
|
||||
export type MenuBarSizeValue =
|
||||
| "xsmall"
|
||||
| "default"
|
||||
| "medium"
|
||||
| "large"
|
||||
| "XSmall"
|
||||
| "Default"
|
||||
| "Medium"
|
||||
| "Large";
|
||||
|
||||
interface MenuBarProps extends React.HTMLAttributes<HTMLElement> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
size?: "xsmall" | "default" | "medium" | "large";
|
||||
/**
|
||||
* Menu bar size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: MenuBarSizeValue;
|
||||
}
|
||||
|
||||
const MenuBar = memo<MenuBarProps>(
|
||||
({ children, className = "", size = "default", ...props }) => {
|
||||
({ children, className = "", size: sizeProp = "default", ...props }) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const size = normalizeMenuBarSize(sizeProp);
|
||||
const t = useTranslation("menuBar");
|
||||
const sizeStyles: Record<string, string> = {
|
||||
xsmall:
|
||||
|
||||
@@ -3,19 +3,24 @@
|
||||
import { memo } from "react";
|
||||
import MenuBarItemView from "./MenuBarItem.view";
|
||||
import type { MenuBarItemProps } from "./MenuBarItem.types";
|
||||
import { normalizeMenuBarItemVariant } from "../../../lib/propNormalization";
|
||||
|
||||
const MenuBarItemContainer = memo<MenuBarItemProps>(
|
||||
({
|
||||
href = "#",
|
||||
children,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
variant: variantProp = "default",
|
||||
size: sizeProp = "default",
|
||||
className = "",
|
||||
disabled = false,
|
||||
isActive = false,
|
||||
ariaLabel,
|
||||
...props
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const variant = normalizeMenuBarItemVariant(variantProp);
|
||||
// Size has many values, normalize by lowercasing
|
||||
const size = (sizeProp?.toLowerCase() || "default") as typeof sizeProp;
|
||||
const variantStyles: Record<string, string> = {
|
||||
default:
|
||||
"bg-transparent text-[var(--color-content-default-brand-primary)] hover:bg-[var(--color-surface-default-tertiary)] hover:text-[var(--color-content-default-brand-primary)] hover:scale-[1.02] active:bg-transparent active:text-[var(--color-content-default-brand-primary)] active:scale-[0.98] disabled:bg-[var(--color-surface-default-tertiary)] disabled:text-[var(--color-content-default-tertiary)] disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 disabled:active:scale-100",
|
||||
|
||||
@@ -1,18 +1,40 @@
|
||||
export type MenuBarItemSizeValue =
|
||||
| "default"
|
||||
| "xsmall"
|
||||
| "xsmallUseCases"
|
||||
| "home"
|
||||
| "homeMd"
|
||||
| "homeUseCases"
|
||||
| "large"
|
||||
| "largeUseCases"
|
||||
| "homeXlarge"
|
||||
| "xlarge"
|
||||
| "Default"
|
||||
| "XSmall"
|
||||
| "XSmallUseCases"
|
||||
| "Home"
|
||||
| "HomeMd"
|
||||
| "HomeUseCases"
|
||||
| "Large"
|
||||
| "LargeUseCases"
|
||||
| "HomeXlarge"
|
||||
| "XLarge";
|
||||
|
||||
export type MenuBarItemVariantValue = "default" | "home" | "Default" | "Home";
|
||||
|
||||
export interface MenuBarItemProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
href?: string;
|
||||
children?: React.ReactNode;
|
||||
variant?: "default" | "home";
|
||||
size?:
|
||||
| "default"
|
||||
| "xsmall"
|
||||
| "xsmallUseCases"
|
||||
| "home"
|
||||
| "homeMd"
|
||||
| "homeUseCases"
|
||||
| "large"
|
||||
| "largeUseCases"
|
||||
| "homeXlarge"
|
||||
| "xlarge";
|
||||
/**
|
||||
* Menu bar item variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
variant?: MenuBarItemVariantValue;
|
||||
/**
|
||||
* Menu bar item size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: MenuBarItemSizeValue;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
isActive?: boolean;
|
||||
|
||||
@@ -3,18 +3,22 @@
|
||||
import { memo } from "react";
|
||||
import NavigationItemView from "./NavigationItem.view";
|
||||
import type { NavigationItemProps } from "./NavigationItem.types";
|
||||
import { normalizeNavigationItemVariant, normalizeNavigationItemSize } from "../../../lib/propNormalization";
|
||||
|
||||
const NavigationItemContainer = memo<NavigationItemProps>(
|
||||
({
|
||||
href = "#",
|
||||
children,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
variant: variantProp = "default",
|
||||
size: sizeProp = "default",
|
||||
className = "",
|
||||
disabled = false,
|
||||
isActive = false,
|
||||
...props
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const variant = normalizeNavigationItemVariant(variantProp);
|
||||
const size = normalizeNavigationItemSize(sizeProp);
|
||||
// Variant styles
|
||||
const variantStyles: Record<string, string> = {
|
||||
default:
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
export type NavigationItemVariantValue = "default" | "Default";
|
||||
export type NavigationItemSizeValue = "default" | "xsmall" | "Default" | "XSmall";
|
||||
|
||||
export interface NavigationItemProps extends Omit<
|
||||
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||
"isActive"
|
||||
> {
|
||||
href?: string;
|
||||
children?: React.ReactNode;
|
||||
variant?: "default";
|
||||
size?: "default" | "xsmall";
|
||||
/**
|
||||
* Navigation item variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
variant?: NavigationItemVariantValue;
|
||||
/**
|
||||
* Navigation item size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: NavigationItemSizeValue;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
isActive?: boolean;
|
||||
|
||||
@@ -3,15 +3,33 @@
|
||||
import { memo } from "react";
|
||||
import SectionNumber from "./SectionNumber";
|
||||
|
||||
import { normalizeNumberCardSize } from "../../lib/propNormalization";
|
||||
|
||||
export type NumberCardSizeValue =
|
||||
| "Small"
|
||||
| "Medium"
|
||||
| "Large"
|
||||
| "XLarge"
|
||||
| "small"
|
||||
| "medium"
|
||||
| "large"
|
||||
| "xlarge";
|
||||
|
||||
interface NumberCardProps {
|
||||
number: number;
|
||||
text: string;
|
||||
size?: "Small" | "Medium" | "Large" | "XLarge";
|
||||
/**
|
||||
* Number card size. Accepts both PascalCase (Figma default) and lowercase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses PascalCase - both are supported.
|
||||
*/
|
||||
size?: NumberCardSizeValue;
|
||||
iconShape?: string;
|
||||
iconColor?: string;
|
||||
}
|
||||
|
||||
const NumberCard = memo<NumberCardProps>(({ number, text, size }) => {
|
||||
const NumberCard = memo<NumberCardProps>(({ number, text, size: sizeProp }) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const size = normalizeNumberCardSize(sizeProp);
|
||||
// Base classes common to all sizes
|
||||
const baseClasses = "bg-[var(--color-surface-inverse-primary)] rounded-[12px] shadow-lg";
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@ import { memo, useState } from "react";
|
||||
import { logger } from "../../../lib/logger";
|
||||
import QuoteBlockView from "./QuoteBlock.view";
|
||||
import type { QuoteBlockProps, VariantConfig } from "./QuoteBlock.types";
|
||||
import { normalizeQuoteBlockVariant } from "../../../lib/propNormalization";
|
||||
|
||||
const QuoteBlockContainer = memo<QuoteBlockProps>(
|
||||
({
|
||||
variant = "standard",
|
||||
variant: variantProp = "standard",
|
||||
className = "",
|
||||
quote = "The rules of decision-making must be open and available to everyone, and this can happen only if they are formalized.",
|
||||
author = "Jo Freeman",
|
||||
@@ -17,6 +18,8 @@ const QuoteBlockContainer = memo<QuoteBlockProps>(
|
||||
fallbackAvatarSrc = "/assets/Quote_Avatar.svg",
|
||||
onError,
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const variant = normalizeQuoteBlockVariant(variantProp);
|
||||
const [imageError, setImageError] = useState(false);
|
||||
const [imageLoading, setImageLoading] = useState(true);
|
||||
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
export type QuoteBlockVariantValue =
|
||||
| "compact"
|
||||
| "standard"
|
||||
| "extended"
|
||||
| "Compact"
|
||||
| "Standard"
|
||||
| "Extended";
|
||||
|
||||
export interface QuoteBlockProps {
|
||||
variant?: "compact" | "standard" | "extended";
|
||||
/**
|
||||
* Quote block variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
variant?: QuoteBlockVariantValue;
|
||||
className?: string;
|
||||
quote?: string;
|
||||
author?: string;
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
import { memo, useCallback, useId } from "react";
|
||||
import { RadioButtonView } from "./RadioButton.view";
|
||||
import type { RadioButtonProps } from "./RadioButton.types";
|
||||
import { normalizeMode, normalizeState } from "../../../lib/propNormalization";
|
||||
|
||||
const RadioButtonContainer = ({
|
||||
checked = false,
|
||||
mode = "standard",
|
||||
state = "default", // This state prop is now only for static display in Storybook/Preview
|
||||
mode: modeProp = "standard",
|
||||
state: stateProp = "default", // This state prop is now only for static display in Storybook/Preview
|
||||
indicator = true, // From Figma: whether to show the indicator dot
|
||||
disabled = false,
|
||||
label,
|
||||
onChange,
|
||||
@@ -17,6 +19,13 @@ const RadioButtonContainer = ({
|
||||
ariaLabel,
|
||||
className = "",
|
||||
}: RadioButtonProps) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const mode = normalizeMode(modeProp);
|
||||
const state = normalizeState(stateProp);
|
||||
|
||||
// If state is "selected", it means checked in Figma terms
|
||||
const normalizedState = state === "selected" || checked ? "selected" : state;
|
||||
|
||||
const isInverse = mode === "inverse";
|
||||
const isStandard = mode === "standard";
|
||||
|
||||
@@ -113,7 +122,7 @@ const RadioButtonContainer = ({
|
||||
radioId={radioId}
|
||||
checked={checked}
|
||||
mode={mode}
|
||||
state={state} // Passed for static display in Storybook/Preview
|
||||
state={normalizedState} // Normalized state (handles "selected" from Figma)
|
||||
disabled={disabled}
|
||||
label={label}
|
||||
name={name}
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
import type { ModeValue, StateValue } from "../../../lib/propNormalization";
|
||||
|
||||
export interface RadioButtonProps {
|
||||
checked?: boolean;
|
||||
mode?: "standard" | "inverse";
|
||||
state?: "default" | "hover" | "focus";
|
||||
/**
|
||||
* Mode variant. Accepts both "standard"/"Standard" and "inverse"/"Inverse" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
mode?: ModeValue;
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "hover"/"Hover", "focus"/"Focus", "selected"/"Selected" (case-insensitive).
|
||||
* Note: "selected" state is represented by the `checked` prop in practice.
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: StateValue;
|
||||
/**
|
||||
* Whether to show the indicator dot. From Figma specification.
|
||||
*/
|
||||
indicator?: boolean;
|
||||
disabled?: boolean;
|
||||
label?: string;
|
||||
onChange?: (_data: { checked: boolean; value?: string }) => void;
|
||||
@@ -16,7 +31,7 @@ export interface RadioButtonViewProps {
|
||||
radioId: string;
|
||||
checked: boolean;
|
||||
mode: "standard" | "inverse";
|
||||
state: "default" | "hover" | "focus";
|
||||
state: "default" | "hover" | "focus" | "selected";
|
||||
disabled: boolean;
|
||||
label?: string;
|
||||
name?: string;
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
import { memo, useCallback, useId } from "react";
|
||||
import { RadioGroupView } from "./RadioGroup.view";
|
||||
import type { RadioGroupProps } from "./RadioGroup.types";
|
||||
import { normalizeMode, normalizeState } from "../../../lib/propNormalization";
|
||||
|
||||
const RadioGroupContainer = ({
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
mode = "standard",
|
||||
state = "default",
|
||||
mode: modeProp = "standard",
|
||||
state: stateProp = "default",
|
||||
disabled = false,
|
||||
options = [],
|
||||
className = "",
|
||||
...props
|
||||
}: RadioGroupProps) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const mode = normalizeMode(modeProp);
|
||||
// Normalize state, but handle "With Subtext" separately (it's represented by options with subtext)
|
||||
const state = typeof stateProp === "string" &&
|
||||
(stateProp.toLowerCase() === "with subtext" || stateProp === "With Subtext")
|
||||
? "default" // "With Subtext" is handled via RadioOption.subtext, use default state
|
||||
: normalizeState(stateProp);
|
||||
// Generate unique ID for accessibility if not provided
|
||||
const generatedId = useId();
|
||||
const groupId = name || `radio-group-${generatedId}`;
|
||||
|
||||
@@ -5,12 +5,23 @@ export interface RadioOption {
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
import type { ModeValue, StateValue } from "../../../lib/propNormalization";
|
||||
|
||||
export interface RadioGroupProps {
|
||||
name?: string;
|
||||
value?: string;
|
||||
onChange?: (_data: { value: string }) => void;
|
||||
mode?: "standard" | "inverse";
|
||||
state?: "default" | "hover" | "focus";
|
||||
/**
|
||||
* Mode variant. Accepts both "standard"/"Standard" and "inverse"/"Inverse" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
mode?: ModeValue;
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "hover"/"Hover", "focus"/"Focus" (case-insensitive).
|
||||
* Figma also supports "With Subtext" state, which is handled via RadioOption.subtext.
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: StateValue | "With Subtext" | "with subtext";
|
||||
disabled?: boolean;
|
||||
options?: RadioOption[];
|
||||
className?: string;
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import { normalizeSectionHeaderVariant } from "../../lib/propNormalization";
|
||||
|
||||
export type SectionHeaderVariantValue = "default" | "multi-line" | "Default" | "Multi-Line";
|
||||
|
||||
interface SectionHeaderProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
titleLg?: string;
|
||||
variant?: "default" | "multi-line";
|
||||
/**
|
||||
* Section header variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
variant?: SectionHeaderVariantValue;
|
||||
}
|
||||
|
||||
const SectionHeader = memo<SectionHeaderProps>(
|
||||
({ title, subtitle, titleLg, variant = "default" }) => {
|
||||
({ title, subtitle, titleLg, variant: variantProp = "default" }) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const variant = normalizeSectionHeaderVariant(variantProp);
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
|
||||
@@ -16,13 +16,16 @@ import React, {
|
||||
import { useClickOutside } from "../../hooks";
|
||||
import { SelectInputView } from "./SelectInput.view";
|
||||
import type { SelectInputProps } from "./SelectInput.types";
|
||||
import { normalizeState, normalizeSmallMediumLargeSize, normalizeLabelVariant } from "../../../lib/propNormalization";
|
||||
|
||||
const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
|
||||
(
|
||||
{
|
||||
id,
|
||||
label,
|
||||
state: externalState = "default",
|
||||
labelVariant: labelVariantProp,
|
||||
size: sizeProp,
|
||||
state: externalStateProp = "default",
|
||||
disabled = false,
|
||||
error = false,
|
||||
placeholder = "Choose an option",
|
||||
@@ -35,6 +38,11 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const labelVariant = labelVariantProp ? normalizeLabelVariant(labelVariantProp) : undefined;
|
||||
const size = sizeProp ? normalizeSmallMediumLargeSize(sizeProp) : undefined;
|
||||
const externalState = normalizeState(externalStateProp);
|
||||
|
||||
const generatedId = useId();
|
||||
const selectId = id || `select-input-${generatedId}`;
|
||||
const labelId = `${selectId}-label`;
|
||||
|
||||
@@ -5,12 +5,29 @@ export interface SelectOptionData {
|
||||
label: string;
|
||||
}
|
||||
|
||||
import type { StateValue } from "../../../lib/propNormalization";
|
||||
|
||||
export type SelectInputLabelVariantValue = "default" | "horizontal" | "Default" | "Horizontal";
|
||||
export type SelectInputSizeValue = "small" | "medium" | "large" | "Small" | "Medium" | "Large";
|
||||
|
||||
export interface SelectInputProps {
|
||||
id?: string;
|
||||
label?: string;
|
||||
labelVariant?: "default" | "horizontal";
|
||||
size?: "small" | "medium" | "large";
|
||||
state?: "default" | "hover" | "focus";
|
||||
/**
|
||||
* Label variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
labelVariant?: SelectInputLabelVariantValue;
|
||||
/**
|
||||
* Select input size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: SelectInputSizeValue;
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "hover"/"Hover", "focus"/"Focus" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: StateValue;
|
||||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
placeholder?: string;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { forwardRef, memo, useCallback } from "react";
|
||||
import { SelectOptionView } from "./SelectOption.view";
|
||||
import type { SelectOptionProps } from "./SelectOption.types";
|
||||
import { normalizeContextMenuItemSize } from "../../../lib/propNormalization";
|
||||
|
||||
const SelectOptionContainer = forwardRef<HTMLDivElement, SelectOptionProps>(
|
||||
(
|
||||
@@ -12,11 +13,13 @@ const SelectOptionContainer = forwardRef<HTMLDivElement, SelectOptionProps>(
|
||||
disabled = false,
|
||||
className = "",
|
||||
onClick,
|
||||
size = "medium",
|
||||
size: sizeProp = "medium",
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const size = normalizeContextMenuItemSize(sizeProp);
|
||||
const getTextSize = (): string => {
|
||||
switch (size) {
|
||||
case "small":
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export type SelectOptionSizeValue = "small" | "medium" | "large" | "Small" | "Medium" | "Large";
|
||||
|
||||
export interface SelectOptionProps {
|
||||
children?: React.ReactNode;
|
||||
selected?: boolean;
|
||||
@@ -6,7 +8,11 @@ export interface SelectOptionProps {
|
||||
onClick?: (
|
||||
_e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>,
|
||||
) => void;
|
||||
size?: "small" | "medium" | "large";
|
||||
/**
|
||||
* Select option size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: SelectOptionSizeValue;
|
||||
}
|
||||
|
||||
export interface SelectOptionViewProps {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { memo, useCallback, useId, forwardRef } from "react";
|
||||
import { SwitchView } from "./Switch.view";
|
||||
import type { SwitchProps } from "./Switch.types";
|
||||
import { normalizeState } from "../../../lib/propNormalization";
|
||||
|
||||
const SwitchContainer = memo(
|
||||
forwardRef<HTMLButtonElement, SwitchProps>((props, ref) => {
|
||||
@@ -11,11 +12,14 @@ const SwitchContainer = memo(
|
||||
onChange,
|
||||
onFocus,
|
||||
onBlur,
|
||||
state = "default",
|
||||
state: stateProp = "default",
|
||||
label,
|
||||
className = "",
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const state = normalizeState(stateProp);
|
||||
|
||||
const switchId = useId();
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { StateValue } from "../../../lib/propNormalization";
|
||||
|
||||
export interface SwitchProps extends Omit<
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
"onChange"
|
||||
@@ -10,7 +12,11 @@ export interface SwitchProps extends Omit<
|
||||
) => void;
|
||||
onFocus?: (_e: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
onBlur?: (_e: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
state?: "default" | "hover" | "focus";
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "hover"/"Hover", "focus"/"Focus" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: StateValue;
|
||||
label?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@ import { memo, forwardRef } from "react";
|
||||
import { useComponentId, useFormField } from "../../hooks";
|
||||
import { TextAreaView } from "./TextArea.view";
|
||||
import type { TextAreaProps } from "./TextArea.types";
|
||||
import { normalizeInputState, normalizeSmallMediumLargeSize, normalizeLabelVariant } from "../../../lib/propNormalization";
|
||||
|
||||
const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
(
|
||||
{
|
||||
size = "medium",
|
||||
labelVariant = "default",
|
||||
state = "default",
|
||||
size: sizeProp = "medium",
|
||||
labelVariant: labelVariantProp = "default",
|
||||
state: stateProp = "default",
|
||||
disabled = false,
|
||||
error = false,
|
||||
label,
|
||||
@@ -27,6 +28,10 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const size = normalizeSmallMediumLargeSize(sizeProp);
|
||||
const labelVariant = normalizeLabelVariant(labelVariantProp);
|
||||
const state = normalizeInputState(stateProp);
|
||||
// Generate unique ID for accessibility if not provided
|
||||
const { id: textareaId, labelId } = useComponentId("textarea", id);
|
||||
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
import type { InputStateValue } from "../../../lib/propNormalization";
|
||||
|
||||
export type TextAreaSizeValue = "small" | "medium" | "large" | "Small" | "Medium" | "Large";
|
||||
export type TextAreaLabelVariantValue = "default" | "horizontal" | "Default" | "Horizontal";
|
||||
|
||||
export interface TextAreaProps extends Omit<
|
||||
React.TextareaHTMLAttributes<HTMLTextAreaElement>,
|
||||
"size" | "onChange" | "onFocus" | "onBlur"
|
||||
> {
|
||||
size?: "small" | "medium" | "large";
|
||||
labelVariant?: "default" | "horizontal";
|
||||
state?: "default" | "active" | "hover" | "focus";
|
||||
/**
|
||||
* Text area size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
size?: TextAreaSizeValue;
|
||||
/**
|
||||
* Label variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
labelVariant?: TextAreaLabelVariantValue;
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "active"/"Active", "hover"/"Hover", "focus"/"Focus" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: InputStateValue;
|
||||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
label?: string;
|
||||
|
||||
@@ -4,11 +4,12 @@ import { memo, forwardRef, useState, useRef } from "react";
|
||||
import { useComponentId, useFormField } from "../../hooks";
|
||||
import { TextInputView } from "./TextInput.view";
|
||||
import type { TextInputProps } from "./TextInput.types";
|
||||
import { normalizeInputState } from "../../../lib/propNormalization";
|
||||
|
||||
const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
(
|
||||
{
|
||||
state: externalState = "default",
|
||||
state: externalStateProp = "default",
|
||||
disabled = false,
|
||||
error = false,
|
||||
label,
|
||||
@@ -26,6 +27,9 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const externalState = normalizeInputState(externalStateProp);
|
||||
|
||||
// Generate unique ID for accessibility if not provided
|
||||
const { id: inputId, labelId } = useComponentId("text-input", id);
|
||||
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import type { InputStateValue } from "../../../lib/propNormalization";
|
||||
|
||||
export interface TextInputProps extends Omit<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
"size" | "onChange" | "onFocus" | "onBlur"
|
||||
> {
|
||||
state?: "default" | "active" | "hover" | "focus";
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "active"/"Active", "hover"/"Hover", "focus"/"Focus" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: InputStateValue;
|
||||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
label?: string;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { memo, useCallback, useId, forwardRef } from "react";
|
||||
import { ToggleView } from "./Toggle.view";
|
||||
import type { ToggleProps } from "./Toggle.types";
|
||||
import { normalizeState } from "../../../lib/propNormalization";
|
||||
|
||||
const ToggleContainer = forwardRef<HTMLButtonElement, ToggleProps>(
|
||||
(
|
||||
@@ -13,7 +14,7 @@ const ToggleContainer = forwardRef<HTMLButtonElement, ToggleProps>(
|
||||
onFocus,
|
||||
onBlur,
|
||||
disabled = false,
|
||||
state = "default",
|
||||
state: stateProp = "default",
|
||||
showIcon = false,
|
||||
showText = false,
|
||||
icon = "I",
|
||||
@@ -23,6 +24,8 @@ const ToggleContainer = forwardRef<HTMLButtonElement, ToggleProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const state = normalizeState(stateProp);
|
||||
const toggleId = useId();
|
||||
const labelId = useId();
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { StateValue } from "../../../lib/propNormalization";
|
||||
|
||||
export interface ToggleProps extends Omit<
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
"onChange"
|
||||
@@ -12,7 +14,11 @@ export interface ToggleProps extends Omit<
|
||||
onFocus?: (_e: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
onBlur?: (_e: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
disabled?: boolean;
|
||||
state?: "default" | "hover" | "focus";
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "hover"/"Hover", "focus"/"Focus" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: StateValue;
|
||||
showIcon?: boolean;
|
||||
showText?: boolean;
|
||||
icon?: string;
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
import { memo, useCallback, useId, forwardRef } from "react";
|
||||
import { ToggleGroupView } from "./ToggleGroup.view";
|
||||
import type { ToggleGroupProps } from "./ToggleGroup.types";
|
||||
import { normalizeToggleState, normalizeToggleGroupPosition } from "../../../lib/propNormalization";
|
||||
|
||||
const ToggleGroupContainer = memo(
|
||||
forwardRef<HTMLButtonElement, ToggleGroupProps>((props, _ref) => {
|
||||
const {
|
||||
children,
|
||||
className = "",
|
||||
position = "left",
|
||||
state = "default",
|
||||
position: positionProp = "left",
|
||||
state: stateProp = "default",
|
||||
showText = true,
|
||||
ariaLabel,
|
||||
onChange,
|
||||
@@ -18,6 +19,10 @@ const ToggleGroupContainer = memo(
|
||||
onBlur,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const position = normalizeToggleGroupPosition(positionProp);
|
||||
const state = normalizeToggleState(stateProp);
|
||||
|
||||
const groupId = useId();
|
||||
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
import type { StateValue } from "../../../lib/propNormalization";
|
||||
|
||||
export type ToggleGroupPositionValue = "left" | "middle" | "right" | "Left" | "Middle" | "Right";
|
||||
|
||||
export interface ToggleGroupProps extends Omit<
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
"onChange"
|
||||
> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
position?: "left" | "middle" | "right";
|
||||
state?: "default" | "hover" | "focus" | "selected";
|
||||
/**
|
||||
* Toggle group position. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
position?: ToggleGroupPositionValue;
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "hover"/"Hover", "focus"/"Focus", "selected"/"Selected" (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: StateValue | "selected" | "Selected";
|
||||
showText?: boolean;
|
||||
ariaLabel?: string;
|
||||
onChange?: (
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
import { memo, useState } from "react";
|
||||
import { TooltipView } from "./Tooltip.view";
|
||||
import type { TooltipProps } from "./Tooltip.types";
|
||||
import { normalizeTooltipPosition } from "../../../lib/propNormalization";
|
||||
|
||||
const TooltipContainer = memo<TooltipProps>(
|
||||
({ children, text, position = "top", className = "", disabled = false }) => {
|
||||
({ children, text, position: positionProp = "top", className = "", disabled = false }) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const position = normalizeTooltipPosition(positionProp);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
if (disabled) {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
export type TooltipPositionValue = "top" | "bottom" | "Top" | "Bottom";
|
||||
|
||||
export interface TooltipProps {
|
||||
children: React.ReactNode;
|
||||
text: string;
|
||||
position?: "top" | "bottom";
|
||||
/**
|
||||
* Tooltip position. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
position?: TooltipPositionValue;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user