feat: implement list-manager
This commit is contained in:
parent
fa89bd904f
commit
36369fb1f0
@ -1,11 +1,134 @@
|
||||
import { mergeProps } from "solid-js";
|
||||
import { createMemo, createSignal, mergeProps } from "solid-js";
|
||||
|
||||
function ListManager(props) {
|
||||
props = mergeProps(
|
||||
{ items: [], availableItems: [], itemToString: () => "" },
|
||||
{
|
||||
items: [],
|
||||
availableItems: [],
|
||||
itemToString: () => "",
|
||||
style: "",
|
||||
onItemSelect: () => {},
|
||||
onItemDeselect: () => {},
|
||||
itemsTitle: "Selected items",
|
||||
availableItemsTitle: "Available items",
|
||||
},
|
||||
props
|
||||
);
|
||||
return <></>;
|
||||
|
||||
const [selectedAvailableItemIndex, setSelectedAvailableItemIndex] =
|
||||
createSignal(-1);
|
||||
const [selectedItemIndex, setSelectedItemIndex] = createSignal(-1);
|
||||
|
||||
const selectableAvailableItems = createMemo(() =>
|
||||
props.availableItems
|
||||
.filter((item) => !props.items.includes(item))
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
);
|
||||
const selectableItems = createMemo(() => props.items);
|
||||
const canSelect = createMemo(
|
||||
() =>
|
||||
selectedAvailableItemIndex() >= 0 &&
|
||||
selectedAvailableItemIndex() < selectableAvailableItems().length
|
||||
);
|
||||
const canDeselect = createMemo(
|
||||
() =>
|
||||
selectedItemIndex() >= 0 && selectedItemIndex() < selectableItems().length
|
||||
);
|
||||
|
||||
function handleSelectItem() {
|
||||
let itemIndex = selectedAvailableItemIndex();
|
||||
let item = selectableAvailableItems()[itemIndex];
|
||||
if (itemIndex === selectableAvailableItems().length - 1) {
|
||||
setSelectedAvailableItemIndex(itemIndex - 1);
|
||||
}
|
||||
props.onItemSelect(item);
|
||||
}
|
||||
|
||||
function handleDeselectItem() {
|
||||
let itemIndex = selectedItemIndex();
|
||||
let item = selectableItems()[itemIndex];
|
||||
if (itemIndex === selectableItems().length - 1) {
|
||||
setSelectedItemIndex(itemIndex - 1);
|
||||
}
|
||||
props.onItemDeselect(item);
|
||||
}
|
||||
|
||||
function ListItem(props) {
|
||||
props = mergeProps(
|
||||
{ children: "", selected: false, onClick: () => {} },
|
||||
props
|
||||
);
|
||||
return (
|
||||
<div
|
||||
class={"px-2" + (props.selected ? " bg-secondary" : "")}
|
||||
role="button"
|
||||
onClick={props.onClick}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={"d-flex"} style={props.style}>
|
||||
<div class="flex-shrink-1 w-50 d-flex flex-column">
|
||||
<div class="px-2">{props.itemsTitle}</div>
|
||||
<div class="rounded border bg-body flex-fill overflow-y-scroll">
|
||||
{selectableItems().map((item, index) => (
|
||||
<ListItem
|
||||
onClick={() => setSelectedItemIndex(index)}
|
||||
selected={index === selectedItemIndex()}
|
||||
>
|
||||
{props.itemToString(item)}
|
||||
</ListItem>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-2 d-flex flex-column justify-content-center align-items-center">
|
||||
<button
|
||||
class="btn btn-sm btn-outline-secondary mb-2"
|
||||
onClick={handleSelectItem}
|
||||
disabled={!canSelect()}
|
||||
>
|
||||
<i class="bi bi-caret-left-fill"></i>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
onClick={handleDeselectItem}
|
||||
disabled={!canDeselect()}
|
||||
>
|
||||
<i class="bi bi-caret-right-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-shrink-1 w-50 d-flex flex-column">
|
||||
<div class="px-2">{props.availableItemsTitle}</div>
|
||||
<div class="rounded border bg-body flex-fill overflow-y-scroll">
|
||||
{selectableAvailableItems().map((item, index) => (
|
||||
<ListItem
|
||||
onClick={() => setSelectedAvailableItemIndex(index)}
|
||||
selected={index === selectedAvailableItemIndex()}
|
||||
>
|
||||
{props.itemToString(item)}
|
||||
</ListItem>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ListManager;
|
||||
|
||||
@ -42,6 +42,16 @@ function CreateRemoteModal(props) {
|
||||
setError("");
|
||||
}
|
||||
|
||||
function handleCommandSelect(item) {
|
||||
setCommands([...commands(), item]);
|
||||
}
|
||||
|
||||
function handleCommandDeselect(item) {
|
||||
setCommands(
|
||||
commands().filter((command) => command.getId() !== item.getId())
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
ref={props.ref}
|
||||
@ -49,7 +59,7 @@ function CreateRemoteModal(props) {
|
||||
modalTitle="New Remote"
|
||||
centered={true}
|
||||
>
|
||||
<div class="modal-body" style="overflow-y:inherit !important;">
|
||||
<div class="modal-body bg-body-tertiary">
|
||||
<Show when={error() !== ""}>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{error()}
|
||||
@ -68,12 +78,19 @@ function CreateRemoteModal(props) {
|
||||
errorText={`Title must be at least ${MIN_TITLE_LENGTH} characters long`}
|
||||
/>
|
||||
</div>
|
||||
<ListManager
|
||||
style="height: 20em;"
|
||||
items={commands()}
|
||||
availableItems={availableCommands()}
|
||||
itemToString={(command) =>
|
||||
`${command.getTitle()} (${command.getProtocol()}:${command.getCommandType()})`
|
||||
}
|
||||
onItemSelect={handleCommandSelect}
|
||||
onItemDeselect={handleCommandDeselect}
|
||||
itemsTitle="Selected Commands"
|
||||
availableItemsTitle="Available Commands"
|
||||
/>
|
||||
</div>
|
||||
<ListManager
|
||||
items={commands()}
|
||||
availableItems={availableCommands()}
|
||||
itemToString={(command) => command.getName()}
|
||||
/>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||
Cancel
|
||||
|
||||
@ -1,7 +1,33 @@
|
||||
import Command from "../data/command";
|
||||
import Serializer from "../data/serializer";
|
||||
|
||||
function RemotesService() {
|
||||
let commands = [];
|
||||
let commands = [
|
||||
new Command({
|
||||
id: 1,
|
||||
protocol: "samsung",
|
||||
commandNumber: 1,
|
||||
device: 7,
|
||||
commandType: "power",
|
||||
title: "Power Samsung",
|
||||
}),
|
||||
new Command({
|
||||
id: 2,
|
||||
protocol: "samsung",
|
||||
commandNumber: 2,
|
||||
device: 7,
|
||||
commandType: "input",
|
||||
title: "Input Samsung",
|
||||
}),
|
||||
new Command({
|
||||
id: 3,
|
||||
protocol: "samsung",
|
||||
commandNumber: 3,
|
||||
device: 7,
|
||||
commandType: "volume_up",
|
||||
title: "Volume Up Samsung",
|
||||
}),
|
||||
];
|
||||
let remotes = [];
|
||||
|
||||
async function getRemotes() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user