Merge pull request 'ui/select machines/tags: add custom combobox' (#4983) from search into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4983
This commit is contained in:
38
pkgs/clan-app/ui/src/components/Search/TagSelect.module.css
Normal file
38
pkgs/clan-app/ui/src/components/Search/TagSelect.module.css
Normal 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;
|
||||
}
|
||||
}
|
||||
38
pkgs/clan-app/ui/src/components/Search/TagSelect.stories.tsx
Normal file
38
pkgs/clan-app/ui/src/components/Search/TagSelect.stories.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Meta, StoryObj } from "@kachurun/storybook-solid";
|
||||
|
||||
import { TagSelect, TagSelectProps } from "./TagSelect";
|
||||
import { Tag } from "../Tag/Tag";
|
||||
import Icon from "../Icon/Icon";
|
||||
|
||||
const meta = {
|
||||
title: "Components/Custom/SelectStepper",
|
||||
component: TagSelect,
|
||||
} satisfies Meta<TagSelectProps<string>>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<TagSelectProps<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");
|
||||
},
|
||||
},
|
||||
};
|
||||
71
pkgs/clan-app/ui/src/components/Search/TagSelect.tsx
Normal file
71
pkgs/clan-app/ui/src/components/Search/TagSelect.tsx
Normal 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 "./TagSelect.module.css";
|
||||
import { Combobox } from "@kobalte/core/combobox";
|
||||
import { Button } from "../Button/Button";
|
||||
|
||||
export interface TagSelectProps<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 TagSelect<T>(props: TagSelectProps<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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user