ui/tagSelect: simplify by requiring objects with value key
This commit is contained in:
@@ -1,22 +1,3 @@
|
|||||||
.dummybg {
|
|
||||||
padding: 1rem;
|
|
||||||
width: 20rem;
|
|
||||||
min-height: 10rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid #2e4a4b;
|
|
||||||
background:
|
|
||||||
linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%),
|
|
||||||
linear-gradient(
|
|
||||||
180deg,
|
|
||||||
theme(colors.bg.inv.2) 0%,
|
|
||||||
theme(colors.bg.inv.3) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
box-shadow:
|
|
||||||
0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
|
||||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.trigger {
|
.trigger {
|
||||||
@apply rounded-md bg-inv-4 w-full min-h-11;
|
@apply rounded-md bg-inv-4 w-full min-h-11;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Meta, StoryObj } from "@kachurun/storybook-solid";
|
|||||||
import { TagSelect, TagSelectProps } from "./TagSelect";
|
import { TagSelect, TagSelectProps } from "./TagSelect";
|
||||||
import { Tag } from "../Tag/Tag";
|
import { Tag } from "../Tag/Tag";
|
||||||
import Icon from "../Icon/Icon";
|
import Icon from "../Icon/Icon";
|
||||||
|
import { createSignal } from "solid-js";
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: "Components/Custom/SelectStepper",
|
title: "Components/Custom/SelectStepper",
|
||||||
@@ -11,28 +12,51 @@ const meta = {
|
|||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
type Story = StoryObj<TagSelectProps<string>>;
|
interface Item {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
const Item = (item: string) => (
|
type Story = StoryObj<TagSelectProps<Item>>;
|
||||||
|
|
||||||
|
const Item = (item: Item) => (
|
||||||
<Tag
|
<Tag
|
||||||
inverted
|
inverted
|
||||||
icon={(tag) => (
|
icon={(tag) => (
|
||||||
<Icon icon={"Machine"} size="0.5rem" inverted={tag.inverted} />
|
<Icon icon={"Machine"} size="0.5rem" inverted={tag.inverted} />
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{item}
|
{item.label}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
renderItem: Item,
|
renderItem: Item,
|
||||||
values: ["foo", "bar"],
|
label: "Peer",
|
||||||
options: ["foo", "bar", "baz", "qux", "quux"],
|
options: [
|
||||||
onChange: (values: string[]) => {
|
{ value: "foo", label: "Foo" },
|
||||||
console.log("Selected values:", values);
|
{ value: "bar", label: "Bar" },
|
||||||
},
|
{ value: "baz", label: "Baz" },
|
||||||
onClick: () => {
|
{ value: "qux", label: "Qux" },
|
||||||
console.log("Combobox clicked");
|
{ value: "quux", label: "Quux" },
|
||||||
},
|
{ value: "corge", label: "Corge" },
|
||||||
|
{ value: "grault", label: "Grault" },
|
||||||
|
],
|
||||||
|
} satisfies Partial<TagSelectProps<Item>>,
|
||||||
|
render: (args: TagSelectProps<Item>) => {
|
||||||
|
const [state, setState] = createSignal<Item[]>([]);
|
||||||
|
return (
|
||||||
|
<TagSelect<Item>
|
||||||
|
{...args}
|
||||||
|
values={state()}
|
||||||
|
onClick={() => {
|
||||||
|
console.log("Clicked, current values:");
|
||||||
|
setState(() => [
|
||||||
|
{ value: "baz", label: "Baz" },
|
||||||
|
{ value: "qux", label: "Qux" },
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,43 +5,58 @@ import styles from "./TagSelect.module.css";
|
|||||||
import { Combobox } from "@kobalte/core/combobox";
|
import { Combobox } from "@kobalte/core/combobox";
|
||||||
import { Button } from "../Button/Button";
|
import { Button } from "../Button/Button";
|
||||||
|
|
||||||
|
// Base props common to both modes
|
||||||
export interface TagSelectProps<T> {
|
export interface TagSelectProps<T> {
|
||||||
// Define any props needed for the SelectStepper component
|
onClick: () => void;
|
||||||
|
label: string;
|
||||||
values: T[];
|
values: T[];
|
||||||
options: T[];
|
options: T[];
|
||||||
onChange: (values: T[]) => void;
|
|
||||||
onClick: () => void;
|
|
||||||
renderItem: (item: T) => JSX.Element;
|
renderItem: (item: T) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TagSelect<T>(props: TagSelectProps<T>) {
|
/**
|
||||||
|
* Shallowly interactive field for selecting multiple tags / machines.
|
||||||
|
* It does only handle click and focus interactions
|
||||||
|
* Displays the selected items as tags
|
||||||
|
*/
|
||||||
|
export function TagSelect<T extends { value: unknown }>(
|
||||||
|
props: TagSelectProps<T>,
|
||||||
|
) {
|
||||||
|
const optionValue = "value";
|
||||||
return (
|
return (
|
||||||
<div class={styles.dummybg}>
|
|
||||||
<div class="flex flex-col gap-1.5">
|
<div class="flex flex-col gap-1.5">
|
||||||
<div class="flex w-full items-center gap-2 px-1.5">
|
<div class="flex w-full items-center gap-2 px-1.5 py-0">
|
||||||
<Typography
|
<Typography
|
||||||
hierarchy="body"
|
hierarchy="label"
|
||||||
weight="medium"
|
weight="medium"
|
||||||
class="flex gap-2 uppercase"
|
class="flex gap-2 uppercase"
|
||||||
size="s"
|
size="xs"
|
||||||
inverted
|
inverted
|
||||||
color="secondary"
|
color="secondary"
|
||||||
>
|
>
|
||||||
Servers
|
{props.label}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Icon icon="Info" color="tertiary" inverted />
|
<Icon icon="Info" color="tertiary" inverted size={11} />
|
||||||
<Button icon="Settings" hierarchy="primary" ghost class="ml-auto" />
|
<Button
|
||||||
|
icon="Settings"
|
||||||
|
hierarchy="primary"
|
||||||
|
ghost
|
||||||
|
class="ml-auto"
|
||||||
|
size="xs"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Combobox<T>
|
<Combobox<T>
|
||||||
multiple
|
multiple
|
||||||
|
optionValue={optionValue}
|
||||||
value={props.values}
|
value={props.values}
|
||||||
onChange={props.onChange}
|
|
||||||
options={props.options}
|
options={props.options}
|
||||||
allowsEmptyCollection
|
allowsEmptyCollection
|
||||||
class="w-full"
|
class="w-full"
|
||||||
>
|
>
|
||||||
<Combobox.Control<T> aria-label="Fruits">
|
<Combobox.Control<T> aria-label="Fruits">
|
||||||
{(state) => (
|
{(state) => {
|
||||||
|
console.log("combobox state selected", state.selectedOptions());
|
||||||
|
return (
|
||||||
<Combobox.Trigger
|
<Combobox.Trigger
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
class={styles.trigger}
|
class={styles.trigger}
|
||||||
@@ -62,10 +77,10 @@ export function TagSelect<T>(props: TagSelectProps<T>) {
|
|||||||
<For each={state.selectedOptions()}>{props.renderItem}</For>
|
<For each={state.selectedOptions()}>{props.renderItem}</For>
|
||||||
</div>
|
</div>
|
||||||
</Combobox.Trigger>
|
</Combobox.Trigger>
|
||||||
)}
|
);
|
||||||
|
}}
|
||||||
</Combobox.Control>
|
</Combobox.Control>
|
||||||
</Combobox>
|
</Combobox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user