Files
community-rule/lib/propNormalization.ts
T
2026-04-06 16:37:15 -06:00

855 lines
21 KiB
TypeScript

/**
* Utility functions for normalizing component props to match Figma specifications
* while maintaining backward compatibility with existing lowercase usage.
*
* Figma uses PascalCase (e.g., "Standard", "Inverse") but codebase uses lowercase.
* These helpers accept both formats and normalize to lowercase for internal use.
*/
/**
* Normalize mode prop values (Standard/Inverse -> standard/inverse)
*/
export function normalizeMode(
value: string | undefined,
defaultValue: "standard" | "inverse" = "standard",
): "standard" | "inverse" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
if (normalized === "standard" || normalized === "inverse") {
return normalized;
}
return defaultValue;
}
/**
* Normalize state prop values (Default/Hover/Focus/Selected -> default/hover/focus/selected)
*/
export function normalizeState(
value: string | undefined,
defaultValue: "default" | "hover" | "focus" | "selected" = "default",
): "default" | "hover" | "focus" | "selected" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
if (
normalized === "default" ||
normalized === "hover" ||
normalized === "focus" ||
normalized === "selected"
) {
return normalized;
}
return defaultValue;
}
/**
* Normalize state prop values for form inputs (Default/Active/Hover/Focus)
*/
export function normalizeInputState(
value: string | undefined,
defaultValue: "default" | "active" | "hover" | "focus" = "default",
): "default" | "active" | "hover" | "focus" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
if (
normalized === "default" ||
normalized === "active" ||
normalized === "hover" ||
normalized === "focus"
) {
return normalized;
}
return defaultValue;
}
/**
* Normalize toggle state prop values (Default/Hover/Focus/Selected)
*/
export function normalizeToggleState(
value: string | undefined,
defaultValue: "default" | "hover" | "focus" | "selected" = "default",
): "default" | "hover" | "focus" | "selected" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
if (
normalized === "default" ||
normalized === "hover" ||
normalized === "focus" ||
normalized === "selected"
) {
return normalized;
}
return defaultValue;
}
/**
* Type helper for case-insensitive mode prop
*/
export type ModeValue = "standard" | "inverse" | "Standard" | "Inverse";
/**
* Type helper for case-insensitive state prop
*/
export type StateValue =
| "default"
| "hover"
| "focus"
| "selected"
| "Default"
| "Hover"
| "Focus"
| "Selected";
/**
* Type helper for case-insensitive input state prop
*/
export type InputStateValue =
| "default"
| "active"
| "hover"
| "focus"
| "Default"
| "Active"
| "Hover"
| "Focus";
/**
* Normalize button size prop values
*/
export function normalizeSize(
value: string | undefined,
defaultValue: "xsmall" | "small" | "medium" | "large" | "xlarge" = "xsmall",
): "xsmall" | "small" | "medium" | "large" | "xlarge" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const sizes = ["xsmall", "small", "medium", "large", "xlarge"];
if (sizes.includes(normalized)) {
return normalized as "xsmall" | "small" | "medium" | "large" | "xlarge";
}
return defaultValue;
}
/**
* Normalize alert status prop values
*/
export function normalizeAlertStatus(
value: string | undefined,
defaultValue: "default" = "default",
): "default" | "positive" | "warning" | "danger" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const statuses = ["default", "positive", "warning", "danger"];
if (statuses.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize alert type prop values
*/
export function normalizeAlertType(
value: string | undefined,
defaultValue: "toast" = "toast",
): "toast" | "banner" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const types = ["toast", "banner"];
if (types.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize tooltip position prop values
*/
export function normalizeTooltipPosition(
value: string | undefined,
defaultValue: "top" = "top",
): "top" | "bottom" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const positions = ["top", "bottom"];
if (positions.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Type helper for case-insensitive size prop
*/
export type SizeValue =
| "xsmall"
| "small"
| "medium"
| "large"
| "xlarge"
| "XSmall"
| "Small"
| "Medium"
| "Large"
| "XLarge";
/**
* Normalize MenuBar size prop values to Figma specifications
* Maps to: "X Small" | "Small" | "Medium" | "Large" | "X Large"
* Also supports legacy format for backward compatibility
*/
export function normalizeMenuBarSize(
value: string | undefined,
defaultValue:
| "X Small"
| "Small"
| "Medium"
| "Large"
| "X Large" = "X Small",
): "X Small" | "Small" | "Medium" | "Large" | "X Large" {
if (!value) return defaultValue;
if (
value === "X Small" ||
value === "Small" ||
value === "Medium" ||
value === "Large" ||
value === "X Large"
) {
return value;
}
return defaultValue;
}
/**
* Normalize navigation item variant prop values
*/
export function normalizeNavigationItemVariant(
value: string | undefined,
defaultValue: "default" = "default",
): "default" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
if (normalized === "default") {
return "default";
}
return defaultValue;
}
/**
* Normalize navigation item size prop values
*/
export function normalizeNavigationItemSize(
value: string | undefined,
defaultValue: "default" = "default",
): "default" | "xsmall" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const sizes = ["default", "xsmall"];
if (sizes.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize content lockup variant prop values
*/
export function normalizeContentLockupVariant(
value: string | undefined,
defaultValue: "hero" = "hero",
): "hero" | "feature" | "learn" | "ask" | "ask-inverse" | "modal" | "login" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const variants = [
"hero",
"feature",
"learn",
"ask",
"ask-inverse",
"modal",
"login",
];
if (variants.includes(normalized)) {
return normalized as
| "hero"
| "feature"
| "learn"
| "ask"
| "ask-inverse"
| "modal"
| "login";
}
return defaultValue;
}
/**
* Normalize alignment prop values
*/
export function normalizeAlignment(
value: string | undefined,
defaultValue: "center" = "center",
): "center" | "left" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const alignments = ["center", "left"];
if (alignments.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize numbered list size prop values
*/
export function normalizeNumberedListSize(
value: string | undefined,
defaultValue: "M" = "M",
): "M" | "S" {
if (!value) return defaultValue;
const normalized = value.toUpperCase();
if (normalized === "M" || normalized === "S") {
return normalized;
}
return defaultValue;
}
/**
* Normalize header lockup justification prop values
*/
export function normalizeHeaderLockupJustification(
value: string | undefined,
defaultValue: "left" = "left",
): "left" | "center" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
if (normalized === "left" || normalized === "center") {
return normalized;
}
return defaultValue;
}
/**
* Normalize header lockup size prop values
*/
export function normalizeHeaderLockupSize(
value: string | undefined,
defaultValue: "L" = "L",
): "L" | "M" {
if (!value) return defaultValue;
const normalized = value.toUpperCase();
if (normalized === "L" || normalized === "M") {
return normalized;
}
return defaultValue;
}
/**
* Normalize header lockup palette prop values (Default/Inverse -> default/inverse)
*/
export function normalizeHeaderLockupPalette(
value: string | undefined,
defaultValue: "default" = "default",
): "default" | "inverse" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
if (normalized === "default" || normalized === "inverse") {
return normalized;
}
return defaultValue;
}
/**
* Normalize text input size prop values
*/
export function normalizeTextInputSize(
value: string | undefined,
defaultValue: "medium" = "medium",
): "small" | "medium" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
if (normalized === "small" || normalized === "medium") {
return normalized;
}
return defaultValue;
}
/**
* Normalize content container size prop values
*/
export function normalizeContentContainerSize(
value: string | undefined,
defaultValue: "responsive" = "responsive",
): "xs" | "responsive" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const sizes = ["xs", "responsive"];
if (sizes.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize content thumbnail variant prop values
*/
export function normalizeContentThumbnailVariant(
value: string | undefined,
defaultValue: "vertical" = "vertical",
): "vertical" | "horizontal" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const variants = ["vertical", "horizontal"];
if (variants.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize section header variant prop values
*/
export function normalizeSectionHeaderVariant(
value: string | undefined,
defaultValue: "default" = "default",
): "default" | "multi-line" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const variants = ["default", "multi-line"];
if (variants.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize quote block variant prop values
*/
export function normalizeQuoteBlockVariant(
value: string | undefined,
defaultValue: "standard" = "standard",
): "compact" | "standard" | "extended" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const variants = ["compact", "standard", "extended"];
if (variants.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize number card size prop values (already PascalCase in codebase, supports both)
*/
export function normalizeNumberCardSize(
value: string | undefined,
defaultValue: "Medium" = "Medium",
): "Small" | "Medium" | "Large" | "XLarge" {
if (!value) return defaultValue;
// Check if already PascalCase
if (
value === "Small" ||
value === "Medium" ||
value === "Large" ||
value === "XLarge"
) {
return value;
}
// Normalize lowercase to PascalCase
const normalized = value.toLowerCase();
if (normalized === "small") return "Small";
if (normalized === "medium") return "Medium";
if (normalized === "large") return "Large";
if (normalized === "xlarge") return "XLarge";
return defaultValue;
}
/**
* Normalize ask organizer variant prop values
*/
export function normalizeAskOrganizerVariant(
value: string | undefined,
defaultValue: "centered" = "centered",
): "centered" | "left-aligned" | "compact" | "inverse" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const variants = ["centered", "left-aligned", "compact", "inverse"];
if (variants.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize context menu item size prop values
*/
export function normalizeContextMenuItemSize(
value: string | undefined,
defaultValue: "medium" = "medium",
): "small" | "medium" | "large" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const sizes = ["small", "medium", "large"];
if (sizes.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize image placeholder color prop values
*/
export function normalizeImagePlaceholderColor(
value: string | undefined,
defaultValue: "blue" = "blue",
): "blue" | "green" | "purple" | "red" | "orange" | "teal" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const colors = ["blue", "green", "purple", "red", "orange", "teal"];
if (colors.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize toggle group position prop values
*/
export function normalizeToggleGroupPosition(
value: string | undefined,
defaultValue: "left" = "left",
): "left" | "middle" | "right" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const positions = ["left", "middle", "right"];
if (positions.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize label variant prop values
*/
export function normalizeLabelVariant(
value: string | undefined,
defaultValue: "default" = "default",
): "default" | "horizontal" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const variants = ["default", "horizontal"];
if (variants.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize TextArea appearance prop (default/embedded; Figma: Default/Embedded).
*/
export function normalizeTextAreaAppearance(
value: string | undefined,
defaultValue: "default" = "default",
): "default" | "embedded" {
if (!value) return defaultValue;
const n = value.toLowerCase();
return n === "embedded" ? "embedded" : "default";
}
/**
* Normalize small/medium/large size prop values (for SelectInput, TextArea, etc.)
*/
export function normalizeSmallMediumLargeSize(
value: string | undefined,
defaultValue: "medium" = "medium",
): "small" | "medium" | "large" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const sizes = ["small", "medium", "large"];
if (sizes.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize RuleCard size prop values (L/M -> l/m -> L/M)
*/
export function normalizeRuleCardSize(
value: string | undefined,
defaultValue: "L" = "L",
): "XS" | "S" | "M" | "L" {
if (!value) return defaultValue;
const normalized = value.toUpperCase();
if (
normalized === "XS" ||
normalized === "S" ||
normalized === "M" ||
normalized === "L"
) {
return normalized;
}
return defaultValue;
}
/**
* Type helper for case-insensitive Chip state prop
*/
export type ChipStateValue =
| "unselected"
| "selected"
| "disabled"
| "custom"
| "Unselected"
| "Selected"
| "Disabled"
| "Custom";
/**
* Type helper for case-insensitive Chip palette prop
*/
export type ChipPaletteValue = "default" | "inverse" | "Default" | "Inverse";
/**
* Type helper for case-insensitive Chip size prop
*/
export type ChipSizeValue = "s" | "m" | "S" | "M";
/**
* Normalize Chip state prop values (Unselected/Selected/Disabled/Custom)
*/
export function normalizeChipState(
value: string | undefined,
defaultValue: "unselected" = "unselected",
): "unselected" | "selected" | "disabled" | "custom" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const states = ["unselected", "selected", "disabled", "custom"];
if (states.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize Chip palette prop values (Default/Inverse -> default/inverse)
*/
export function normalizeChipPalette(
value: string | undefined,
defaultValue: "default" = "default",
): "default" | "inverse" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const palettes = ["default", "inverse"];
if (palettes.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize Chip size prop values (S/M -> s/m)
*/
export function normalizeChipSize(
value: string | undefined,
defaultValue: "s" = "s",
): "s" | "m" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const sizes = ["s", "m"];
if (sizes.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize MultiSelect size prop values (S/M -> s/m)
*/
export function normalizeMultiSelectSize(
value: string | undefined,
defaultValue: "m" = "m",
): "s" | "m" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const sizes = ["s", "m"];
if (sizes.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize InputLabel size prop values (S/M -> s/m)
*/
export function normalizeInputLabelSize(
value: string | undefined,
defaultValue: "s" = "s",
): "s" | "m" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const sizes = ["s", "m"];
if (sizes.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize InputLabel palette prop values (Default/Inverse -> default/inverse)
*/
export function normalizeInputLabelPalette(
value: string | undefined,
defaultValue: "default" = "default",
): "default" | "inverse" {
if (!value) return defaultValue;
const normalized = value.toLowerCase();
const palettes = ["default", "inverse"];
if (palettes.includes(normalized)) {
return normalized as typeof defaultValue;
}
return defaultValue;
}
/**
* Normalize MenuBarItem state prop values (Default/Hover/Selected -> default/hover/selected)
*/
export function normalizeMenuBarItemState(
value: string | undefined,
defaultValue: "default" | "hover" | "selected" = "default",
): "default" | "hover" | "selected" {
if (!value) return defaultValue;
if (value === "default" || value === "hover" || value === "selected") {
return value;
}
return defaultValue;
}
/**
* Normalize MenuBarItem mode prop values.
* Default mode: yellow text on dark background (standard header)
* Inverse mode: black text on yellow background (folderTop variant)
*/
export function normalizeMenuBarItemMode(
value: string | undefined,
defaultValue: "default" | "inverse" = "default",
): "default" | "inverse" {
if (!value) return defaultValue;
if (value === "default" || value === "inverse") {
return value;
}
return defaultValue;
}
/**
* Normalize MenuBarItem size prop values.
* Accepts: "X Small", "Small", "Medium", "Large", "X Large"
*/
export function normalizeMenuBarItemSize(
value: string | undefined,
defaultValue:
| "X Small"
| "Small"
| "Medium"
| "Large"
| "X Large" = "X Small",
): "X Small" | "Small" | "Medium" | "Large" | "X Large" {
if (!value) return defaultValue;
if (
value === "X Small" ||
value === "Small" ||
value === "Medium" ||
value === "Large" ||
value === "X Large"
) {
return value;
}
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";