Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SUR-256 Update tabs component as per new mockup #46

Merged
merged 13 commits into from
Sep 3, 2024
2 changes: 1 addition & 1 deletion dist/force-ui.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react', 'react-dom'), 'version' => 'c1888d6239fede688622');
<?php return array('dependencies' => array('react', 'react-dom'), 'version' => '4cd740b67cc191bc7ef3');
2 changes: 1 addition & 1 deletion dist/force-ui.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/components/tabs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ The `Tabs` component is a flexible and customizable component built with Tailwin
- **Type:** `string`
- **Default:** `"sm"`
- **Description:** Defines the size of the tabs. Options include:
- `"xs"`
- `"sm"`
- `"md"`
- `"lg"`
Expand Down
59 changes: 37 additions & 22 deletions src/components/tabs/tabs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import React, {
createContext,
useContext,
} from 'react';
import { twMerge } from 'tailwind-merge';
import { cn } from '@/utilities/functions';
import { motion } from 'framer-motion';

// Context for managing the TabsGroup state.
const TabsGroupContext = createContext();
Expand All @@ -20,7 +21,7 @@ const TabsGroup = ( props ) => {
activeItem = null, // The currently active item in the group.
onChange, // Callback when the active item changes.
className, // Additional class names for styling.
size = 'sm', // Size of the tabs in the group ('sm', 'md', 'lg').
size = 'sm', // Size of the tabs in the group ('xs', 'sm', 'md', 'lg').
orientation = 'horizontal', // Orientation of the tabs ('horizontal', 'vertical').
variant = 'pill', // Style variant of the tabs ('pill', 'rounded', 'underline').
iconPosition = 'left', // Position of the icon in the tab ('left' or 'right').
Expand All @@ -40,16 +41,32 @@ const TabsGroup = ( props ) => {
// Determine styles based on the variant and orientation.
let borderRadius = 'rounded-full',
padding = 'p-1',
gap = orientation === 'vertical' ? 'gap-0.5' : 'gap-1',
gap,
border = 'border border-tab-border border-solid';

if ( orientation === 'vertical' ) {
gap = 'gap-0.5';
} else if ( variant === 'rounded' || variant === 'pill' ) {
if ( size === 'xs' || size === 'sm' ) {
gap = 'gap-0.5';
} else if ( size === 'md' || size === 'lg' ) {
gap = 'gap-1';
}
}

if ( variant === 'rounded' || orientation === 'vertical' ) {
borderRadius = 'rounded-md';
} else if ( variant === 'underline' ) {
borderRadius = 'rounded-none';
padding = 'p-0';
gap = 'gap-0';
border = 'border-none';
border = 'border-t-0 border-r-0 border-l-0 border-b border-solid border-tab-border';
if ( size === 'xs' ) {
gap = 'gap-0';
} else if ( size === 'sm' ) {
gap = 'gap-2.5';
} else if ( size === 'md' || size === 'lg' ) {
gap = 'gap-3';
}
}

// Determine width classes.
Expand All @@ -61,7 +78,7 @@ const TabsGroup = ( props ) => {
const baseClasses = `box-border [&>*]:box-border flex items-center ${ widthClasses } ${ orientationClasses }`;

// Merge classes.
const groupClassName = twMerge(
const groupClassName = cn(
baseClasses,
borderRadius,
padding,
Expand Down Expand Up @@ -123,9 +140,10 @@ const Tab = ( props, ref ) => {

// Determine size classes.
const sizes = {
sm: 'p-1 text-sm [&>svg]:h-4 [&>svg]:w-4',
md: 'p-2 text-base [&>svg]:h-5 [&>svg]:w-5',
lg: 'p-2.5 text-lg [&>svg]:h-6 [&>svg]:w-6',
xs: 'px-1.5 py-0.5 text-xs [&>svg]:size-3',
sm: variant === 'underline' ? 'py-1.5 text-sm [&>svg]:size-4' : 'px-3 py-1.5 text-sm [&>svg]:size-4',
md: variant === 'underline' ? 'py-2 text-base [&>svg]:size-5' : 'px-3.5 py-1.5 text-base [&>svg]:size-5',
lg: variant === 'underline' ? 'p-2.5 text-lg [&>svg]:size-6' : 'px-3.5 py-1.5 text-lg [&>svg]:size-6',
}[ size ];

// Determine width and orientation classes for tabs.
Expand All @@ -134,22 +152,17 @@ const Tab = ( props, ref ) => {
orientation === 'vertical' ? 'w-full justify-between' : '';

// Base classes for the Tab.
const baseClasses = `bg-transparent text-text-tertiary cursor-pointer flex items-center justify-center transition-colors duration-200 ${ fullWidth } ${ orientationClasses }`;
const baseClasses = cn( 'relative border-none bg-transparent text-text-secondary cursor-pointer flex items-center justify-center transition-colors duration-200', fullWidth, orientationClasses );

const borderClasses = 'border-none';

let borderBottomClasses = '';
let variantClasses = 'rounded-full';
if ( variant === 'rounded' ) {
variantClasses = 'rounded-md';
} else if ( variant === 'underline' ) {
variantClasses = 'rounded-none';
borderBottomClasses =
'border-t-0 border-r-0 border-l-0 border-b border-solid border-tab-border';
variantClasses = 'rounded-none w-fit max-w-fit';
}

const borderActiveInlineClasses = 'border-border-interactive';

// Additional classes.
const hoverClasses = '';
const focusClasses = 'focus:outline-none';
Expand All @@ -159,14 +172,10 @@ const Tab = ( props, ref ) => {
const activeClasses = activeItem === slug ? 'bg-background-primary text-text-primary' : '';

// Merge classes.
const tabClassName = twMerge(
const tabClassName = cn(
baseClasses,
borderClasses,
variantClasses,
borderBottomClasses,
activeItem === slug && variant === 'underline'
? borderActiveInlineClasses
: '',
hoverClasses,
focusClasses,
disabledClasses,
Expand All @@ -175,7 +184,7 @@ const Tab = ( props, ref ) => {
className
);

const iconParentClasses = twMerge( 'flex items-center gap-1' );
const iconParentClasses = 'flex items-center gap-1';

// Handle click event.
const handleClick = ( event ) => {
Expand All @@ -190,6 +199,12 @@ const Tab = ( props, ref ) => {
onClick={ handleClick }
{ ...rest }
>
{ activeItem === slug && variant === 'underline' && (
<motion.span
layoutId="underline"
className="absolute right-0 left-0 -bottom-px h-px bg-border-interactive"
/>
) }
<span className={ iconParentClasses }>
{ iconPosition === 'left' && icon && (
<span className="mr-1">{ icon }</span>
Expand Down
Loading