Update props in components
This commit is contained in:
@@ -1,19 +1,64 @@
|
||||
import { memo } from "react";
|
||||
import type { VariantValue, SizeValue } from "../../../lib/propNormalization";
|
||||
import { normalizeVariant, normalizeSize } from "../../../lib/propNormalization";
|
||||
import type {
|
||||
VariantValue,
|
||||
SizeValue,
|
||||
ButtonTypeValue,
|
||||
ButtonPaletteValue,
|
||||
ButtonStateValue,
|
||||
} from "../../../lib/propNormalization";
|
||||
import {
|
||||
normalizeVariant,
|
||||
normalizeSize,
|
||||
normalizeButtonType,
|
||||
normalizeButtonPalette,
|
||||
} from "../../../lib/propNormalization";
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* @deprecated Use `type` and `palette` props instead. This prop is maintained for backward compatibility.
|
||||
* Button variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
variant?: VariantValue;
|
||||
/**
|
||||
* Button type (Figma prop). Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
* @default "filled"
|
||||
*/
|
||||
buttonType?: ButtonTypeValue;
|
||||
/**
|
||||
* Button palette (Figma prop). Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses "Invert", codebase uses "inverse" - both are supported.
|
||||
* @default "default"
|
||||
*/
|
||||
palette?: ButtonPaletteValue;
|
||||
/**
|
||||
* Button size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
* @default "xsmall"
|
||||
*/
|
||||
size?: SizeValue;
|
||||
/**
|
||||
* Button state (Figma prop). Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* @default "default"
|
||||
*/
|
||||
state?: ButtonStateValue;
|
||||
/**
|
||||
* Whether to show a leading icon (Figma prop).
|
||||
* @default false
|
||||
*/
|
||||
hasIconLeading?: boolean;
|
||||
/**
|
||||
* Whether to show a following icon (Figma prop).
|
||||
* @default false
|
||||
*/
|
||||
hasIconFollowing?: boolean;
|
||||
/**
|
||||
* Whether to show text (Figma prop).
|
||||
* @default true
|
||||
*/
|
||||
hasText?: boolean;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
type?: "button" | "submit" | "reset";
|
||||
@@ -29,11 +74,17 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
const Button = memo<ButtonProps>(
|
||||
({
|
||||
children,
|
||||
variant: variantProp = "filled",
|
||||
variant: variantProp,
|
||||
buttonType: typeProp,
|
||||
palette: paletteProp,
|
||||
size: sizeProp = "xsmall",
|
||||
state: _stateProp,
|
||||
hasIconLeading = false,
|
||||
hasIconFollowing = false,
|
||||
hasText = true,
|
||||
className = "",
|
||||
disabled = false,
|
||||
type = "button",
|
||||
type: htmlType = "button",
|
||||
onClick,
|
||||
href,
|
||||
target,
|
||||
@@ -41,9 +92,68 @@ const Button = memo<ButtonProps>(
|
||||
ariaLabel,
|
||||
...props
|
||||
}) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
const variant = normalizeVariant(variantProp);
|
||||
// Determine type and palette from either new props or legacy variant prop
|
||||
let buttonType: "filled" | "outline" | "ghost" | "danger";
|
||||
let buttonPalette: "default" | "inverse";
|
||||
|
||||
if (variantProp) {
|
||||
// Backward compatibility: map old variant to new type/palette
|
||||
const variant = normalizeVariant(variantProp);
|
||||
if (variant === "filled") {
|
||||
buttonType = "filled";
|
||||
buttonPalette = "default";
|
||||
} else if (variant === "filled-inverse") {
|
||||
buttonType = "filled";
|
||||
buttonPalette = "inverse";
|
||||
} else if (variant === "outline") {
|
||||
buttonType = "outline";
|
||||
buttonPalette = "default";
|
||||
} else if (variant === "outline-inverse") {
|
||||
buttonType = "outline";
|
||||
buttonPalette = "inverse";
|
||||
} else if (variant === "ghost") {
|
||||
buttonType = "ghost";
|
||||
buttonPalette = "default";
|
||||
} else if (variant === "ghost-inverse") {
|
||||
buttonType = "ghost";
|
||||
buttonPalette = "inverse";
|
||||
} else if (variant === "danger") {
|
||||
buttonType = "danger";
|
||||
buttonPalette = "default";
|
||||
} else {
|
||||
// danger-inverse
|
||||
buttonType = "danger";
|
||||
buttonPalette = "inverse";
|
||||
}
|
||||
} else {
|
||||
// Use new type/palette props
|
||||
buttonType = normalizeButtonType(typeProp, "filled");
|
||||
buttonPalette = normalizeButtonPalette(paletteProp, "default");
|
||||
}
|
||||
|
||||
// Normalize other props
|
||||
const size = normalizeSize(sizeProp);
|
||||
// State prop is for Figma alignment - actual state is handled by CSS pseudo-classes
|
||||
// We accept it for API alignment but don't use it for styling (CSS handles states)
|
||||
|
||||
// Map type + palette to legacy variant for styling (maintains existing styles)
|
||||
const getVariantFromTypeAndPalette = (
|
||||
type: typeof buttonType,
|
||||
palette: typeof buttonPalette,
|
||||
): string => {
|
||||
if (type === "filled" && palette === "default") return "filled";
|
||||
if (type === "filled" && palette === "inverse") return "filled-inverse";
|
||||
if (type === "outline" && palette === "default") return "outline";
|
||||
if (type === "outline" && palette === "inverse") return "outline-inverse";
|
||||
if (type === "ghost" && palette === "default") return "ghost";
|
||||
if (type === "ghost" && palette === "inverse") return "ghost-inverse";
|
||||
if (type === "danger" && palette === "default") return "danger";
|
||||
// danger + inverse
|
||||
return "danger-inverse";
|
||||
};
|
||||
|
||||
const variant = getVariantFromTypeAndPalette(buttonType, buttonPalette);
|
||||
|
||||
const sizeStyles: Record<string, string> = {
|
||||
xsmall:
|
||||
"p-[var(--spacing-scale-004)] gap-[var(--spacing-scale-002)]",
|
||||
@@ -102,6 +212,10 @@ const Button = memo<ButtonProps>(
|
||||
? ""
|
||||
: hoverOutlineStyles[size];
|
||||
|
||||
// Apply state-based styles if state prop is provided (overrides default hover/focus/active)
|
||||
// Note: State prop is informational for Figma alignment - actual state is handled by CSS pseudo-classes
|
||||
// For now, we maintain existing behavior and state prop is for documentation/alignment purposes
|
||||
|
||||
const baseStyles = `inline-flex items-center justify-start box-border whitespace-nowrap shrink-0 ${sizeStyles[size]} rounded-[var(--radius-measures-radius-full)] ${fontStyles[size]} transition-all duration-500 ease-in-out cursor-pointer ${variantStyles[variant]} ${outlineStyles}`;
|
||||
const combinedStyles = `${baseStyles} ${className}`;
|
||||
|
||||
@@ -111,6 +225,16 @@ const Button = memo<ButtonProps>(
|
||||
tabIndex: disabled ? -1 : 0,
|
||||
};
|
||||
|
||||
// Filter children based on hasIconLeading, hasIconFollowing, hasText props
|
||||
// For now, we render all children but these props are available for future icon support
|
||||
const renderContent = () => {
|
||||
if (!hasText && !hasIconLeading && !hasIconFollowing) {
|
||||
return children; // If all are false, render children as-is (backward compatibility)
|
||||
}
|
||||
// TODO: When icon support is added, filter children based on these props
|
||||
return children;
|
||||
};
|
||||
|
||||
if (href && !disabled) {
|
||||
const anchorProps: React.AnchorHTMLAttributes<HTMLAnchorElement> = {
|
||||
href,
|
||||
@@ -121,11 +245,11 @@ const Button = memo<ButtonProps>(
|
||||
...(rel && { rel }),
|
||||
};
|
||||
|
||||
return <a {...anchorProps}>{children}</a>;
|
||||
return <a {...anchorProps}>{renderContent()}</a>;
|
||||
}
|
||||
|
||||
const buttonProps: React.ButtonHTMLAttributes<HTMLButtonElement> = {
|
||||
type,
|
||||
type: htmlType,
|
||||
className: combinedStyles,
|
||||
disabled,
|
||||
onClick,
|
||||
@@ -133,7 +257,7 @@ const Button = memo<ButtonProps>(
|
||||
...props,
|
||||
};
|
||||
|
||||
return <button {...buttonProps}>{children}</button>;
|
||||
return <button {...buttonProps}>{renderContent()}</button>;
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -22,10 +22,18 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
|
||||
(
|
||||
{
|
||||
id,
|
||||
label,
|
||||
label: labelProp,
|
||||
labelText,
|
||||
showLabel,
|
||||
labelVariant: labelVariantProp,
|
||||
size: sizeProp,
|
||||
state: externalStateProp = "default",
|
||||
asterisk = false,
|
||||
iconHelp = true,
|
||||
textOptional = false,
|
||||
textData = true,
|
||||
iconRight = true,
|
||||
textHint = false,
|
||||
disabled = false,
|
||||
error = false,
|
||||
placeholder = "Choose an option",
|
||||
@@ -38,6 +46,17 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
// Handle backward compatibility: if label is string, use it as labelText
|
||||
const actualLabelText = labelText || labelProp;
|
||||
const shouldShowLabel = showLabel !== undefined ? showLabel : (actualLabelText !== undefined);
|
||||
|
||||
// Normalize state - handle "state5" as disabled
|
||||
let normalizedState = externalStateProp;
|
||||
if (normalizedState === "state5" || normalizedState === "State5") {
|
||||
normalizedState = "default"; // Map to default, disabled prop handles the disabled state
|
||||
}
|
||||
const externalState = normalizeState(normalizedState);
|
||||
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
// Note: labelVariant and size are normalized for future use but not yet implemented in the view
|
||||
const _labelVariant = labelVariantProp ? normalizeLabelVariant(labelVariantProp) : undefined;
|
||||
@@ -45,7 +64,6 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
|
||||
// Mark as intentionally unused for future implementation
|
||||
void _labelVariant;
|
||||
void _size;
|
||||
const externalState = normalizeState(externalStateProp);
|
||||
|
||||
const generatedId = useId();
|
||||
const selectId = id || `select-input-${generatedId}`;
|
||||
@@ -193,7 +211,7 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
|
||||
|
||||
return (
|
||||
<SelectInputView
|
||||
label={label}
|
||||
label={shouldShowLabel ? actualLabelText : undefined}
|
||||
placeholder={placeholder}
|
||||
state={actualState}
|
||||
disabled={disabled}
|
||||
@@ -214,8 +232,14 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
|
||||
onOptionClick={handleOptionSelect}
|
||||
selectRef={selectRef}
|
||||
menuRef={menuRef}
|
||||
ariaLabelledby={label ? labelId : undefined}
|
||||
ariaLabelledby={shouldShowLabel ? labelId : undefined}
|
||||
ariaInvalid={error}
|
||||
asterisk={asterisk}
|
||||
iconHelp={iconHelp}
|
||||
textOptional={textOptional}
|
||||
textData={textData}
|
||||
iconRight={iconRight}
|
||||
textHint={textHint}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,21 @@ export type SelectInputSizeValue = "small" | "medium" | "large" | "Small" | "Med
|
||||
|
||||
export interface SelectInputProps {
|
||||
id?: string;
|
||||
/**
|
||||
* Label text (backward compatibility - if provided, label is shown).
|
||||
* For Figma alignment, use `labelText` prop instead.
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* Label text (Figma prop - use this for new code).
|
||||
*/
|
||||
labelText?: string;
|
||||
/**
|
||||
* Whether to show label above input (Figma prop).
|
||||
* If `label` or `labelText` is provided, defaults to true.
|
||||
* @default true
|
||||
*/
|
||||
showLabel?: boolean;
|
||||
/**
|
||||
* Label variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
@@ -24,10 +38,40 @@ export interface SelectInputProps {
|
||||
*/
|
||||
size?: SelectInputSizeValue;
|
||||
/**
|
||||
* Visual state. Accepts "default"/"Default", "hover"/"Hover", "focus"/"Focus" (case-insensitive).
|
||||
* Visual state. Accepts "default"/"Default", "active"/"Active", "focus"/"Focus", "error"/"Error", "state5"/"State5" (State5 = Disabled).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
state?: StateValue;
|
||||
state?: StateValue | "state5" | "State5";
|
||||
/**
|
||||
* Whether to show asterisk (*) in label (Figma prop).
|
||||
* @default false
|
||||
*/
|
||||
asterisk?: boolean;
|
||||
/**
|
||||
* Whether to show help icon in label (Figma prop).
|
||||
* @default true
|
||||
*/
|
||||
iconHelp?: boolean;
|
||||
/**
|
||||
* Whether to show "Optional" text in label (Figma prop).
|
||||
* @default false
|
||||
*/
|
||||
textOptional?: boolean;
|
||||
/**
|
||||
* Whether to show data text (placeholder/entered text) - internal, always true (Figma prop).
|
||||
* @default true
|
||||
*/
|
||||
textData?: boolean;
|
||||
/**
|
||||
* Whether to show dropdown icon on the right (Figma prop).
|
||||
* @default true
|
||||
*/
|
||||
iconRight?: boolean;
|
||||
/**
|
||||
* Whether to show hint text below input (Figma prop).
|
||||
* @default false
|
||||
*/
|
||||
textHint?: boolean;
|
||||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
placeholder?: string;
|
||||
|
||||
@@ -33,6 +33,13 @@ export interface SelectInputViewProps {
|
||||
// Additional props
|
||||
ariaLabelledby?: string;
|
||||
ariaInvalid?: boolean;
|
||||
// Figma props
|
||||
asterisk?: boolean;
|
||||
iconHelp?: boolean;
|
||||
textOptional?: boolean;
|
||||
textData?: boolean;
|
||||
iconRight?: boolean;
|
||||
textHint?: boolean;
|
||||
}
|
||||
|
||||
export function SelectInputView({
|
||||
@@ -59,6 +66,12 @@ export function SelectInputView({
|
||||
menuRef,
|
||||
ariaLabelledby,
|
||||
ariaInvalid,
|
||||
asterisk = false,
|
||||
iconHelp = true,
|
||||
textOptional = false,
|
||||
textData = true,
|
||||
iconRight = true,
|
||||
textHint = false,
|
||||
}: SelectInputViewProps) {
|
||||
// Styles based on Figma design
|
||||
const containerClasses = "flex flex-col gap-[8px]";
|
||||
@@ -135,14 +148,26 @@ export function SelectInputView({
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
<div className="relative shrink-0 size-[12px]">
|
||||
<img
|
||||
src={getAssetPath(ASSETS.ICON_HELP)}
|
||||
alt="Help"
|
||||
className="block max-w-none size-full"
|
||||
/>
|
||||
</div>
|
||||
{asterisk && (
|
||||
<span className="text-[var(--color-content-default-negative-primary,#ea4845)] text-[10px] leading-[12px] font-medium">
|
||||
*
|
||||
</span>
|
||||
)}
|
||||
{iconHelp && (
|
||||
<div className="relative shrink-0 size-[12px]">
|
||||
<img
|
||||
src={getAssetPath(ASSETS.ICON_HELP)}
|
||||
alt="Help"
|
||||
className="block max-w-none size-full"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{textOptional && (
|
||||
<span className="text-[var(--color-content-default-tertiary,#b4b4b4)] text-[10px] leading-[14px] font-normal">
|
||||
Optional text
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
@@ -161,24 +186,26 @@ export function SelectInputView({
|
||||
onFocus={onButtonFocus}
|
||||
onBlur={onButtonBlur}
|
||||
>
|
||||
<span className={`flex-1 text-left pr-[32px] ${textColorClass}`}>
|
||||
{displayText}
|
||||
<span className={`flex-1 text-left ${iconRight ? "pr-[32px]" : ""} ${textColorClass}`}>
|
||||
{textData ? displayText : placeholder}
|
||||
</span>
|
||||
<div className="flex items-center justify-center shrink-0">
|
||||
<svg
|
||||
className={chevronClasses}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{iconRight && (
|
||||
<div className="flex items-center justify-center shrink-0">
|
||||
<svg
|
||||
className={chevronClasses}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
{state === "focus" && (
|
||||
<div
|
||||
@@ -235,6 +262,13 @@ export function SelectInputView({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{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
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
type = "text",
|
||||
className = "",
|
||||
showHelpIcon = true,
|
||||
textHint = false,
|
||||
formHeader = true,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
@@ -220,6 +222,8 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
isFilled={isFilled}
|
||||
inputWrapperClasses={stateStyles.inputWrapper}
|
||||
focusRingClasses={stateStyles.focusRing}
|
||||
textHint={textHint}
|
||||
formHeader={formHeader}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -19,6 +19,16 @@ export interface TextInputProps extends Omit<
|
||||
onBlur?: (_e: React.FocusEvent<HTMLInputElement>) => void;
|
||||
className?: string;
|
||||
showHelpIcon?: boolean;
|
||||
/**
|
||||
* Whether to show hint text below input (Figma prop).
|
||||
* @default false
|
||||
*/
|
||||
textHint?: boolean;
|
||||
/**
|
||||
* Whether to show form header (label and help icon) above input (Figma prop).
|
||||
* @default true
|
||||
*/
|
||||
formHeader?: boolean;
|
||||
}
|
||||
|
||||
export interface TextInputViewProps {
|
||||
@@ -45,4 +55,6 @@ export interface TextInputViewProps {
|
||||
isFilled?: boolean;
|
||||
inputWrapperClasses?: string;
|
||||
focusRingClasses?: string;
|
||||
textHint?: boolean;
|
||||
formHeader?: boolean;
|
||||
}
|
||||
|
||||
@@ -26,12 +26,14 @@ export const TextInputView = forwardRef<HTMLInputElement, TextInputViewProps>(
|
||||
showHelpIcon = true,
|
||||
inputWrapperClasses = "relative",
|
||||
focusRingClasses = "",
|
||||
textHint = false,
|
||||
formHeader = true,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
return (
|
||||
<div className={containerClasses}>
|
||||
{label && (
|
||||
{formHeader && label && (
|
||||
<div className="flex flex-wrap gap-[var(--measures-spacing-200,4px_8px)] items-baseline pr-[var(--measures-spacing-100,4px)] relative shrink-0 w-full">
|
||||
<div className="flex gap-[var(--measures-spacing-050,2px)] items-center relative shrink-0">
|
||||
<label
|
||||
@@ -75,6 +77,13 @@ export const TextInputView = forwardRef<HTMLInputElement, TextInputViewProps>(
|
||||
<div className={focusRingClasses} aria-hidden="true" />
|
||||
)}
|
||||
</div>
|
||||
{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
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -11,6 +11,8 @@ const AlertContainer = memo<AlertProps>(
|
||||
description,
|
||||
status: statusProp = "default",
|
||||
type: typeProp = "toast",
|
||||
hasLeadingIcon = true,
|
||||
hasBodyText = true,
|
||||
onClose,
|
||||
className = "",
|
||||
}) => {
|
||||
@@ -104,6 +106,8 @@ const AlertContainer = memo<AlertProps>(
|
||||
description={description}
|
||||
status={status}
|
||||
type={type}
|
||||
hasLeadingIcon={hasLeadingIcon}
|
||||
hasBodyText={hasBodyText}
|
||||
className={className}
|
||||
containerClasses={containerClasses}
|
||||
containerStyle={containerStyle}
|
||||
|
||||
@@ -23,6 +23,16 @@ export interface AlertProps {
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
*/
|
||||
type?: AlertTypeValue;
|
||||
/**
|
||||
* Whether to show the leading icon (Figma prop).
|
||||
* @default true
|
||||
*/
|
||||
hasLeadingIcon?: boolean;
|
||||
/**
|
||||
* Whether to show body text/description (Figma prop).
|
||||
* @default true
|
||||
*/
|
||||
hasBodyText?: boolean;
|
||||
onClose?: () => void;
|
||||
className?: string;
|
||||
}
|
||||
@@ -32,6 +42,8 @@ export interface AlertViewProps {
|
||||
description?: string;
|
||||
status: "default" | "positive" | "warning" | "danger";
|
||||
type: "toast" | "banner";
|
||||
hasLeadingIcon: boolean;
|
||||
hasBodyText: boolean;
|
||||
className: string;
|
||||
containerClasses: string;
|
||||
containerStyle?: React.CSSProperties;
|
||||
|
||||
@@ -6,6 +6,8 @@ export function AlertView({
|
||||
description,
|
||||
status: _status,
|
||||
type: _type,
|
||||
hasLeadingIcon,
|
||||
hasBodyText,
|
||||
className,
|
||||
containerClasses,
|
||||
containerStyle,
|
||||
@@ -41,12 +43,16 @@ export function AlertView({
|
||||
style={containerStyle}
|
||||
role="alert"
|
||||
>
|
||||
<div className="shrink-0 w-[24px] h-[24px] flex items-center justify-center">
|
||||
{getIcon()}
|
||||
</div>
|
||||
{hasLeadingIcon && (
|
||||
<div className="shrink-0 w-[24px] h-[24px] flex items-center justify-center">
|
||||
{getIcon()}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-1 flex-col items-start justify-center min-h-0 min-w-0">
|
||||
<p className={titleClasses}>{title}</p>
|
||||
{description && <p className={descriptionClasses}>{description}</p>}
|
||||
{hasBodyText && description && (
|
||||
<p className={descriptionClasses}>{description}</p>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -692,3 +692,91 @@ export function normalizeMenuBarItemSize(
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize button type prop values (Filled/Outline/Ghost/Danger -> filled/outline/ghost/danger)
|
||||
*/
|
||||
export function normalizeButtonType(
|
||||
value: string | undefined,
|
||||
defaultValue: "filled" = "filled"
|
||||
): "filled" | "outline" | "ghost" | "danger" {
|
||||
if (!value) return defaultValue;
|
||||
const normalized = value.toLowerCase();
|
||||
const types = ["filled", "outline", "ghost", "danger"];
|
||||
if (types.includes(normalized)) {
|
||||
return normalized as typeof defaultValue;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize button palette prop values (Default/Invert -> default/inverse)
|
||||
*/
|
||||
export function normalizeButtonPalette(
|
||||
value: string | undefined,
|
||||
defaultValue: "default" = "default"
|
||||
): "default" | "inverse" {
|
||||
if (!value) return defaultValue;
|
||||
const normalized = value.toLowerCase();
|
||||
// Handle "invert" -> "inverse" mapping
|
||||
if (normalized === "invert" || normalized === "inverse") {
|
||||
return "inverse";
|
||||
}
|
||||
if (normalized === "default") {
|
||||
return "default";
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize button state prop values (Default/Focus/Active/Hover/Disabled -> default/focus/active/hover/disabled)
|
||||
*/
|
||||
export function normalizeButtonState(
|
||||
value: string | undefined,
|
||||
defaultValue: "default" = "default"
|
||||
): "default" | "focus" | "active" | "hover" | "disabled" {
|
||||
if (!value) return defaultValue;
|
||||
const normalized = value.toLowerCase();
|
||||
const states = ["default", "focus", "active", "hover", "disabled"];
|
||||
if (states.includes(normalized)) {
|
||||
return normalized as typeof defaultValue;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type helper for case-insensitive button type prop
|
||||
*/
|
||||
export type ButtonTypeValue =
|
||||
| "filled"
|
||||
| "outline"
|
||||
| "ghost"
|
||||
| "danger"
|
||||
| "Filled"
|
||||
| "Outline"
|
||||
| "Ghost"
|
||||
| "Danger";
|
||||
|
||||
/**
|
||||
* Type helper for case-insensitive button palette prop
|
||||
*/
|
||||
export type ButtonPaletteValue =
|
||||
| "default"
|
||||
| "inverse"
|
||||
| "Default"
|
||||
| "Invert"
|
||||
| "Inverse";
|
||||
|
||||
/**
|
||||
* Type helper for case-insensitive button state prop
|
||||
*/
|
||||
export type ButtonStateValue =
|
||||
| "default"
|
||||
| "focus"
|
||||
| "active"
|
||||
| "hover"
|
||||
| "disabled"
|
||||
| "Default"
|
||||
| "Focus"
|
||||
| "Active"
|
||||
| "Hover"
|
||||
| "Disabled";
|
||||
Reference in New Issue
Block a user