ui/select machines/tags: add custom combobox

This just renders machines and tags as chips
onclick will open another combobox
This commit is contained in:
Johannes Kirschbauer
2025-08-26 15:08:45 +02:00
parent f7cde8eb0f
commit 85006c8103
3 changed files with 147 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
.dummybg {
padding: 1rem;
width: 20rem;
min-height: 10rem;
border-radius: 8px;
border: 1px solid #2e4a4b;
background:
linear-gradient(0deg, rgba(0, 0, 0, 0.18) 0%, rgba(0, 0, 0, 0.18) 100%),
linear-gradient(
180deg,
var(--clr-bg-inv-3, rgba(46, 74, 75, 0.79)) 0%,
var(--clr-bg-inv-4, rgba(32, 54, 55, 0.79)) 100%
);
box-shadow:
0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.trigger {
@apply rounded-md bg-inv-4 w-full min-h-11;
&:focus,
&:hover {
@apply outline outline-def-1 outline-1;
background:
linear-gradient(
90deg,
var(--clr-bg-inv-acc-3, #2c4347) 0%,
var(--clr-bg-inv-acc-2, #4f747a) 100%
),
var(--clr-bg-inv-4, #203637);
box-shadow: 0 0 0 2px var(--clr-bg-inv-acc-4, #162324) inset;
}
&.open {
@apply bg-inv-acc-4;
}
}

View File

@@ -0,0 +1,38 @@
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { SelectStepper, SelectStepperProps } from "./SelectStepper";
import { Tag } from "../Tag/Tag";
import Icon from "../Icon/Icon";
const meta = {
title: "Components/Custom/SelectStepper",
component: SelectStepper,
} satisfies Meta<SelectStepperProps<string>>;
export default meta;
type Story = StoryObj<SelectStepperProps<string>>;
const Item = (item: string) => (
<Tag
inverted
icon={(tag) => (
<Icon icon={"Machine"} size="0.5rem" inverted={tag.inverted} />
)}
>
{item}
</Tag>
);
export const Default: Story = {
args: {
renderItem: Item,
values: ["foo", "bar"],
options: ["foo", "bar", "baz", "qux", "quux"],
onChange: (values: string[]) => {
console.log("Selected values:", values);
},
onClick: () => {
console.log("Combobox clicked");
},
},
};

View File

@@ -0,0 +1,71 @@
import Icon from "../Icon/Icon";
import { Typography } from "../Typography/Typography";
import { For, JSX, Show } from "solid-js";
import styles from "./SelectStepper.module.css";
import { Combobox } from "@kobalte/core/combobox";
import { Button } from "../Button/Button";
export interface SelectStepperProps<T> {
// Define any props needed for the SelectStepper component
values: T[];
options: T[];
onChange: (values: T[]) => void;
onClick: () => void;
renderItem: (item: T) => JSX.Element;
}
export function SelectStepper<T>(props: SelectStepperProps<T>) {
return (
<div class={styles.dummybg}>
<div class="flex flex-col gap-1.5">
<div class="flex w-full items-center gap-2 px-1.5">
<Typography
hierarchy="body"
weight="medium"
class="flex gap-2 uppercase"
size="s"
inverted
color="secondary"
>
Servers
</Typography>
<Icon icon="Info" color="tertiary" inverted />
<Button icon="Settings" hierarchy="primary" ghost class="ml-auto" />
</div>
<Combobox<T>
multiple
value={props.values}
onChange={props.onChange}
options={props.options}
allowsEmptyCollection
class="w-full"
>
<Combobox.Control<T> aria-label="Fruits">
{(state) => (
<Combobox.Trigger
tabIndex={1}
class={styles.trigger}
onClick={props.onClick}
>
<div class="flex flex-wrap items-center gap-2 px-2 py-3">
<Icon icon="Search" color="quaternary" inverted />
<Show when={state.selectedOptions().length === 0}>
<Typography
color="tertiary"
inverted
hierarchy="body"
size="s"
>
Select
</Typography>
</Show>
<For each={state.selectedOptions()}>{props.renderItem}</For>
</div>
</Combobox.Trigger>
)}
</Combobox.Control>
</Combobox>
</div>
</div>
);
}