Skip to content

Commit 6a29600

Browse files
authored
Merge pull request #118 from buildo/3692938-implement_tabs_alternative_versions
2 parents 5502169 + 987ac8c commit 6a29600

File tree

12 files changed

+330
-92
lines changed

12 files changed

+330
-92
lines changed

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
},
77
"scripts": {
88
"build": "turbo run build",
9-
"start": "turbo run start",
9+
"start": "pnpm --filter bento-design-system start -- --onSuccess 'pnpm --filter storybook start'",
1010
"prepare": "husky install",
1111
"prettier-check": "turbo run prettier-check",
1212
"prettier-write": "turbo run prettier-write",

Diff for: packages/bento-design-system/src/Tabs/Config.ts

+37-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,40 @@
1+
import { IconProps } from "../Icons";
12
import { BentoSprinkles } from "../internal";
23
import { LabelProps } from "../Typography/Label/Label";
4+
import { TabsSize } from "./createTabs";
35

4-
export type TabsConfig = {
5-
radius: BentoSprinkles["borderRadius"];
6-
paddingX: BentoSprinkles["paddingX"];
7-
paddingY: BentoSprinkles["paddingY"];
8-
labelSize: LabelProps["size"];
9-
};
6+
type SizeConfig<T> = Record<TabsSize, T>;
7+
8+
type TabsWidthConfig =
9+
| {
10+
tabsWidth: "fit-content";
11+
tabsAlignment: "left" | "center" | "right";
12+
}
13+
| {
14+
tabsWidth: "fill-parent";
15+
tabsAlignment?: never;
16+
};
17+
18+
type TabsKindConfig =
19+
| {
20+
kind: "folder";
21+
radius: BentoSprinkles["borderRadius"];
22+
}
23+
| {
24+
kind: "underline";
25+
lineHeight: BentoSprinkles["borderBottomWidth"];
26+
lineColor: NonNullable<BentoSprinkles["borderColor"]>;
27+
};
28+
29+
export type TabsConfig = TabsWidthConfig &
30+
TabsKindConfig & {
31+
spaceBetweenTabs: BentoSprinkles["gap"];
32+
internalSpacing: BentoSprinkles["gap"];
33+
paddingX: SizeConfig<BentoSprinkles["paddingX"]>;
34+
paddingY: SizeConfig<BentoSprinkles["paddingY"]>;
35+
labelSize: SizeConfig<LabelProps["size"]>;
36+
uppercaseLabel: boolean;
37+
iconSize: IconProps["size"];
38+
notificationSize: number;
39+
notificationColor: BentoSprinkles["color"];
40+
};

Diff for: packages/bento-design-system/src/Tabs/Tabs.css.ts

+44-10
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,7 @@ export const tabRecipe = strictRecipe({
1010
}),
1111
variants: {
1212
active: {
13-
true: bentoSprinkles({
14-
background: "backgroundInteractiveOverlay",
15-
color: "textInteractive",
16-
fill: "textInteractive",
17-
}),
1813
false: bentoSprinkles({
19-
background: {
20-
default: "secondaryTransparentEnabledBackground",
21-
hover: "secondaryTransparentHoverBackground",
22-
focus: "secondaryTransparentFocusBackground",
23-
},
2414
color: {
2515
default: "secondaryTransparentEnabledForeground",
2616
hover: "secondaryTransparentHoverForeground",
@@ -34,5 +24,49 @@ export const tabRecipe = strictRecipe({
3424
cursor: "pointer",
3525
}),
3626
},
27+
kind: {
28+
folder: {},
29+
underline: {},
30+
},
3731
},
32+
compoundVariants: [
33+
{
34+
variants: {
35+
active: false,
36+
kind: "folder",
37+
},
38+
style: bentoSprinkles({
39+
background: {
40+
default: "secondaryTransparentEnabledBackground",
41+
hover: "secondaryTransparentHoverBackground",
42+
focus: "secondaryTransparentFocusBackground",
43+
},
44+
}),
45+
},
46+
{
47+
variants: {
48+
active: true,
49+
kind: "folder",
50+
},
51+
style: bentoSprinkles({
52+
background: {
53+
default: "backgroundInteractiveOverlay",
54+
hover: "backgroundInteractiveOverlay",
55+
focus: "backgroundInteractiveOverlay",
56+
},
57+
color: { default: "textInteractive", hover: "textInteractive", focus: "textInteractive" },
58+
fill: { default: "textInteractive", hover: "textInteractive", focus: "textInteractive" },
59+
}),
60+
},
61+
{
62+
variants: {
63+
active: true,
64+
kind: "underline",
65+
},
66+
style: bentoSprinkles({
67+
color: { default: "textPrimary", hover: "textPrimary", focus: "textPrimary" },
68+
fill: { default: "textPrimary", hover: "textPrimary", focus: "textPrimary" },
69+
}),
70+
},
71+
],
3872
});

Diff for: packages/bento-design-system/src/Tabs/createTabs.tsx

+54-27
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import { usePress } from "@react-aria/interactions";
22
import { Box, Columns, Column } from "../internal";
3-
import { Children, IconPlaceholder, IconProps, Label } from "..";
3+
import { Children, IconProps, Label } from "..";
44
import { LocalizedString } from "../util/LocalizedString";
55
import { tabRecipe } from "./Tabs.css";
66
import { TabsConfig } from "./Config";
7+
import { normalizeStatusValue } from "../internal/sprinkles.css";
8+
9+
export type TabsSize = "medium" | "large";
710

811
type Props<A> = {
12+
size: TabsSize;
913
value: A;
1014
onChange: (v: A) => void;
1115
tabs: Array<{
@@ -19,61 +23,84 @@ type Props<A> = {
1923

2024
export function createTabs(config: TabsConfig) {
2125
type TabProps = {
26+
size: TabsSize;
2227
label: LocalizedString;
2328
onPress: () => void;
2429
active: boolean;
2530
disabled?: boolean;
2631
icon?: (props: IconProps) => Children;
2732
hasNotification?: boolean;
2833
};
29-
function Tab({ active, onPress, label, disabled, icon, hasNotification }: TabProps) {
34+
function Tab({ size, active, onPress, label, disabled, icon, hasNotification }: TabProps) {
3035
const {
3136
pressProps: { color: ignored1, ...pressProps },
3237
} = usePress({ onPress, isDisabled: disabled });
3338

3439
return (
3540
<Box
3641
tabIndex={active || disabled ? -1 : 0}
37-
className={tabRecipe({ active })}
42+
className={tabRecipe({ active, kind: config.kind })}
3843
{...pressProps}
3944
disabled={disabled}
40-
borderTopRadius={config.radius}
41-
paddingX={config.paddingX}
42-
paddingY={config.paddingY}
45+
borderTopRadius={config.kind === "folder" ? config.radius : undefined}
46+
paddingX={config.paddingX[size]}
47+
paddingY={config.paddingY[size]}
48+
position="relative"
49+
borderColor={
50+
config.kind === "underline"
51+
? active
52+
? normalizeStatusValue(config.lineColor).active
53+
: config.lineColor
54+
: undefined
55+
}
56+
borderBottomWidth={config.kind === "underline" ? config.lineHeight : undefined}
57+
borderStyle="solid"
4358
>
44-
<Columns space={8} alignY="center">
45-
{icon && <Column width="content">{icon({ size: 16, color: "inherit" })}</Column>}
46-
<Label size={config.labelSize} uppercase>
59+
<Columns space={config.internalSpacing} alignY="center">
60+
{icon && (
61+
<Column width="content">{icon({ size: config.iconSize, color: "inherit" })}</Column>
62+
)}
63+
<Label size={config.labelSize[size]} uppercase={config.uppercaseLabel}>
4764
{label}
4865
</Label>
4966
{hasNotification && (
5067
<Column width="content">
51-
<IconPlaceholder size={8} color="inherit" />
68+
<Box
69+
borderRadius="circled"
70+
color={config.notificationColor}
71+
style={{
72+
width: config.notificationSize,
73+
height: config.notificationSize,
74+
background: "currentColor",
75+
}}
76+
></Box>
5277
</Column>
5378
)}
5479
</Columns>
5580
</Box>
5681
);
5782
}
5883

59-
return function Tabs<A>({ value, tabs, onChange }: Props<A>) {
84+
return function Tabs<A>({ size, value, tabs, onChange }: Props<A>) {
6085
return (
61-
<Box boxShadow="outlineInteractiveBottom">
62-
<Columns space={0}>
63-
{tabs.map((t) => (
64-
<Column key={t.label} width="content">
65-
<Tab
66-
label={t.label}
67-
onPress={() => onChange(t.value)}
68-
active={value === t.value}
69-
disabled={t.disabled}
70-
icon={t.icon}
71-
hasNotification={t.hasNotification}
72-
/>
73-
</Column>
74-
))}
75-
</Columns>
76-
</Box>
86+
<Columns
87+
space={config.spaceBetweenTabs}
88+
align={config.tabsWidth === "fit-content" ? config.tabsAlignment : undefined}
89+
>
90+
{tabs.map((t) => (
91+
<Column key={t.label} width={config.tabsWidth === "fit-content" ? "content" : undefined}>
92+
<Tab
93+
size={size}
94+
label={t.label}
95+
onPress={() => onChange(t.value)}
96+
active={value === t.value}
97+
disabled={t.disabled}
98+
icon={t.icon}
99+
hasNotification={t.hasNotification}
100+
/>
101+
</Column>
102+
))}
103+
</Columns>
77104
);
78105
};
79106
}

Diff for: packages/bento-design-system/src/internal/sprinkles.css.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,28 @@ import {
77
import { createDefineBentoSprinklesFn } from "../sprinkles";
88

99
const defineBentoSprinkles = createDefineBentoSprinklesFn();
10-
export const { sprinkles: bentoSprinkles, responsiveStyles } = defineBentoSprinkles();
10+
export const { sprinkles: bentoSprinkles, responsiveStyles, statusStyles } = defineBentoSprinkles();
1111

1212
export type BentoSprinkles = Parameters<typeof bentoSprinkles>[0];
1313

1414
export const mapResponsiveValue = createMapValueFn(responsiveStyles);
15-
1615
export const normalizeResponsiveValue = createNormalizeValueFn(responsiveStyles);
17-
1816
export type OptionalResponsiveValue<Value extends string | number> = ConditionalValue<
1917
typeof responsiveStyles,
2018
Value
2119
>;
22-
2320
export type RequiredResponsiveValue<Value extends string | number> = RequiredConditionalValue<
2421
typeof responsiveStyles,
2522
Value
2623
>;
24+
25+
export const mapStatusValue = createMapValueFn(statusStyles);
26+
export const normalizeStatusValue = createNormalizeValueFn(statusStyles);
27+
export type OptionalStatusValue<Value extends string | number> = ConditionalValue<
28+
typeof statusStyles,
29+
Value
30+
>;
31+
export type RequiredStatusValue<Value extends string | number> = RequiredConditionalValue<
32+
typeof statusStyles,
33+
Value
34+
>;

Diff for: packages/bento-design-system/src/util/atoms.ts

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ export const unconditionalProperties = {
2727
overflow: ["hidden", "visible", "auto"],
2828
overflowX: ["hidden", "visible", "auto"],
2929
overflowY: ["hidden", "visible", "auto"],
30+
borderBottomWidth: {
31+
1: "1px",
32+
2: "2px",
33+
},
34+
borderStyle: ["solid"],
3035
isolation: ["auto", "isolate"],
3136
} as const;
3237

@@ -111,4 +116,5 @@ export const statusProperties = {
111116
stroke: color,
112117
textDecoration: ["none", "underline"],
113118
fill: { ...color, ...background, inherit: "inherit" },
119+
borderColor: { ...color, transparent: "transparent" },
114120
} as const;

Diff for: packages/bento-design-system/src/util/defaultConfigs.tsx

+45-7
Original file line numberDiff line numberDiff line change
@@ -354,13 +354,6 @@ export const table: TableConfig = {
354354
emptyIllustration: IllustrationSearch,
355355
};
356356

357-
export const tabs: TabsConfig = {
358-
radius: 8,
359-
paddingX: 40,
360-
paddingY: 8,
361-
labelSize: "large",
362-
};
363-
364357
export const toast: ToastConfig = {
365358
paddingX: 16,
366359
paddingY: 16,
@@ -370,3 +363,48 @@ export const toast: ToastConfig = {
370363
closeIconSize: 12,
371364
smallButtonPaddingY: button.paddingY.small,
372365
};
366+
367+
const tabsBaseConfig = {
368+
tabsWidth: "fit-content",
369+
tabsAlignment: "left",
370+
spaceBetweenTabs: 0,
371+
internalSpacing: 8,
372+
paddingY: {
373+
medium: 8,
374+
large: 16,
375+
},
376+
labelSize: {
377+
medium: "medium",
378+
large: "large",
379+
},
380+
uppercaseLabel: false,
381+
iconSize: 16,
382+
notificationSize: 6,
383+
notificationColor: "foregroundInteractive",
384+
} as const;
385+
386+
export const folderTabs: TabsConfig = {
387+
...tabsBaseConfig,
388+
kind: "folder",
389+
radius: 4,
390+
paddingX: {
391+
medium: 40,
392+
large: 40,
393+
},
394+
};
395+
396+
export const underlineTabs: TabsConfig = {
397+
...tabsBaseConfig,
398+
kind: "underline",
399+
paddingX: {
400+
medium: 16,
401+
large: 32,
402+
},
403+
lineHeight: 2,
404+
lineColor: {
405+
default: "transparent",
406+
hover: "foregroundSecondaryInverse",
407+
focus: "foregroundSecondaryInverse",
408+
active: "brandPrimary",
409+
},
410+
};

0 commit comments

Comments
 (0)