Update TopNav component
This commit is contained in:
@@ -46,7 +46,7 @@ const Button = memo<ButtonProps>(
|
||||
const size = normalizeSize(sizeProp);
|
||||
const sizeStyles: Record<string, string> = {
|
||||
xsmall:
|
||||
"p-[var(--spacing-scale-006)] gap-[var(--spacing-scale-002)]",
|
||||
"p-[var(--spacing-scale-004)] gap-[var(--spacing-scale-002)]",
|
||||
small:
|
||||
"p-[var(--spacing-scale-008)] gap-[var(--spacing-scale-002)]",
|
||||
medium: "p-[var(--spacing-scale-010)] gap-[var(--spacing-scale-004)]",
|
||||
@@ -102,7 +102,7 @@ const Button = memo<ButtonProps>(
|
||||
? ""
|
||||
: hoverOutlineStyles[size];
|
||||
|
||||
const baseStyles = `inline-flex items-center justify-start box-border ${sizeStyles[size]} rounded-[var(--radius-measures-radius-full)] ${fontStyles[size]} transition-all duration-500 ease-in-out cursor-pointer ${variantStyles[variant]} ${outlineStyles}`;
|
||||
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}`;
|
||||
|
||||
const sharedA11y = {
|
||||
|
||||
@@ -19,13 +19,13 @@ const Avatar = memo<AvatarProps>(
|
||||
// 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]",
|
||||
small: "w-[var(--spacing-scale-016)] h-[var(--spacing-scale-016)] border-[1.5px] border-[#FFFFFF4D] border-solid",
|
||||
medium: "w-[var(--spacing-scale-018)] h-[var(--spacing-scale-018)]",
|
||||
large: "w-[var(--spacing-scale-024)] h-[var(--spacing-scale-024)]",
|
||||
xlarge: "w-[var(--spacing-scale-032)] h-[var(--spacing-scale-032)]",
|
||||
};
|
||||
|
||||
const baseStyles = `rounded-[var(--radius-measures-radius-full)] object-cover ${sizeStyles[size]} ${className}`;
|
||||
const baseStyles = `rounded-[var(--radius-measures-radius-full)] object-cover box-border ${sizeStyles[size]} ${className}`;
|
||||
|
||||
return <img src={src} alt={alt} className={baseStyles} {...props} />;
|
||||
},
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import { memo } from "react";
|
||||
import { getAssetPath } from "../../../lib/assetUtils";
|
||||
|
||||
interface HeaderTabProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
stretch?: boolean;
|
||||
}
|
||||
|
||||
const HeaderTab = memo<HeaderTabProps>(
|
||||
({ children, className = "", stretch = false, ...props }) => {
|
||||
const stretchClasses = stretch
|
||||
? "flex-1 sm:mr-[var(--spacing-scale-008)] md:mr-[185px] lg:mr-[var(--spacing-scale-024)] xl:mr-[var(--spacing-scale-032)]"
|
||||
: "";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`HeaderTab header-breakpoint-transition relative bg-[var(--color-surface-inverse-brand-primary)] rounded-t-[32px] sm:rounded-t-[32px] md:rounded-t-[32px] lg:rounded-t-[32px] xl:rounded-t-[32px] pl-[var(--spacing-scale-012)] h-[40px] sm:h-[52px] md:h-[52px] lg:h-[52px] xl:h-[64px] sm:pr-[var(--spacing-scale-006)] md:pl-[var(--spacing-scale-024)] lg:pl-[var(--spacing-scale-024)] xl:pl-[var(--spacing-scale-032)] md:pr-[var(--spacing-scale-012)] lg:pr-[var(--spacing-scale-048)] xl:pr-[var(--spacing-scale-120)] md:gap-[var(--spacing-scale-032)] ${stretchClasses} ${className}`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<img
|
||||
src={getAssetPath("assets/Union_xsm.svg")}
|
||||
alt=""
|
||||
role="presentation"
|
||||
className="absolute -bottom-[3px] -right-[52px] w-[61px] h-[24px] sm:w-[61px] sm:h-[31.5px] sm:hidden -z-10"
|
||||
/>
|
||||
<img
|
||||
src={getAssetPath("assets/Union_sm_md_lg.svg")}
|
||||
alt=""
|
||||
role="presentation"
|
||||
className="absolute -bottom-[3.7px] -right-[53px] w-[61px] h-[24px] sm:w-[61px] sm:h-[31.5px] hidden sm:block xl:hidden -z-10"
|
||||
/>
|
||||
<img
|
||||
src={getAssetPath("assets/Union_xlg.svg")}
|
||||
alt=""
|
||||
role="presentation"
|
||||
className="absolute -bottom-[6px] -right-[94px] w-[105px] h-[53px] hidden xl:block -z-10"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
HeaderTab.displayName = "HeaderTab";
|
||||
|
||||
export default HeaderTab;
|
||||
@@ -5,39 +5,42 @@ import { useTranslation } from "../../contexts/MessagesContext";
|
||||
import { normalizeMenuBarSize } from "../../../lib/propNormalization";
|
||||
|
||||
export type MenuBarSizeValue =
|
||||
| "xsmall"
|
||||
| "default"
|
||||
| "medium"
|
||||
| "large"
|
||||
| "XSmall"
|
||||
| "Default"
|
||||
| "X Small"
|
||||
| "Small"
|
||||
| "Medium"
|
||||
| "Large";
|
||||
| "Large"
|
||||
| "X Large";
|
||||
|
||||
interface MenuBarProps extends React.HTMLAttributes<HTMLElement> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
/**
|
||||
* Menu bar size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
* Menu bar size. Uses Figma format: "X Small", "Small", "Medium", "Large", "X Large".
|
||||
* @default "X Small"
|
||||
*/
|
||||
size?: MenuBarSizeValue;
|
||||
}
|
||||
|
||||
const MenuBar = memo<MenuBarProps>(
|
||||
({ children, className = "", size: sizeProp = "default", ...props }) => {
|
||||
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
|
||||
({ children, className = "", size: sizeProp = "X Small", ...props }) => {
|
||||
const size = normalizeMenuBarSize(sizeProp);
|
||||
const t = useTranslation("menuBar");
|
||||
const sizeStyles: Record<string, string> = {
|
||||
xsmall:
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-001)] rounded-[4px]",
|
||||
default:
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-001)]",
|
||||
medium:
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-004)]",
|
||||
large:
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-012)]",
|
||||
|
||||
// Size styles based on Figma specifications
|
||||
const sizeStyles: Record<
|
||||
"X Small" | "Small" | "Medium" | "Large" | "X Large",
|
||||
string
|
||||
> = {
|
||||
"X Small":
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-001)] rounded-[var(--spacing-scale-004)]",
|
||||
Small:
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-004)] rounded-[var(--spacing-scale-004)]",
|
||||
Medium:
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-004)] rounded-[var(--spacing-scale-004)]",
|
||||
Large:
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-012)] rounded-[var(--spacing-scale-004)]",
|
||||
"X Large":
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-012)] rounded-[var(--spacing-scale-004)]",
|
||||
};
|
||||
|
||||
const baseStyles = `flex items-center ${sizeStyles[size]} ${className}`;
|
||||
|
||||
@@ -3,152 +3,108 @@
|
||||
import { memo } from "react";
|
||||
import MenuBarItemView from "./MenuBarItem.view";
|
||||
import type { MenuBarItemProps } from "./MenuBarItem.types";
|
||||
import { normalizeMenuBarItemVariant } from "../../../../lib/propNormalization";
|
||||
import {
|
||||
normalizeMenuBarItemState,
|
||||
normalizeMenuBarItemMode,
|
||||
normalizeMenuBarItemSize,
|
||||
} from "../../../../lib/propNormalization";
|
||||
|
||||
const MenuBarItemContainer = memo<MenuBarItemProps>(
|
||||
({
|
||||
href = "#",
|
||||
children,
|
||||
variant: variantProp = "default",
|
||||
size: sizeProp = "default",
|
||||
state: stateProp,
|
||||
mode: modeProp,
|
||||
icon: _icon = false,
|
||||
size: sizeProp = "X Small",
|
||||
className = "",
|
||||
disabled = false,
|
||||
isActive = false,
|
||||
reducedPadding = 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> = {
|
||||
const state = normalizeMenuBarItemState(stateProp, "default");
|
||||
const mode = normalizeMenuBarItemMode(modeProp, "default");
|
||||
const size = normalizeMenuBarItemSize(sizeProp, "X Small");
|
||||
|
||||
// Size styles based on Figma specifications
|
||||
const sizeStyles: Record<
|
||||
"X Small" | "Small" | "Medium" | "Large" | "X Large",
|
||||
string
|
||||
> = {
|
||||
"X Small": reducedPadding ? "px-[var(--spacing-scale-004)] py-[var(--spacing-scale-002)]" : "px-[var(--spacing-scale-004)] py-[var(--spacing-scale-002)]",
|
||||
Small: "px-[var(--spacing-scale-004)] py-[var(--spacing-scale-002)]",
|
||||
Medium: reducedPadding ? "px-[var(--spacing-scale-002)] py-[var(--spacing-scale-008)] h-[32px]" : "px-[var(--spacing-scale-008)] py-[var(--spacing-scale-008)] h-[32px]",
|
||||
Large: "px-[var(--spacing-scale-016)] py-[var(--spacing-scale-016)] h-[44px]",
|
||||
"X Large": "px-[var(--spacing-scale-016)] py-[var(--spacing-scale-016)] h-[44px]",
|
||||
};
|
||||
|
||||
// Text styles based on Figma specifications
|
||||
const textStyles: Record<
|
||||
"X Small" | "Small" | "Medium" | "Large" | "X Large",
|
||||
string
|
||||
> = {
|
||||
"X Small":
|
||||
"font-inter text-[10px] leading-[12px] font-medium tracking-[0%]",
|
||||
Small:
|
||||
"font-inter text-[12px] leading-[14px] font-medium tracking-[0%]",
|
||||
Medium:
|
||||
"font-inter text-[12px] leading-[14px] font-medium tracking-[0%]",
|
||||
Large:
|
||||
"font-inter text-[16px] leading-[20px] font-medium tracking-[0%]",
|
||||
"X Large":
|
||||
"font-inter text-[24px] leading-[28px] font-normal tracking-[0%]",
|
||||
};
|
||||
|
||||
// State styles for Default mode (yellow text on dark background)
|
||||
const defaultModeStyles: Record<
|
||||
"default" | "hover" | "selected",
|
||||
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",
|
||||
home: "bg-transparent text-[var(--color-content-inverse-primary)] hover:bg-[var(--color-content-default-brand-accent)] hover:text-[var(--color-content-inverse-primary)] hover:scale-[1.02] active:bg-transparent active:text-[var(--color-content-inverse-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",
|
||||
"bg-transparent text-[var(--color-content-default-brand-primary,#fefcc9)] hover:bg-[var(--color-gray-800)] hover:text-[var(--color-content-default-brand-primary,#fefcc9)]",
|
||||
hover:
|
||||
"bg-[var(--color-gray-800)] text-[var(--color-content-default-brand-primary,#fefcc9)]",
|
||||
selected:
|
||||
"border border-[var(--color-border-default-brand-primary,#fdfaa8)] text-[var(--color-content-default-brand-primary,#fefcc9)] bg-transparent hover:bg-[var(--color-gray-800)]",
|
||||
};
|
||||
|
||||
const activeOutlineStyles: Record<string, string> = {
|
||||
xsmall:
|
||||
"active:outline-1 active:outline-[var(--color-content-default-primary)] focus:outline-1 focus:outline-[var(--color-content-default-primary)]",
|
||||
xsmallUseCases:
|
||||
"active:outline-1 active:outline-[var(--color-content-default-primary)] focus:outline-1 focus:outline-[var(--color-content-default-primary)]",
|
||||
// State styles for Inverse mode (black text on yellow background)
|
||||
const inverseModeStyles: Record<
|
||||
"default" | "hover" | "selected",
|
||||
string
|
||||
> = {
|
||||
default:
|
||||
"active:outline-[1.5px] active:outline-[var(--color-content-default-brand-primary)] focus:outline-[1.5px] focus:outline-[var(--color-content-default-brand-primary)]",
|
||||
homeMd:
|
||||
"active:outline-[1.5px] active:outline-[var(--color-content-default-brand-primary)] focus:outline-[1.5px] focus:outline-[var(--color-content-default-brand-primary)]",
|
||||
homeUseCases:
|
||||
"active:outline-[1.5px] active:outline-[var(--color-content-default-brand-primary)] focus:outline-[1.5px] focus:outline-[var(--color-content-default-brand-primary)]",
|
||||
large:
|
||||
"active:outline-[1.75px] active:outline-[var(--color-content-default-brand-primary)] focus:outline-[1.75px] focus:outline-[var(--color-content-default-brand-primary)]",
|
||||
largeUseCases:
|
||||
"active:outline-[1.75px] active:outline-[var(--color-content-default-brand-primary)] focus:outline-[1.75px] focus:outline-[var(--color-content-default-brand-primary)]",
|
||||
homeXlarge:
|
||||
"active:outline-[2px] active:outline-[var(--color-content-default-brand-primary)] focus:outline-[2px] focus:outline-[var(--color-content-default-brand-primary)]",
|
||||
xlarge:
|
||||
"active:outline-2 active:outline-[var(--color-content-default-brand-primary)] focus:outline-2 focus:outline-[var(--color-content-default-brand-primary)]",
|
||||
"bg-transparent text-[var(--color-content-inverse-primary,black)] hover:bg-[var(--color-surface-brand-accent,#4d4a00)] hover:text-[var(--color-content-inverse-primary,black)]",
|
||||
hover:
|
||||
"bg-[var(--color-surface-brand-accent,#4d4a00)] text-[var(--color-content-inverse-primary,black)]",
|
||||
selected:
|
||||
"border border-[var(--color-border-default-primary,#141414)] text-[var(--color-content-inverse-primary,black)] bg-transparent hover:bg-[var(--color-surface-brand-accent,#4d4a00)]",
|
||||
};
|
||||
|
||||
const homeOutlineStyles: Record<string, string> = {
|
||||
xsmall:
|
||||
"active:outline-1 active:outline-[var(--color-content-default-primary)] focus:outline-1 focus:outline-[var(--color-content-default-primary)]",
|
||||
xsmallUseCases:
|
||||
"active:outline-1 active:outline-[var(--color-content-default-primary)] focus:outline-1 focus:outline-[var(--color-content-default-primary)]",
|
||||
default:
|
||||
"active:outline-[1.5px] active:outline-[var(--color-content-default-primary)] focus:outline-[1.5px] focus:outline-[var(--color-content-default-primary)]",
|
||||
homeMd:
|
||||
"active:outline-[1.5px] active:outline-[var(--color-content-default-primary)] focus:outline-[1.5px] focus:outline-[var(--color-content-default-primary)]",
|
||||
homeUseCases:
|
||||
"active:outline-[1.5px] active:outline-[var(--color-content-default-primary)] focus:outline-[1.5px] focus:outline-[var(--color-content-default-primary)]",
|
||||
largeUseCases:
|
||||
"active:outline-[1.75px] active:outline-[var(--color-content-default-primary)] focus:outline-[1.75px] focus:outline-[var(--color-content-default-primary)]",
|
||||
large:
|
||||
"active:outline-[1.75px] active:outline-[var(--color-content-default-primary)] focus:outline-[1.75px] focus:outline-[var(--color-content-default-primary)]",
|
||||
homeXlarge:
|
||||
"active:outline-[2px] active:outline-[var(--color-content-default-primary)] focus:outline-[2px] focus:outline-[var(--color-content-default-primary)]",
|
||||
xlarge:
|
||||
"active:outline-2 active:outline-[var(--color-content-default-primary)] focus:outline-2 focus:outline-[var(--color-content-default-primary)]",
|
||||
};
|
||||
// Get state styles based on mode
|
||||
const stateStyles =
|
||||
mode === "inverse" ? inverseModeStyles : defaultModeStyles;
|
||||
|
||||
const activeStateStyles: Record<string, string> = {
|
||||
xsmall:
|
||||
"!outline-1 !outline-[var(--color-content-default-brand-primary)] !text-[var(--color-content-default-brand-primary)] focus:!outline-1 focus:!outline-[var(--color-content-default-brand-primary)]",
|
||||
xsmallUseCases:
|
||||
"!outline-1 !outline-[var(--color-content-default-brand-primary)] !text-[var(--color-content-default-brand-primary)] focus:!outline-1 focus:!outline-[var(--color-content-default-brand-primary)]",
|
||||
default:
|
||||
"!outline-[1.5px] !outline-[var(--color-content-default-brand-primary)] !text-[var(--color-content-default-brand-primary)] focus:!outline-[1.5px] focus:!outline-[var(--color-content-default-brand-primary)]",
|
||||
homeMd:
|
||||
"!outline-[1.5px] !outline-[var(--color-content-default-brand-primary)] !text-[var(--color-content-default-brand-primary)] focus:!outline-[1.5px] focus:!outline-[var(--color-content-default-brand-primary)]",
|
||||
homeUseCases:
|
||||
"!outline-[1.5px] !outline-[var(--color-content-default-brand-primary)] !text-[var(--color-content-default-brand-primary)] focus:!outline-[1.5px] focus:!outline-[var(--color-content-default-brand-primary)]",
|
||||
large:
|
||||
"!outline-[1.75px] !outline-[var(--color-content-default-brand-primary)] !text-[var(--color-content-default-brand-primary)] focus:!outline-[1.75px] focus:!outline-[var(--color-content-default-brand-primary)]",
|
||||
largeUseCases:
|
||||
"!outline-[1.75px] !outline-[var(--color-content-default-brand-primary)] !text-[var(--color-content-default-brand-primary)] focus:!outline-[1.75px] focus:!outline-[var(--color-content-default-brand-primary)]",
|
||||
homeXlarge:
|
||||
"!outline-[2px] !outline-[var(--color-content-default-brand-primary)] !text-[var(--color-content-default-brand-primary)] focus:!outline-[2px] focus:!outline-[var(--color-content-default-brand-primary)]",
|
||||
xlarge:
|
||||
"!outline-2 !outline-[var(--color-content-default-brand-primary)] !text-[var(--color-content-default-brand-primary)] focus:!outline-2 focus:!outline-[var(--color-content-default-brand-primary)]",
|
||||
};
|
||||
// Base styles
|
||||
const baseStyles = `inline-flex items-center whitespace-nowrap ${sizeStyles[size]} ${textStyles[size]} rounded-[var(--radius-measures-radius-full)] transition-all duration-200 ease-in-out cursor-pointer`;
|
||||
|
||||
const sizeStyles: Record<string, string> = {
|
||||
default:
|
||||
"px-[var(--spacing-measures-spacing-016)] py-[var(--spacing-measures-spacing-016)] gap-[var(--spacing-scale-004)]",
|
||||
xsmall:
|
||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-002)] gap-[var(--spacing-scale-004)]",
|
||||
xsmallUseCases:
|
||||
"px-[var(--spacing-scale-002)] py-[var(--spacing-scale-002)] gap-[var(--spacing-scale-004)]",
|
||||
homeMd:
|
||||
"px-[var(--spacing-scale-008)] py-[var(--spacing-scale-008)] gap-[var(--spacing-scale-004)]",
|
||||
homeUseCases:
|
||||
"px-[var(--spacing-scale-002)] py-[var(--spacing-scale-008)] gap-[var(--spacing-scale-004)]",
|
||||
large:
|
||||
"px-[var(--spacing-scale-012)] py-[var(--spacing-scale-012)] gap-[var(--spacing-scale-004)] h-[44px]",
|
||||
largeUseCases:
|
||||
"px-[var(--spacing-scale-012)] py-[var(--spacing-scale-012)] gap-[var(--spacing-scale-004)] h-[44px]",
|
||||
homeXlarge:
|
||||
"px-[var(--spacing-scale-016)] py-[var(--spacing-scale-016)] gap-[var(--spacing-scale-004)] h-[44px]",
|
||||
xlarge:
|
||||
"px-[var(--spacing-scale-016)] py-[var(--spacing-scale-008)] gap-[var(--spacing-scale-004)] h-[44px]",
|
||||
};
|
||||
// Interactive styles
|
||||
const interactiveStyles =
|
||||
"hover:scale-[1.02] active:scale-[0.98] focus:scale-[1.02] disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 disabled:active:scale-100";
|
||||
|
||||
const smallTextStyle =
|
||||
"font-inter text-[10px] leading-[12px] font-medium tracking-[0%]";
|
||||
const mediumTextStyle =
|
||||
"font-inter text-[12px] leading-[14px] font-medium tracking-[0%]";
|
||||
const largeTextStyle =
|
||||
"font-inter text-[16px] leading-[20px] font-medium tracking-[0%]";
|
||||
const xlargeTextStyle =
|
||||
"font-inter text-[24px] leading-[28px] font-normal tracking-[0%]";
|
||||
// Disabled styles
|
||||
const disabledStyles = disabled
|
||||
? "bg-[var(--color-surface-default-tertiary)] text-[var(--color-content-default-tertiary)]"
|
||||
: "";
|
||||
|
||||
const textStyles: Record<string, string> = {
|
||||
default: smallTextStyle,
|
||||
xsmall: smallTextStyle,
|
||||
xsmallUseCases: smallTextStyle,
|
||||
home: smallTextStyle,
|
||||
homeMd: mediumTextStyle,
|
||||
homeUseCases: mediumTextStyle,
|
||||
large: largeTextStyle,
|
||||
largeUseCases: largeTextStyle,
|
||||
homeXlarge: xlargeTextStyle,
|
||||
xlarge: xlargeTextStyle,
|
||||
};
|
||||
|
||||
const baseStyles = `inline-flex items-center ${sizeStyles[size]} rounded-[var(--radius-measures-radius-full)] ${textStyles[size]} transition-all duration-200 ease-in-out cursor-pointer focus:scale-[1.02]`;
|
||||
|
||||
let finalVariant = variant;
|
||||
if (disabled) {
|
||||
finalVariant = "default";
|
||||
}
|
||||
|
||||
const combinedStyles = `${baseStyles} ${variantStyles[finalVariant]} ${
|
||||
finalVariant === "home"
|
||||
? homeOutlineStyles[size]
|
||||
: activeOutlineStyles[size]
|
||||
} ${isActive ? activeStateStyles[size] : ""} ${className}`;
|
||||
// Combine all styles
|
||||
const combinedStyles = `${baseStyles} ${stateStyles[state]} ${interactiveStyles} ${disabledStyles} ${className}`;
|
||||
|
||||
const accessibilityProps = {
|
||||
...(ariaLabel && { "aria-label": ariaLabel }),
|
||||
...(disabled && { "aria-disabled": true }),
|
||||
...(state === "selected" && { "aria-current": "page" as const }),
|
||||
role: "menuitem" as const,
|
||||
tabIndex: disabled ? -1 : 0,
|
||||
...props,
|
||||
|
||||
@@ -1,43 +1,50 @@
|
||||
export type MenuBarItemSizeValue =
|
||||
| "default"
|
||||
| "xsmall"
|
||||
| "xsmallUseCases"
|
||||
| "home"
|
||||
| "homeMd"
|
||||
| "homeUseCases"
|
||||
| "large"
|
||||
| "largeUseCases"
|
||||
| "homeXlarge"
|
||||
| "xlarge"
|
||||
| "Default"
|
||||
| "XSmall"
|
||||
| "XSmallUseCases"
|
||||
| "Home"
|
||||
| "HomeMd"
|
||||
| "HomeUseCases"
|
||||
| "X Small"
|
||||
| "Small"
|
||||
| "Medium"
|
||||
| "Large"
|
||||
| "LargeUseCases"
|
||||
| "HomeXlarge"
|
||||
| "XLarge";
|
||||
| "X Large";
|
||||
|
||||
export type MenuBarItemVariantValue = "default" | "home" | "Default" | "Home";
|
||||
export type MenuBarItemStateValue =
|
||||
| "default"
|
||||
| "hover"
|
||||
| "selected";
|
||||
|
||||
export type MenuBarItemModeValue =
|
||||
| "default"
|
||||
| "inverse";
|
||||
|
||||
export interface MenuBarItemProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
href?: string;
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Menu bar item variant. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
* Menu bar item state: "default", "hover", or "selected".
|
||||
* @default "default"
|
||||
*/
|
||||
variant?: MenuBarItemVariantValue;
|
||||
state?: MenuBarItemStateValue;
|
||||
/**
|
||||
* Menu bar item size. Accepts both lowercase and PascalCase (case-insensitive).
|
||||
* Figma uses PascalCase, codebase uses lowercase - both are supported.
|
||||
* Menu bar item mode. Default mode has yellow text on dark background.
|
||||
* Inverse mode has black text on yellow background (for folderTop variant).
|
||||
* @default "default"
|
||||
*/
|
||||
mode?: MenuBarItemModeValue;
|
||||
/**
|
||||
* Whether to show an icon (for future icon support).
|
||||
* @default false
|
||||
*/
|
||||
icon?: boolean;
|
||||
/**
|
||||
* Menu bar item size. Uses Figma format: "X Small", "Small", "Medium", "Large", "X Large".
|
||||
* @default "X Small"
|
||||
*/
|
||||
size?: MenuBarItemSizeValue;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
isActive?: boolean;
|
||||
/**
|
||||
* Whether to use reduced padding (for "use cases" button).
|
||||
* @default false
|
||||
*/
|
||||
reducedPadding?: boolean;
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -99,54 +99,38 @@ const TopNavContainer = memo<TopNavProps>(
|
||||
];
|
||||
|
||||
const renderNavigationItems = (size: NavSize) => {
|
||||
return navigationItems.map((item, index) => {
|
||||
// Determine size based on folderTop and item properties
|
||||
let itemSize: NavSize = size;
|
||||
if (item.extraPadding) {
|
||||
if (folderTop) {
|
||||
if (
|
||||
size === "xsmall" ||
|
||||
size === "default" ||
|
||||
size === "home" ||
|
||||
size === "homeMd" ||
|
||||
size === "large" ||
|
||||
size === "homeXlarge"
|
||||
) {
|
||||
itemSize =
|
||||
size === "home" || size === "homeMd"
|
||||
? "homeMd"
|
||||
: size === "large"
|
||||
? "large"
|
||||
: size === "homeXlarge"
|
||||
? "homeXlarge"
|
||||
: "xsmallUseCases";
|
||||
}
|
||||
} else {
|
||||
if (size === "xsmall") {
|
||||
itemSize = "xsmallUseCases";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Map NavSize to Figma MenuBarItem sizes
|
||||
const sizeMap: Record<NavSize, "X Small" | "Small" | "Medium" | "Large" | "X Large"> = {
|
||||
default: "Small",
|
||||
xsmall: "X Small",
|
||||
xsmallUseCases: "X Small",
|
||||
home: "X Small",
|
||||
homeMd: "Medium",
|
||||
homeUseCases: "Small",
|
||||
large: "Large",
|
||||
largeUseCases: "Large",
|
||||
homeXlarge: "X Large",
|
||||
xlarge: "X Large",
|
||||
};
|
||||
|
||||
// Determine variant based on folderTop
|
||||
const variant = folderTop
|
||||
? size === "xsmall" ||
|
||||
size === "default" ||
|
||||
size === "home" ||
|
||||
size === "homeMd" ||
|
||||
size === "large" ||
|
||||
size === "homeXlarge"
|
||||
? "home"
|
||||
: "default"
|
||||
: "default";
|
||||
// Determine mode based on folderTop
|
||||
const mode = folderTop ? "inverse" : "default";
|
||||
|
||||
return navigationItems.map((item, index) => {
|
||||
// Map size to Figma size
|
||||
let itemSize = sizeMap[size] || "Small";
|
||||
|
||||
// Pass reducedPadding for "use cases" button (item with extraPadding: true)
|
||||
const isUseCases = item.extraPadding === true;
|
||||
|
||||
return (
|
||||
<MenuBarItem
|
||||
key={index}
|
||||
href={item.href}
|
||||
size={itemSize}
|
||||
variant={variant}
|
||||
isActive={pathname === item.href}
|
||||
mode={mode}
|
||||
state={pathname === item.href ? "selected" : "default"}
|
||||
reducedPadding={isUseCases}
|
||||
ariaLabel={t("ariaLabels.navigateToPage").replace("{text}", item.text)}
|
||||
>
|
||||
{item.text}
|
||||
@@ -174,18 +158,32 @@ const TopNavContainer = memo<TopNavProps>(
|
||||
};
|
||||
|
||||
const renderLoginButton = (size: NavSize) => {
|
||||
// Determine variant based on folderTop
|
||||
const variant = folderTop
|
||||
? size === "xsmall" || size === "default"
|
||||
? "home"
|
||||
: "default"
|
||||
: "default";
|
||||
// Map NavSize to Figma MenuBarItem sizes
|
||||
const sizeMap: Record<NavSize, "X Small" | "Small" | "Medium" | "Large" | "X Large"> = {
|
||||
default: "Small",
|
||||
xsmall: "X Small",
|
||||
xsmallUseCases: "X Small",
|
||||
home: "X Small",
|
||||
homeMd: "Medium",
|
||||
homeUseCases: "Small",
|
||||
large: "Large",
|
||||
largeUseCases: "Large",
|
||||
homeXlarge: "X Large",
|
||||
xlarge: "X Large",
|
||||
};
|
||||
|
||||
// Determine mode based on folderTop and breakpoint size
|
||||
// folderTop: inverse mode (black text) for smallest breakpoints (xsmall/home)
|
||||
// folderTop: default mode (yellow text) for 640px+ breakpoints (homeMd/large/homeXlarge/xlarge)
|
||||
// false folderTop: always default mode (yellow text on dark background)
|
||||
const isSmallBreakpoint = size === "xsmall" || size === "home";
|
||||
const mode = folderTop && isSmallBreakpoint ? "inverse" : "default";
|
||||
|
||||
return (
|
||||
<MenuBarItem
|
||||
href="#"
|
||||
size={size}
|
||||
variant={variant}
|
||||
size={sizeMap[size] || "Small"}
|
||||
mode={mode}
|
||||
ariaLabel={t("ariaLabels.logInToAccount")}
|
||||
>
|
||||
{t("buttons.logIn")}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { memo } from "react";
|
||||
import Script from "next/script";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import HeaderTab from "../HeaderTab";
|
||||
import { getAssetPath } from "../../../../lib/assetUtils";
|
||||
import MenuBar from "../MenuBar";
|
||||
import type { TopNavViewProps } from "./TopNav.types";
|
||||
|
||||
@@ -36,11 +36,12 @@ function TopNavView({
|
||||
aria-label={t("ariaLabels.homePageNavigationHeader")}
|
||||
>
|
||||
<nav
|
||||
className="relative flex items-center justify-between mx-auto h-[50px] sm:h-[62px] md:h-[68px] lg:h-[68px] xl:h-[88px] px-[var(--spacing-scale-008)] pr-[var(--spacing-scale-016)] pt-[var(--spacing-scale-010)] sm:px-[var(--spacing-scale-010)] sm:pr-[var(--spacing-scale-020)] sm:pt-[var(--spacing-scale-010)] md:px-[var(--spacing-scale-016)] md:pr-[var(--spacing-scale-032)] md:pt-[var(--spacing-scale-016)] lg:pl-[var(--spacing-scale-024)] lg:pt-[var(--spacing-scale-016)] lg:pr-[var(--spacing-scale-056)] xl:pl-[var(--spacing-scale-048)] xl:pt-[var(--spacing-scale-024)] xl:pr-[var(--spacing-scale-056)]"
|
||||
className="relative flex items-center justify-between mx-auto h-[50px] sm:h-[62px] md:h-[68px] lg:h-[68px] xl:h-[88px] pl-[var(--spacing-scale-008)] pr-[var(--spacing-scale-016)] pt-[var(--spacing-scale-010)] sm:px-[var(--spacing-scale-010)] sm:pr-[var(--spacing-scale-020)] sm:pt-[var(--spacing-scale-010)] md:px-[var(--spacing-scale-016)] md:pr-[var(--spacing-scale-032)] md:pt-[var(--spacing-scale-016)] lg:pl-[var(--spacing-scale-024)] lg:pt-[var(--spacing-scale-016)] lg:pr-[var(--spacing-scale-056)] xl:pl-[var(--spacing-scale-048)] xl:pt-[var(--spacing-scale-024)] xl:pr-[var(--spacing-scale-056)]"
|
||||
role="navigation"
|
||||
aria-label={t("ariaLabels.mainNavigation")}
|
||||
>
|
||||
<HeaderTab className="flex items-center self-end" stretch={true}>
|
||||
{/* Header Tab - Yellow tab container with decorative Union images */}
|
||||
<div className="HeaderTab header-breakpoint-transition relative bg-[var(--color-surface-inverse-brand-primary)] rounded-tl-[var(--radius-measures-radius-medium)] rounded-tr-[var(--radius-measures-radius-medium)] sm:rounded-t-[var(--radius-measures-radius-xlarge)] md:rounded-t-[var(--radius-measures-radius-xlarge)] lg:rounded-t-[var(--radius-measures-radius-xlarge)] xl:rounded-t-[var(--radius-measures-radius-xlarge)] pl-[var(--spacing-scale-012)] pr-[var(--spacing-scale-048)] h-[var(--spacing-scale-040)] sm:pl-[var(--spacing-scale-012)] sm:h-[52px] sm:pr-[var(--spacing-scale-006)] md:h-[52px] md:pl-[var(--spacing-scale-024)] md:pr-[var(--spacing-scale-012)] lg:h-[52px] lg:pl-[var(--spacing-scale-024)] lg:pr-[var(--spacing-scale-048)] xl:h-[64px] xl:pl-[var(--spacing-scale-032)] xl:pr-[var(--spacing-scale-120)] md:gap-[var(--spacing-scale-032)] flex-1 min-w-0 min-w-[197px] sm:min-w-0 sm:mr-[var(--spacing-scale-008)] md:mr-[185px] lg:mr-[var(--spacing-scale-024)] xl:mr-[var(--spacing-scale-032)] flex items-center self-end">
|
||||
{/* Logo - Consistent left positioning within HeaderTab */}
|
||||
<div>
|
||||
{logoConfig.map((config, index) => (
|
||||
@@ -52,32 +53,56 @@ function TopNavView({
|
||||
|
||||
{/* XSmall menu bar - positioned next to logo */}
|
||||
<div className="block sm:hidden -me-[2px]">
|
||||
<MenuBar size="default">
|
||||
<MenuBar size="X Small">
|
||||
{renderNavigationItems("xsmall")}
|
||||
{logIn && renderLoginButton("xsmall")}
|
||||
</MenuBar>
|
||||
</div>
|
||||
</HeaderTab>
|
||||
|
||||
{/* Decorative Union images for tab appearance */}
|
||||
<img
|
||||
src={getAssetPath("assets/Union_xsm.svg")}
|
||||
alt=""
|
||||
role="presentation"
|
||||
className="absolute -bottom-[3px] -right-[52px] w-[61px] h-[24px] sm:w-[61px] sm:h-[31.5px] sm:hidden -z-10"
|
||||
/>
|
||||
<img
|
||||
src={getAssetPath("assets/Union_sm_md_lg.svg")}
|
||||
alt=""
|
||||
role="presentation"
|
||||
className="absolute -bottom-[3.7px] -right-[53px] w-[61px] h-[24px] sm:w-[61px] sm:h-[31.5px] hidden sm:block xl:hidden -z-10"
|
||||
/>
|
||||
<img
|
||||
src={getAssetPath("assets/Union_xlg.svg")}
|
||||
alt=""
|
||||
role="presentation"
|
||||
className="absolute -bottom-[6px] -right-[94px] w-[105px] h-[53px] hidden xl:block -z-10"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Navigation Links - Centered in header for SM and up */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2 hidden sm:block">
|
||||
{/* 430-639px (sm: breakpoint): MenuBar X Small */}
|
||||
<div className="hidden sm:block md:hidden">
|
||||
<MenuBar size="default">
|
||||
<MenuBar size="X Small">
|
||||
{renderNavigationItems("xsmall")}
|
||||
{logIn && renderLoginButton("xsmall")}
|
||||
</MenuBar>
|
||||
</div>
|
||||
|
||||
{/* 640-1023px (md: breakpoint): MenuBar Small */}
|
||||
<div className="hidden md:block lg:hidden">
|
||||
<MenuBar size="medium">{renderNavigationItems("homeMd")}</MenuBar>
|
||||
<MenuBar size="Small">{renderNavigationItems("homeMd")}</MenuBar>
|
||||
</div>
|
||||
|
||||
{/* 1024-1440px (lg: breakpoint): MenuBar Large */}
|
||||
<div className="hidden lg:block xl:hidden">
|
||||
<MenuBar size="large">{renderNavigationItems("large")}</MenuBar>
|
||||
<MenuBar size="Large">{renderNavigationItems("large")}</MenuBar>
|
||||
</div>
|
||||
|
||||
{/* 1440px+ (xl: breakpoint): MenuBar X Large */}
|
||||
<div className="hidden xl:block">
|
||||
<MenuBar size="large">
|
||||
<MenuBar size="X Large">
|
||||
{renderNavigationItems("homeXlarge")}
|
||||
</MenuBar>
|
||||
</div>
|
||||
@@ -133,7 +158,7 @@ function TopNavView({
|
||||
aria-label={t("ariaLabels.mainNavigationHeader")}
|
||||
>
|
||||
<nav
|
||||
className="flex items-center justify-between mx-auto h-[40px] lg:h-[84px] xl:h-[88px] px-[var(--spacing-measures-spacing-016)] py-[var(--spacing-measures-spacing-008)] lg:px-[var(--spacing-measures-spacing-64,64px)] lg:py-[var(--spacing-measures-spacing-016,16px)]"
|
||||
className="flex items-center gap-[var(--spacing-scale-002)] sm:justify-between mx-auto h-[var(--spacing-scale-040)] lg:h-[84px] xl:h-[88px] px-[var(--spacing-scale-016)] py-[var(--spacing-scale-008)] sm:px-[var(--spacing-measures-spacing-016)] sm:py-[var(--spacing-measures-spacing-008)] lg:px-[var(--spacing-measures-spacing-64,64px)] lg:py-[var(--spacing-measures-spacing-016,16px)] sm:gap-0"
|
||||
role="navigation"
|
||||
aria-label={t("ariaLabels.mainNavigation")}
|
||||
>
|
||||
@@ -151,46 +176,44 @@ function TopNavView({
|
||||
</div>
|
||||
|
||||
{/* Navigation Links - Consistent center positioning */}
|
||||
<div className="flex items-center">
|
||||
{/* XSmall breakpoint - Navigation items moved to right section */}
|
||||
<div className="flex items-center flex-1 justify-end sm:flex-none sm:justify-center">
|
||||
{/* XSmall breakpoint - Navigation items in Actions section (flex-1, justify-end) */}
|
||||
<div className="block sm:hidden" data-testid="nav-xs">
|
||||
{/* Empty for XSmall - navigation moved to right */}
|
||||
</div>
|
||||
|
||||
{/* Small breakpoint - All items grouped together, centered */}
|
||||
<div className="hidden sm:block md:hidden" data-testid="nav-sm">
|
||||
<MenuBar size="default">
|
||||
<MenuBar size="X Small">
|
||||
{renderNavigationItems("xsmall")}
|
||||
{logIn && renderLoginButton("xsmall")}
|
||||
</MenuBar>
|
||||
</div>
|
||||
|
||||
{/* 430-639px (sm: breakpoint): MenuBar X Small */}
|
||||
<div className="hidden sm:block md:hidden" data-testid="nav-sm">
|
||||
<MenuBar size="X Small">
|
||||
{renderNavigationItems("xsmall")}
|
||||
{logIn && renderLoginButton("xsmall")}
|
||||
</MenuBar>
|
||||
</div>
|
||||
|
||||
{/* 640-1023px (md: breakpoint): MenuBar X Small (different from folderTop=true) */}
|
||||
<div className="hidden md:block lg:hidden" data-testid="nav-md">
|
||||
<MenuBar size="default">
|
||||
<MenuBar size="X Small">
|
||||
{renderNavigationItems("xsmall")}
|
||||
</MenuBar>
|
||||
</div>
|
||||
|
||||
<div className="hidden lg:block xl:hidden" data-testid="nav-lg">
|
||||
<MenuBar size="large">{renderNavigationItems("large")}</MenuBar>
|
||||
<MenuBar size="Large">{renderNavigationItems("large")}</MenuBar>
|
||||
</div>
|
||||
|
||||
<div className="hidden xl:block" data-testid="nav-xl">
|
||||
<MenuBar size="large">{renderNavigationItems("xlarge")}</MenuBar>
|
||||
<MenuBar size="X Large">{renderNavigationItems("xlarge")}</MenuBar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Authentication Elements - Consistent right alignment across all breakpoints */}
|
||||
<div className="flex items-center">
|
||||
{/* XSmall breakpoint - All navigation items + Create Rule button */}
|
||||
<div className="block sm:hidden" data-testid="auth-xs">
|
||||
<div className="flex items-center gap-[var(--spacing-scale-001)]">
|
||||
<MenuBar size="default">
|
||||
{renderNavigationItems("xsmall")}
|
||||
{logIn && renderLoginButton("xsmall")}
|
||||
</MenuBar>
|
||||
{renderCreateRuleButton("xsmall", "small", "small")}
|
||||
</div>
|
||||
<div className="flex items-center shrink-0">
|
||||
{/* XSmall breakpoint - Only Create Rule button */}
|
||||
<div className="block sm:hidden shrink-0" data-testid="auth-xs">
|
||||
{renderCreateRuleButton("xsmall", "small", "small")}
|
||||
</div>
|
||||
|
||||
{/* Small breakpoint - Only Create Rule button */}
|
||||
@@ -203,7 +226,7 @@ function TopNavView({
|
||||
{/* Medium breakpoint */}
|
||||
<div className="hidden md:block lg:hidden" data-testid="auth-md">
|
||||
<div className="flex items-center gap-[var(--spacing-measures-spacing-010)]">
|
||||
<MenuBar size="default">
|
||||
<MenuBar size="Small">
|
||||
{logIn && renderLoginButton("xsmall")}
|
||||
</MenuBar>
|
||||
{renderCreateRuleButton("xsmall", "medium", "medium")}
|
||||
@@ -213,7 +236,7 @@ function TopNavView({
|
||||
{/* Large breakpoint */}
|
||||
<div className="hidden lg:block xl:hidden" data-testid="auth-lg">
|
||||
<div className="flex items-center gap-[var(--spacing-measures-spacing-004)]">
|
||||
<MenuBar size="large">
|
||||
<MenuBar size="Large">
|
||||
{logIn && renderLoginButton("large")}
|
||||
</MenuBar>
|
||||
{renderCreateRuleButton("large", "xlarge", "xlarge")}
|
||||
@@ -223,7 +246,7 @@ function TopNavView({
|
||||
{/* XLarge breakpoint */}
|
||||
<div className="hidden xl:block" data-testid="auth-xl">
|
||||
<div className="flex items-center gap-[var(--spacing-measures-spacing-004)]">
|
||||
<MenuBar size="large">
|
||||
<MenuBar size="X Large">
|
||||
{logIn && renderLoginButton("xlarge")}
|
||||
</MenuBar>
|
||||
{renderCreateRuleButton("xlarge", "xlarge", "xlarge")}
|
||||
|
||||
+65
-23
@@ -238,33 +238,23 @@ export type SizeValue =
|
||||
| "XLarge";
|
||||
|
||||
/**
|
||||
* Normalize menu bar size prop values
|
||||
* 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: "default" = "default"
|
||||
): "xsmall" | "default" | "medium" | "large" {
|
||||
defaultValue: "X Small" | "Small" | "Medium" | "Large" | "X Large" = "X Small"
|
||||
): "X Small" | "Small" | "Medium" | "Large" | "X Large" {
|
||||
if (!value) return defaultValue;
|
||||
const normalized = value.toLowerCase();
|
||||
const sizes = ["xsmall", "default", "medium", "large"];
|
||||
if (sizes.includes(normalized)) {
|
||||
return normalized as typeof defaultValue;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize menu bar item variant prop values
|
||||
*/
|
||||
export function normalizeMenuBarItemVariant(
|
||||
value: string | undefined,
|
||||
defaultValue: "default" = "default"
|
||||
): "default" | "home" {
|
||||
if (!value) return defaultValue;
|
||||
const normalized = value.toLowerCase();
|
||||
const variants = ["default", "home"];
|
||||
if (variants.includes(normalized)) {
|
||||
return normalized as typeof defaultValue;
|
||||
if (
|
||||
value === "X Small" ||
|
||||
value === "Small" ||
|
||||
value === "Medium" ||
|
||||
value === "Large" ||
|
||||
value === "X Large"
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
@@ -650,3 +640,55 @@ export function normalizeInputLabelPalette(
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import HeaderTab from "../../app/components/navigation/HeaderTab";
|
||||
import Logo from "../../app/components/icons/Logo";
|
||||
|
||||
export default {
|
||||
title: "Components/Navigation/HeaderTab",
|
||||
component: HeaderTab,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"A header tab container with decorative Union images and responsive behavior. Used to wrap content in the header with consistent styling and responsive breakpoint transitions.",
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
stretch: {
|
||||
control: { type: "boolean" },
|
||||
description: "Whether the tab should stretch to fill available space",
|
||||
},
|
||||
className: {
|
||||
control: { type: "text" },
|
||||
description: "Additional CSS classes",
|
||||
},
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
};
|
||||
|
||||
export const Default = {
|
||||
args: {
|
||||
stretch: false,
|
||||
},
|
||||
render: (args) => (
|
||||
<HeaderTab {...args}>
|
||||
<Logo size="homeHeaderMd" />
|
||||
</HeaderTab>
|
||||
),
|
||||
};
|
||||
@@ -16,7 +16,7 @@ export default {
|
||||
argTypes: {
|
||||
size: {
|
||||
control: { type: "select" },
|
||||
options: ["xsmall", "default", "medium", "large"],
|
||||
options: ["X Small", "Small", "Medium", "Large", "X Large"],
|
||||
description: "The size of the menu bar and its children",
|
||||
},
|
||||
className: {
|
||||
@@ -29,13 +29,13 @@ export default {
|
||||
|
||||
export const Default = {
|
||||
args: {
|
||||
size: "default",
|
||||
size: "Small",
|
||||
},
|
||||
render: (args) => (
|
||||
<MenuBar {...args}>
|
||||
<MenuBarItem size="large">Home</MenuBarItem>
|
||||
<MenuBarItem size="large">About</MenuBarItem>
|
||||
<MenuBarItem size="large">Contact</MenuBarItem>
|
||||
<MenuBarItem size="Large">Home</MenuBarItem>
|
||||
<MenuBarItem size="Large">About</MenuBarItem>
|
||||
<MenuBarItem size="Large">Contact</MenuBarItem>
|
||||
</MenuBar>
|
||||
),
|
||||
};
|
||||
@@ -45,38 +45,47 @@ export const Sizes = {
|
||||
render: () => (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-3">XSmall Size</h3>
|
||||
<MenuBar size="xsmall">
|
||||
<MenuBarItem size="xsmall">Home</MenuBarItem>
|
||||
<MenuBarItem size="xsmall">About</MenuBarItem>
|
||||
<MenuBarItem size="xsmall">Contact</MenuBarItem>
|
||||
<h3 className="text-white font-semibold mb-3">X Small Size</h3>
|
||||
<MenuBar size="X Small">
|
||||
<MenuBarItem size="X Small">Home</MenuBarItem>
|
||||
<MenuBarItem size="X Small">About</MenuBarItem>
|
||||
<MenuBarItem size="X Small">Contact</MenuBarItem>
|
||||
</MenuBar>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-3">Default Size</h3>
|
||||
<MenuBar size="default">
|
||||
<MenuBarItem size="large">Home</MenuBarItem>
|
||||
<MenuBarItem size="large">About</MenuBarItem>
|
||||
<MenuBarItem size="large">Contact</MenuBarItem>
|
||||
<h3 className="text-white font-semibold mb-3">Small Size</h3>
|
||||
<MenuBar size="Small">
|
||||
<MenuBarItem size="Large">Home</MenuBarItem>
|
||||
<MenuBarItem size="Large">About</MenuBarItem>
|
||||
<MenuBarItem size="Large">Contact</MenuBarItem>
|
||||
</MenuBar>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-3">Medium Size</h3>
|
||||
<MenuBar size="medium">
|
||||
<MenuBarItem size="large">Home</MenuBarItem>
|
||||
<MenuBarItem size="large">About</MenuBarItem>
|
||||
<MenuBarItem size="large">Contact</MenuBarItem>
|
||||
<MenuBar size="Medium">
|
||||
<MenuBarItem size="Large">Home</MenuBarItem>
|
||||
<MenuBarItem size="Large">About</MenuBarItem>
|
||||
<MenuBarItem size="Large">Contact</MenuBarItem>
|
||||
</MenuBar>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-3">Large Size</h3>
|
||||
<MenuBar size="large">
|
||||
<MenuBarItem size="large">Home</MenuBarItem>
|
||||
<MenuBarItem size="large">About</MenuBarItem>
|
||||
<MenuBarItem size="large">Contact</MenuBarItem>
|
||||
<MenuBar size="Large">
|
||||
<MenuBarItem size="Large">Home</MenuBarItem>
|
||||
<MenuBarItem size="Large">About</MenuBarItem>
|
||||
<MenuBarItem size="Large">Contact</MenuBarItem>
|
||||
</MenuBar>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-3">X Large Size</h3>
|
||||
<MenuBar size="X Large">
|
||||
<MenuBarItem size="X Large">Home</MenuBarItem>
|
||||
<MenuBarItem size="X Large">About</MenuBarItem>
|
||||
<MenuBarItem size="X Large">Contact</MenuBarItem>
|
||||
</MenuBar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,23 +13,14 @@ export default {
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
variant: {
|
||||
mode: {
|
||||
control: { type: "select" },
|
||||
options: ["default", "home"],
|
||||
description: "The visual style variant of the menu item",
|
||||
options: ["default", "inverse"],
|
||||
description: "The visual style mode of the menu item",
|
||||
},
|
||||
size: {
|
||||
control: { type: "select" },
|
||||
options: [
|
||||
"xsmall",
|
||||
"xsmallUseCases",
|
||||
"homeMd",
|
||||
"homeUseCases",
|
||||
"large",
|
||||
"largeUseCases",
|
||||
"homeXlarge",
|
||||
"xlarge",
|
||||
],
|
||||
options: ["X Small", "Small", "Medium", "Large", "X Large"],
|
||||
description: "The size of the menu item",
|
||||
},
|
||||
disabled: {
|
||||
@@ -48,23 +39,23 @@ export default {
|
||||
export const Default = {
|
||||
args: {
|
||||
children: "Menu Item",
|
||||
size: "large",
|
||||
size: "Large",
|
||||
},
|
||||
};
|
||||
|
||||
export const Variants = {
|
||||
export const Modes = {
|
||||
args: {
|
||||
children: "Menu Item",
|
||||
size: "large",
|
||||
size: "Large",
|
||||
},
|
||||
render: (args) => (
|
||||
<div className="space-y-4">
|
||||
<div className="space-x-4">
|
||||
<MenuBarItem {...args} variant="default">
|
||||
<MenuBarItem {...args} mode="default">
|
||||
Default
|
||||
</MenuBarItem>
|
||||
<MenuBarItem {...args} variant="home">
|
||||
Home
|
||||
<MenuBarItem {...args} mode="inverse">
|
||||
Inverse
|
||||
</MenuBarItem>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,7 +63,7 @@ export const Variants = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Different visual variants of the menu item component.",
|
||||
story: "Different visual modes of the menu item component.",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -81,19 +72,25 @@ export const Variants = {
|
||||
export const Sizes = {
|
||||
args: {
|
||||
children: "Menu Item",
|
||||
variant: "default",
|
||||
mode: "default",
|
||||
},
|
||||
render: (args) => (
|
||||
<div className="space-y-4">
|
||||
<div className="space-x-4">
|
||||
<MenuBarItem {...args} size="xsmall">
|
||||
XSmall
|
||||
<MenuBarItem {...args} size="X Small">
|
||||
X Small
|
||||
</MenuBarItem>
|
||||
<MenuBarItem {...args} size="large">
|
||||
<MenuBarItem {...args} size="Small">
|
||||
Small
|
||||
</MenuBarItem>
|
||||
<MenuBarItem {...args} size="Medium">
|
||||
Medium
|
||||
</MenuBarItem>
|
||||
<MenuBarItem {...args} size="Large">
|
||||
Large
|
||||
</MenuBarItem>
|
||||
<MenuBarItem {...args} size="xlarge">
|
||||
XLarge
|
||||
<MenuBarItem {...args} size="X Large">
|
||||
X Large
|
||||
</MenuBarItem>
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,8 +107,8 @@ export const Sizes = {
|
||||
export const States = {
|
||||
args: {
|
||||
children: "Menu Item",
|
||||
size: "large",
|
||||
variant: "default",
|
||||
size: "Large",
|
||||
mode: "default",
|
||||
},
|
||||
render: (args) => (
|
||||
<div className="space-y-4">
|
||||
@@ -132,30 +129,30 @@ export const States = {
|
||||
},
|
||||
};
|
||||
|
||||
export const AllVariants = {
|
||||
export const AllModes = {
|
||||
args: {},
|
||||
render: () => (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-3">Default Variant</h3>
|
||||
<h3 className="text-white font-semibold mb-3">Default Mode</h3>
|
||||
<div className="space-x-4">
|
||||
<MenuBarItem size="xsmall">XSmall</MenuBarItem>
|
||||
<MenuBarItem size="large">Large</MenuBarItem>
|
||||
<MenuBarItem size="xlarge">XLarge</MenuBarItem>
|
||||
<MenuBarItem size="X Small" mode="default">X Small</MenuBarItem>
|
||||
<MenuBarItem size="Large" mode="default">Large</MenuBarItem>
|
||||
<MenuBarItem size="X Large" mode="default">X Large</MenuBarItem>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-3">Home Variant</h3>
|
||||
<h3 className="text-white font-semibold mb-3">Inverse Mode</h3>
|
||||
<div className="space-x-4">
|
||||
<MenuBarItem variant="home" size="xsmall">
|
||||
XSmall
|
||||
<MenuBarItem mode="inverse" size="X Small">
|
||||
X Small
|
||||
</MenuBarItem>
|
||||
<MenuBarItem variant="home" size="large">
|
||||
<MenuBarItem mode="inverse" size="Large">
|
||||
Large
|
||||
</MenuBarItem>
|
||||
<MenuBarItem variant="home" size="xlarge">
|
||||
XLarge
|
||||
<MenuBarItem mode="inverse" size="X Large">
|
||||
X Large
|
||||
</MenuBarItem>
|
||||
</div>
|
||||
</div>
|
||||
@@ -163,11 +160,11 @@ export const AllVariants = {
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-3">Disabled States</h3>
|
||||
<div className="space-x-4">
|
||||
<MenuBarItem size="large" disabled>
|
||||
<MenuBarItem size="Large" mode="default" disabled>
|
||||
Default Disabled
|
||||
</MenuBarItem>
|
||||
<MenuBarItem variant="home" size="large" disabled>
|
||||
Home Disabled
|
||||
<MenuBarItem mode="inverse" size="Large" disabled>
|
||||
Inverse Disabled
|
||||
</MenuBarItem>
|
||||
</div>
|
||||
</div>
|
||||
@@ -177,7 +174,7 @@ export const AllVariants = {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Complete overview of all menu item variants, sizes, and states.",
|
||||
"Complete overview of all menu item modes, sizes, and states.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -8,14 +8,14 @@ export default {
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"Unified navigation component that supports two variants: folderTop (home page style with yellow HeaderTab) and standard (dark sticky header). Supports all props from Figma design: size, loggedIn, folderTop, profile, and logIn.",
|
||||
"Unified navigation component that supports two variants: folderTop (home page style with yellow tab container) and standard (dark sticky header). Supports all props from Figma design: size, loggedIn, folderTop, profile, and logIn.",
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
folderTop: {
|
||||
control: "boolean",
|
||||
description: "When true, renders the home page variant with HeaderTab wrapper. When false, renders the standard header variant.",
|
||||
description: "When true, renders the home page variant with yellow tab container. When false, renders the standard header variant.",
|
||||
},
|
||||
loggedIn: {
|
||||
control: "boolean",
|
||||
@@ -63,7 +63,7 @@ export const HomePage = {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Home page variant (folderTop=true) with transparent background and yellow HeaderTab wrapper. Use the Viewport toolbar to see responsive behavior.",
|
||||
"Home page variant (folderTop=true) with transparent background and yellow tab container. Use the Viewport toolbar to see responsive behavior.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user