Skip to content

Commit

Permalink
Add new features to Listbox component
Browse files Browse the repository at this point in the history
This commit introduces new functionalities to the Listbox component. Properties like 'size', 'theme', and 'bordered' have been added providing more customization options. Additionally, a 'toPx' function has been implemented to simplify CSS value conversions. This function is used for more precise size values for the Listbox items. Storybook stories have also been updated to accommodate these changes.
  • Loading branch information
riccardoperra committed Nov 25, 2023
1 parent 9eb3b17 commit aab30dc
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 18 deletions.
50 changes: 35 additions & 15 deletions packages/kit/src/components/Listbox/Listbox.css.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { createTheme, style } from "@vanilla-extract/css";
import { tokens } from "../../foundation/contract.css";
import { themeTokens } from "../../foundation";
import { themeTokens, themeVars } from "../../foundation";
import { componentStateStyles } from "@kobalte/vanilla-extract";
import { LISTBOX_ITEM_SIZE } from "./sizes";
import { toPx } from "../../utils/css";

export const [listTheme, listThemeVars] = createTheme({
contentBackground: tokens.dropdownBackground,
Expand All @@ -20,14 +22,6 @@ export const [listTheme, listThemeVars] = createTheme({
indicatorSize: "20px",
});

const sizesCss = {
xs: "30px",
sm: "36px",
md: "40px",
lg: "48px",
xl: "56px",
};

const ButtonSizes = {
xs: "xs",
sm: "sm",
Expand All @@ -36,7 +30,32 @@ const ButtonSizes = {
xl: "xl",
} as const;

export const list = style([listTheme]);
export const list = style([
listTheme,
{
borderRadius: themeTokens.radii.sm,
selectors: {
"&[data-bordered]": {
padding: themeTokens.spacing["2"],
border: `1px solid ${themeVars.separator}`,
},
"&[data-theme=primary]": {
vars: {
[listThemeVars.itemTextColor]: tokens.dropdownItemTextColor,
[listThemeVars.itemHoverBackground]: tokens.brandAccentHover,
[listThemeVars.itemHoverTextColor]: tokens.dropdownItemHoverTextColor,
},
},
"&[data-theme=neutral]": {
vars: {
[listThemeVars.itemTextColor]: tokens.dropdownItemTextColor,
[listThemeVars.itemHoverBackground]: tokens.dropdownItemHoverBackground,
[listThemeVars.itemHoverTextColor]: tokens.dropdownItemHoverTextColor,
},
},
},
},
]);

/**
* TODO: same as select!
Expand Down Expand Up @@ -68,17 +87,18 @@ export const item = style([
{
selectors: {
[`&[data-size=${ButtonSizes.xs}]`]: {
height: "30px",
height: toPx(LISTBOX_ITEM_SIZE.xs),
fontSize: themeTokens.fontSize.sm,
minHeight: "1.25rem",
borderRadius: themeTokens.radii.xs,
minHeight: 0,
},
[`&[data-size=${ButtonSizes.sm}]`]: {
height: "36px",
height: toPx(LISTBOX_ITEM_SIZE.sm),
fontSize: themeTokens.fontSize.md,
minHeight: "1.25rem",
minHeight: 0,
},
[`&[data-size=${ButtonSizes.md}]`]: {
height: "42px",
height: toPx(LISTBOX_ITEM_SIZE.md),
fontSize: themeTokens.fontSize.md,
},
},
Expand Down
17 changes: 15 additions & 2 deletions packages/kit/src/components/Listbox/Listbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,28 @@ export type ListboxProps<Option, OptGroup> = Omit<
"renderItem"
> & {
size?: "xs" | "sm" | "md";
theme?: "primary" | "neutral";
itemLabel?: (item: Option) => JSXElement;
bordered?: boolean;
};

export function Listbox<Option, OptGroup = never>(props: ListboxProps<Option, OptGroup>) {
const [local, others] = splitProps(props, ["class"]);
const [local, others] = splitProps(props, [
"class",
"size",
"itemLabel",
"bordered",
"theme",
]);

return (
<KListbox.Root
data-bordered={local.bordered ? "" : undefined}
data-theme={local.theme ?? "neutral"}
class={mergeClasses(styles.list)}
renderItem={node => <ListboxItem size={props.size} item={node} />}
renderItem={node => (
<ListboxItem itemLabel={local.itemLabel} size={local.size} item={node} />
)}
{...others}
/>
);
Expand Down
4 changes: 3 additions & 1 deletion packages/kit/src/components/Listbox/VirtualizedListbox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createVirtualizer } from "@tanstack/solid-virtual";
import { Listbox, ListboxItem, ListboxProps } from "./Listbox";
import { For } from "solid-js";
import { LISTBOX_ITEM_SIZE } from "./sizes";

type VirtualizedListboxProps<Option, OptGroup> = Omit<
ListboxProps<Option, OptGroup>,
Expand Down Expand Up @@ -38,7 +39,8 @@ export function VirtualizedListbox<Option, OptGroup = never>(
getScrollElement: () => listboxRef,
estimateSize: (index: number) =>
// TODO: fix that size
props.virtualizerOptions?.estimateSize?.(index) ?? 42,
props.virtualizerOptions?.estimateSize?.(index) ??
LISTBOX_ITEM_SIZE[props.size ?? "md"],
// TODO: why error?
// @ts-ignore
getItemKey: (index: number) => {
Expand Down
7 changes: 7 additions & 0 deletions packages/kit/src/components/Listbox/sizes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const LISTBOX_ITEM_SIZE: Record<ListboxItemSizeKey, number> = {
xs: 30,
sm: 36,
md: 40,
};

export type ListboxItemSizeKey = "xs" | "sm" | "md";
4 changes: 4 additions & 0 deletions packages/kit/src/utils/css.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export function mergeClasses(...classNames: Array<string | null | undefined>) {
return classNames.filter(Boolean).join(" ");
}

export function toPx(value: number) {
return `${value}px`;
}
22 changes: 22 additions & 0 deletions packages/storybook/src/stories/Listbox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ const meta = {
control: { type: "radio" },
options: ["multiple", "none", "single"],
},
size: {
control: { type: "radio" },
options: ["xs", "sm", "md"],
},
bordered: {
type: "boolean",
},
theme: {
control: { type: "radio" },
options: ["primary", "neutral"],
},
},

tags: ["autodocs"],
Expand Down Expand Up @@ -70,6 +81,17 @@ export const ListboxMedium: Story = {
},
};

export const ListboxOptionWithCustomItemLabel: Story = {
name: "Listbox - Custom Item Label",
args: {
size: "md",
defaultValue: ["Item1"],
options: ["Item1", "Item2", "Item3"],
selectionMode: "multiple",
itemLabel: (item: string) => <span style={{ color: "yellow" }}>Custom {item}</span>,
},
};

export const ListboxVirtualized: Story = {
name: "Listbox Virtualized",
render: args => {
Expand Down

0 comments on commit aab30dc

Please sign in to comment.