wip
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
||||
ComboboxRootProps,
|
||||
} from "@kobalte/core/combobox";
|
||||
import { FieldProps } from "./Field";
|
||||
import { Accessor, createSignal, For, Show } from "solid-js";
|
||||
import { Accessor, createEffect, createSignal, For, Show } from "solid-js";
|
||||
import Icon from "../Icon/Icon";
|
||||
import cx from "classnames";
|
||||
import { Typography } from "@/src/components/Typography/Typography";
|
||||
@@ -28,13 +28,15 @@ interface MachineTag {
|
||||
|
||||
export type MachineTagsProps = FieldProps & ComboboxRootProps<MachineTag> & {};
|
||||
|
||||
const deduplicateOptions = (options: MachineTag[]) => {
|
||||
return options.filter((option, index) => {
|
||||
return options.findIndex((o) => o.value === option.value) === index;
|
||||
const uniqueOptions = (options: MachineTag[]) => {
|
||||
const record: Record<string, MachineTag> = {};
|
||||
options.forEach((option) => {
|
||||
record[option.value] = option;
|
||||
});
|
||||
return Object.values(record);
|
||||
};
|
||||
|
||||
const sortOptions = (options: MachineTag[]) => {
|
||||
const sortedOptions = (options: MachineTag[]) => {
|
||||
return options.sort((a, b) => {
|
||||
if (a.disabled && !b.disabled) return -1;
|
||||
return a.value.localeCompare(b.value);
|
||||
@@ -56,63 +58,57 @@ const ItemComponent = (props: { item: CollectionNode<MachineTag> }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Control = (props: {
|
||||
inverted?: boolean;
|
||||
readOnly?: boolean;
|
||||
disabled?: boolean;
|
||||
}) => (
|
||||
<Combobox.Control<MachineTag> class="control">
|
||||
{(state) => (
|
||||
<div class="selected-options">
|
||||
<For each={state.selectedOptions()}>
|
||||
{(option) => (
|
||||
<Tag
|
||||
label={option.value}
|
||||
inverted={props.inverted}
|
||||
action={
|
||||
option.disabled || props.disabled || props.readOnly
|
||||
? undefined
|
||||
: {
|
||||
icon: "Close",
|
||||
onClick: () => state.remove(option),
|
||||
}
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
<Show when={!props.readOnly}>
|
||||
<div class="input-container">
|
||||
<Combobox.Input />
|
||||
<Combobox.Trigger class="trigger">
|
||||
<Combobox.Icon class="icon">
|
||||
<Icon icon="Expand" inverted={!props.inverted} size="100%" />
|
||||
</Combobox.Icon>
|
||||
</Combobox.Trigger>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
</Combobox.Control>
|
||||
);
|
||||
|
||||
export const MachineTags = (props: MachineTagsProps) => {
|
||||
// options which are applied to all machines and cannot be interacted with
|
||||
const alwaysOptions = [{ value: "All", disabled: true }];
|
||||
|
||||
const [options, setOptions] = createSignal<MachineTag[]>(
|
||||
sortOptions([
|
||||
const [selectedOptions, setSelectedOptions] = createSignal<MachineTag[]>([
|
||||
...alwaysOptions,
|
||||
...((props.defaultValue as MachineTag[]) || []),
|
||||
]);
|
||||
|
||||
const [availableOptions, setAvailableOptions] = createSignal<MachineTag[]>(
|
||||
sortedOptions([
|
||||
...alwaysOptions,
|
||||
...((props.defaultValue as MachineTag[]) || []),
|
||||
]),
|
||||
);
|
||||
|
||||
const _setOptions = (options: MachineTag[] | null) => {
|
||||
if (options === null) setOptions([]);
|
||||
setOptions(
|
||||
sortOptions(deduplicateOptions([...alwaysOptions, ...(options || [])])),
|
||||
const _setAvailableOptions = (options: MachineTag[] | null) => {
|
||||
if (options === null) setAvailableOptions([...alwaysOptions]);
|
||||
|
||||
setAvailableOptions(
|
||||
sortedOptions(uniqueOptions([...alwaysOptions, ...(options || [])])),
|
||||
);
|
||||
};
|
||||
|
||||
const _setSelectedOptions = (options: MachineTag[] | null) => {
|
||||
if (options === null) setSelectedOptions([...alwaysOptions]);
|
||||
|
||||
setSelectedOptions(
|
||||
sortedOptions(uniqueOptions([...alwaysOptions, ...(options || [])])),
|
||||
);
|
||||
};
|
||||
|
||||
const addSelectedOption = (option: MachineTag) => {
|
||||
_setAvailableOptions([...availableOptions(), option]);
|
||||
|
||||
setSelectedOptions(
|
||||
sortedOptions(uniqueOptions([...selectedOptions(), option])),
|
||||
);
|
||||
};
|
||||
|
||||
const removeSelectedOption = (option: MachineTag) => {
|
||||
setSelectedOptions(
|
||||
selectedOptions().filter((o) => o.value !== option.value),
|
||||
)
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
console.log("selectedOptions", selectedOptions());
|
||||
console.log("availableOptions", availableOptions());
|
||||
});
|
||||
|
||||
const align = () => {
|
||||
if (props.readOnly) {
|
||||
return "center";
|
||||
@@ -121,6 +117,20 @@ export const MachineTags = (props: MachineTagsProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyDown = (event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const input = event.currentTarget as HTMLInputElement;
|
||||
if (input.value === "") return;
|
||||
|
||||
addSelectedOption({ value: input.value });
|
||||
|
||||
input.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Combobox<MachineTag>
|
||||
multiple
|
||||
@@ -129,14 +139,13 @@ export const MachineTags = (props: MachineTagsProps) => {
|
||||
ghost: props.ghost,
|
||||
})}
|
||||
{...props}
|
||||
itemComponent={ItemComponent}
|
||||
defaultValue={options()}
|
||||
value={options()}
|
||||
value={selectedOptions()}
|
||||
options={availableOptions()}
|
||||
optionLabel={(option) => option.value}
|
||||
optionTextValue="value"
|
||||
options={options()}
|
||||
onChange={_setOptions}
|
||||
placeholder="Tag a machine"
|
||||
itemComponent={ItemComponent}
|
||||
onChange={_setSelectedOptions}
|
||||
placeholder="Enter a tag name"
|
||||
>
|
||||
<Orienter orientation={props.orientation} align={align()}>
|
||||
<Label
|
||||
@@ -147,11 +156,40 @@ export const MachineTags = (props: MachineTagsProps) => {
|
||||
|
||||
{/*<KCombobox.HiddenSelect {...props.input} />*/}
|
||||
|
||||
<Control
|
||||
inverted={props.inverted}
|
||||
readOnly={props.readOnly}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<Combobox.Control<MachineTag> class="control">
|
||||
<div class="selected-options">
|
||||
<For each={selectedOptions()}>
|
||||
{(option) => (
|
||||
<Tag
|
||||
label={option.value}
|
||||
inverted={props.inverted}
|
||||
action={
|
||||
option.disabled || props.disabled || props.readOnly
|
||||
? undefined
|
||||
: {
|
||||
icon: "Close",
|
||||
onClick: () => removeSelectedOption(option),
|
||||
}
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
<Show when={!props.readOnly}>
|
||||
<div class="input-container">
|
||||
<Combobox.Input onKeyDown={onKeyDown} />
|
||||
<Combobox.Trigger class="trigger">
|
||||
<Combobox.Icon class="icon">
|
||||
<Icon
|
||||
icon="Expand"
|
||||
inverted={!props.inverted}
|
||||
size="100%"
|
||||
/>
|
||||
</Combobox.Icon>
|
||||
</Combobox.Trigger>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Combobox.Control>
|
||||
</Orienter>
|
||||
|
||||
<Combobox.Portal>
|
||||
|
||||
@@ -146,6 +146,7 @@ export const Default: Story = {
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
multiple
|
||||
defaultValue={[{ value: "Foo"}, {value: "bar"}, {value: "baz"}]}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
Reference in New Issue
Block a user