Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
joshwooding committed Jul 31, 2024
1 parent 8a7522a commit c383430
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 206 deletions.
1 change: 0 additions & 1 deletion .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ export const argTypes: ArgTypes = {
};

export const parameters: Parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
layout: "centered",
// Show props description in Controls panel
controls: { expanded: true, sort: "requiredFirst" },
Expand Down
17 changes: 12 additions & 5 deletions packages/lab/src/tabs-next/TabNext.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* Class applied to root Tab element */
.saltTabNext {
display: inline-flex;
align-items: center;
justify-content: center;
appearance: none;
-webkit-appearance: none;
display: inline-flex;
background: var(--salt-navigable-primary-background);
gap: var(--salt-spacing-100);
border: none;
Expand All @@ -29,6 +29,14 @@
font-size: var(--salt-text-fontSize);
}

.saltTabNext-label {
display: flex;
gap: var(--salt-spacing-100);
align-items: center;
justify-content: center;
flex: 1;
}

.saltTabNext::after {
content: "";
position: absolute;
Expand All @@ -50,8 +58,8 @@
background: var(--salt-navigable-indicator-hover);
}

.saltTabNext:disabled:hover::after,
.saltTabNext:disabled:focus-visible::after {
.saltTabNext[aria-disabled="true"]:hover::after,
.saltTabNext[aria-disabled="true"]:focus-visible::after {
background: none;
}

Expand All @@ -69,8 +77,7 @@
background: var(--salt-navigable-indicator-active);
}

.saltTabNext:disabled {
.saltTabNext[aria-disabled="true"] {
cursor: var(--salt-navigable-cursor-disabled);
color: var(--salt-content-primary-foreground-disabled);
}

155 changes: 94 additions & 61 deletions packages/lab/src/tabs-next/TabNext.tsx
Original file line number Diff line number Diff line change
@@ -1,81 +1,114 @@
import { makePrefixer, useForkRef, useId } from "@salt-ds/core";
import { Button, makePrefixer, useForkRef } from "@salt-ds/core";
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";
import clsx from "clsx";
import {
type ComponentPropsWithoutRef,
type MouseEvent,
type ReactElement,
forwardRef,
ReactElement,
ComponentPropsWithoutRef,
useEffect,
useId,
useRef,
useState,
} from "react";
import clsx from "clsx";

import { useTabs } from "./TabNextContext";
import { CloseIcon } from "@salt-ds/icons";
import tabCss from "./TabNext.css";
import { useTabs } from "./TabNextContext";

const withBaseName = makePrefixer("saltTabNext");

export interface TabNextProps extends ComponentPropsWithoutRef<"button"> {
export interface TabNextProps extends ComponentPropsWithoutRef<"div"> {
/* Value prop is mandatory and must be unique in order for overflow to work. */
disabled?: boolean;
value: string;
onClose?: () => void;
}

export const TabNext = forwardRef<HTMLButtonElement, TabNextProps>(function Tab(
props,
ref
): ReactElement<TabNextProps> {
const {
children,
className,
disabled: disabledProp,
onClick,
onFocus,
value,
...rest
} = props;
const targetWindow = useWindow();
useComponentCssInjection({
testId: "salt-tab-next",
css: tabCss,
window: targetWindow,
});
const {
disabled: tabstripDisabled,
registerItem,
variant,
setSelected,
selected,
active,
} = useTabs();
const disabled = tabstripDisabled || disabledProp;
export const TabNext = forwardRef<HTMLDivElement, TabNextProps>(
function Tab(props, ref): ReactElement<TabNextProps> {
const {
children,
className,
disabled: disabledProp,
onClick,
onClose,
onFocus,
value,
...rest
} = props;
const targetWindow = useWindow();
useComponentCssInjection({
testId: "salt-tab-next",
css: tabCss,
window: targetWindow,
});
const { registerItem, variant, setSelected, selected, focusInside } =
useTabs();
const disabled = disabledProp;

const tabRef = useRef<HTMLDivElement>(null);
const handleRef = useForkRef(ref, tabRef);

const handleClick = (event: MouseEvent<HTMLDivElement>) => {
onClick?.(event);
setSelected(value);
};

useEffect(() => {
return registerItem({ id: value, element: tabRef.current });

Check failure on line 60 in packages/lab/src/tabs-next/TabNext.tsx

View workflow job for this annotation

GitHub Actions / type-checks

Type 'HTMLDivElement | null' is not assignable to type 'HTMLElement'.
}, [value]);

const closeButtonId = useId();
const labelId = useId();

const [focused, setFocused] = useState(false);

const tabRef = useRef<HTMLButtonElement>(null);
const handleRef = useForkRef(ref, tabRef);
const handleFocus = () => {
setFocused(true);
};

const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
onClick?.(event);
setSelected(value);
};
const handleBlur = () => {
setFocused(false);
};

useEffect(() => {
return registerItem({ id: value, element: tabRef.current });
}, [value]);
const handleClose = (event: MouseEvent<HTMLButtonElement>) => {
onClose?.();
event.stopPropagation();
};

return (
<button
className={clsx(withBaseName(), withBaseName(variant), className)}
data-value={value}
aria-selected={selected === value || undefined}
disabled={disabled}
tabIndex={active === value ? 0 : -1}
value={value}
ref={handleRef}
role="tab"
type="button"
onClick={handleClick}
{...rest}
>
{children}
</button>
);
});
return (
<div
className={clsx(withBaseName(), withBaseName(variant), className)}
data-value={value}
aria-selected={selected === value || undefined}
aria-disabled={disabled}
tabIndex={!disabled ? (selected === value ? 0 : -1) : undefined}
ref={handleRef}
role="tab"
onClick={!disabled ? handleClick : undefined}
onFocus={handleFocus}
onBlur={handleBlur}
{...rest}
>
<span className={withBaseName("label")} id={labelId}>
{children}
</span>
{onClose ? (
<Button
aria-label="Dismiss"
id={closeButtonId}
aria-labelledby={clsx(closeButtonId, labelId)}
aria-hidden={!focused}
tabIndex={focused || (!focusInside && selected === value) ? 0 : -1}
variant="secondary"
onClick={handleClose}
>
<CloseIcon aria-hidden />
</Button>
) : null}
</div>
);
},
);
28 changes: 10 additions & 18 deletions packages/lab/src/tabs-next/TabNextContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,24 @@ import { createContext } from "@salt-ds/core";
import { type ReactNode, type SyntheticEvent, useContext } from "react";

interface TabValue {
value: string;
label: ReactNode;
id: string;
element: HTMLElement;
}

export interface TabsContextValue {
activeColor: "primary" | "secondary";
disabled?: boolean;
activate: (event: SyntheticEvent<HTMLButtonElement>) => void;
isActive: (id: string) => boolean;
setFocusable: (id: string) => void;
isFocusable: (id: string) => boolean;
registerTab: (tab: TabValue) => void;
unregisterTab: (id: string) => void;
registerItem: (tab: TabValue) => void;
variant: "main" | "inline";
setSelected: (id: string) => void;
selected?: string;
focusInside: boolean;
}

export const TabsContext = createContext<TabsContextValue>("TabsContext", {
activeColor: "primary",
disabled: false,
activate: () => undefined,
isActive: () => false,
setFocusable: () => undefined,
isFocusable: () => false,
registerTab: () => undefined,
unregisterTab: () => undefined,
registerItem: () => undefined,
variant: "main",
setSelected: () => undefined,
selected: undefined,
focusInside: false,
});

export function useTabs() {
Expand Down
4 changes: 0 additions & 4 deletions packages/lab/src/tabs-next/TabOverflowList.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

.saltTabOverflow {
position: relative;
}
Expand Down Expand Up @@ -27,7 +26,6 @@
pointer-events: none;
}


.saltTabOverflow-list [role="tab"] {
color: var(--salt-content-primary-foreground);
background: var(--salt-selectable-background);
Expand All @@ -50,13 +48,11 @@
display: none;
}


.saltTabOverflow-list [role="tab"][aria-disabled="true"] {
color: var(--salt-content-primary-foreground-disabled);
cursor: var(--salt-selectable-cursor-disabled);
}


.saltTabOverflow-list [role="tab"]:focus-visible {
outline: var(--salt-focused-outline);
outline-offset: calc(var(--salt-size-border) * -2);
Expand Down
22 changes: 13 additions & 9 deletions packages/lab/src/tabs-next/TabOverflowList.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { Button, makePrefixer } from "@salt-ds/core";
import { OverflowMenuIcon } from "@salt-ds/icons";
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";
import {
forwardRef,
Children,
ReactNode,
type ComponentPropsWithoutRef,
type ReactNode,
useState,
ComponentPropsWithoutRef,
} from "react";
import { Button, makePrefixer } from "@salt-ds/core";
import { OverflowMenuIcon } from "@salt-ds/icons";
import { clsx } from "clsx";
import tabOverflowListCss from "./TabOverflowList.css";
import { useWindow } from "@salt-ds/window";
import { useComponentCssInjection } from "@salt-ds/styles";

interface TabOverflowListProps extends ComponentPropsWithoutRef<"button"> {
children?: ReactNode;
Expand All @@ -24,7 +22,7 @@ export function TabOverflowList(props: TabOverflowListProps) {

const targetWindow = useWindow();
useComponentCssInjection({
testId: "salt-tabstrip-nex-overflow",
testId: "salt-tabstrip-next-overflow",
css: tabOverflowListCss,
window: targetWindow,
});
Expand All @@ -41,18 +39,24 @@ export function TabOverflowList(props: TabOverflowListProps) {
setHidden(true);
};

const handleListClick = () => {
setHidden(true);
};

if (Children.count(children) === 0) return null;

return (
<div className={withBaseName()}>
<Button tabIndex={-1} variant="secondary" onClick={handleClick} {...rest}>
<OverflowMenuIcon aria-hidden />
</Button>
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<div
className={withBaseName("list")}
data-hidden={hidden}
onFocus={handleFocus}
onBlur={handleBlur}
onClick={handleListClick}
>
<div className={withBaseName("listContainer")}>{children}</div>
</div>
Expand Down
18 changes: 2 additions & 16 deletions packages/lab/src/tabs-next/TabstripNext.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,14 @@
.saltTabstripNext {
display: flex;
flex-wrap: nowrap;
justify-content: var(--tabstripNext-justifyContent);
justify-content: var(--tabstripNext-justifyContent, flex-start);
align-items: center;
position: relative;
background: transparent;
width: 100%;
font-family: var(--salt-text-fontFamily);
font-size: var(--salt-text-fontSize);
font-weight: var(--salt-text-fontWeight);
line-height: var(--salt-text-lineHeight);
min-height: calc(var(--salt-size-base) + var(--salt-spacing-100));
gap: var(--salt-spacing-100);
}
.saltTabstripNext-container {
display: block;
width: 100%;
height: fit-content;
position: relative;
}

.saltTabstripNext-main {
padding-left: var(--salt-spacing-300);
Expand All @@ -34,11 +25,6 @@
border-bottom: var(--salt-size-border) var(--salt-separable-borderStyle) var(--salt-separable-secondary-borderColor);
}

.saltTabstripNext .saltTabNext-wrapper:not(:last-child) {
padding-right: var(--salt-spacing-100);
box-sizing: border-box;
}

.saltTabstripNext-activeColorPrimary {
--saltTabstripNext-activeColor: var(--salt-container-primary-background);
}
Expand Down
Loading

0 comments on commit c383430

Please sign in to comment.