Fix the item-list situation

This commit is contained in:
kolaflash
2025-12-30 19:07:18 +01:00
parent fe1e76fe41
commit 24508da335
5 changed files with 208 additions and 222 deletions
+59 -2
View File
@@ -1,12 +1,15 @@
.item-list { .item-list {
border-radius: var(--sp-1);
ul {
background: var(--c-bg-1); background: var(--c-bg-1);
color: var(--c-fg-1); color: var(--c-fg-1);
list-style: none; list-style: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.item-list-item { li {
padding: var(--sp-1) var(--sp-2); padding: var(--sp-1) var(--sp-2);
border-radius: var(--sp-1); border-radius: var(--sp-1);
cursor: pointer; cursor: pointer;
@@ -15,4 +18,58 @@
&:hover { &:hover {
background: var(--c-bg-3); background: var(--c-bg-3);
} }
}
button {
color: inherit;
border-radius: var(--sp-1);
border: none;
background: transparent;
padding: var(--sp-1);
margin-left: var(--sp-1);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
button:hover {
background: var(--c-bg-3);
}
svg {
width: 1.5em;
height: 1.5em;
}
}
.item-list-header {
display: flex;
justify-content: space-between;
border-bottom: 1px solid var(--c-fg-3);
}
.item-list-name {
padding-left: var(--sp-1);
cursor: pointer;
display: flex;
align-items: center;
justify-content: flex-start;
width: fit-content;
border-radius: var(--sp-1);
&:hover {
background: var(--c-bg-3);
}
svg {
margin-left: var(--sp-1);
width: 0.875em;
height: 0.875em;
}
}
.item-list-name.hide {
svg {
rotate: 0.75turn;
}
} }
+127 -6
View File
@@ -1,20 +1,138 @@
import "./ItemList.css"; import "./ItemList.css";
import { type Accessor, type Component, For } from "solid-js"; import {
type Accessor,
type Component,
createEffect,
createSignal,
For,
Match,
Show,
type Signal,
Switch,
} from "solid-js";
import {
ArrowNarrowDownIcon,
ArrowNarrowUpIcon,
ArrowsSortIcon,
ChevronDownIcon,
} from "#/dev/icons/index.ts";
export type ListItem = { label: string; isActive: boolean }; export type ListItem = { label: string; isActive: boolean };
export type ItemHandler = (item: ListItem, index: number) => void; export type ItemHandler = (item: ListItem, index: number) => void;
export type Direction = "default" | "asc" | "desc";
function useDirection(
signal: Signal<Direction>,
): [Accessor<Direction>, (forcedDirection?: Direction) => Direction] {
const [direction, setDirection] = signal;
return [
direction,
(forcedDirection) => {
if (forcedDirection) {
setDirection(forcedDirection);
return direction();
}
switch (direction()) {
case "asc": {
setDirection("desc");
break;
}
case "desc": {
setDirection("default");
break;
}
default: {
setDirection("asc");
}
}
return direction();
},
];
}
export const ItemList: Component<{ export const ItemList: Component<{
name: string;
items: Accessor<Array<ListItem>>; items: Accessor<Array<ListItem>>;
handler: ItemHandler; handler: ItemHandler;
}> = (props) => ( }> = (props) => {
<ul class="item-list"> const [showList, setShowList] = createSignal(true);
<For each={props.items()}> const [items, setItems] = createSignal([...props.items()]);
const [direction, toggleDirection] = useDirection(
createSignal<Direction>("default"),
);
createEffect(() => {
sortItems(props.items(), direction());
});
const sortItems = (items: Array<ListItem>, direction: Direction) => {
if (direction === "default") {
setItems([...props.items()]);
return;
}
const newItems = [...items];
newItems.sort((a, b) => {
const labelA = a.label.toLocaleLowerCase();
const labelB = b.label.toLocaleLowerCase();
if (labelA < labelB) {
return direction === "asc" ? -1 : 1;
}
if (labelA > labelB) {
return direction === "asc" ? 1 : -1;
}
return 0;
});
setItems(newItems);
};
return (
<div class="item-list">
<div class="item-list-header">
<div
class="item-list-name"
classList={{
hide: !showList(),
}}
role="button"
onClick={() => setShowList(!showList())}
>
{props.name}
<ChevronDownIcon />
</div>
<button
type="button"
onClick={() => {
sortItems(items(), toggleDirection());
}}
>
<Switch fallback={<ArrowsSortIcon />}>
<Match when={direction() === "asc"}>
<ArrowNarrowUpIcon />
</Match>
<Match when={direction() === "desc"}>
<ArrowNarrowDownIcon />
</Match>
</Switch>
</button>
</div>
<Show when={showList()}>
<ul>
<For each={items()}>
{(item, index) => { {(item, index) => {
return ( return (
<li <li
class="item-list-item"
classList={{ classList={{
active: item.isActive, active: item.isActive,
}} }}
@@ -26,4 +144,7 @@ export const ItemList: Component<{
}} }}
</For> </For>
</ul> </ul>
); </Show>
</div>
);
};
-56
View File
@@ -1,56 +0,0 @@
.remote-item-list {
border-radius: var(--sp-1);
button {
color: inherit;
border-radius: var(--sp-1);
border: none;
background: transparent;
padding: var(--sp-1);
margin-left: var(--sp-1);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
button:hover {
background: var(--c-bg-3);
}
svg {
width: 1.5em;
height: 1.5em;
}
}
.remote-item-list-header {
display: flex;
justify-content: space-between;
border-bottom: 1px solid var(--c-fg-3);
}
.remote-item-list-name {
padding-left: var(--sp-1);
cursor: pointer;
display: flex;
align-items: center;
justify-content: flex-start;
width: fit-content;
border-radius: var(--sp-1);
&:hover {
background: var(--c-bg-3);
}
svg {
margin-left: var(--sp-1);
width: 0.875em;
height: 0.875em;
}
}
.remote-item-list-name.hide {
svg {
rotate: 0.75turn;
}
}
-130
View File
@@ -1,130 +0,0 @@
import "./RemoteItemList.css";
import {
type Accessor,
type Component,
createEffect,
createSignal,
Match,
Show,
type Signal,
Switch,
} from "solid-js";
import { type ItemHandler, ItemList, type ListItem } from "#/dev/ItemList.tsx";
import {
ArrowNarrowDownIcon,
ArrowNarrowUpIcon,
ArrowsSortIcon,
ChevronDownIcon,
} from "#/dev/icons/index.ts";
export type Direction = "default" | "asc" | "desc";
function useDirection(
signal: Signal<Direction>,
): [Accessor<Direction>, (forcedDirection?: Direction) => Direction] {
const [direction, setDirection] = signal;
return [
direction,
(forcedDirection) => {
if (forcedDirection) {
setDirection(forcedDirection);
return direction();
}
switch (direction()) {
case "asc": {
setDirection("desc");
break;
}
case "desc": {
setDirection("default");
break;
}
default: {
setDirection("asc");
}
}
return direction();
},
];
}
export const RemoteItemList: Component<{
name: string;
items: Accessor<Array<ListItem>>;
handler: ItemHandler;
}> = (props) => {
const [showList, setShowList] = createSignal(true);
const [items, setItems] = createSignal([...props.items()]);
const [direction, toggleDirection] = useDirection(
createSignal<Direction>("default"),
);
createEffect(() => {
sortItems(props.items(), direction());
});
const sortItems = (items: Array<ListItem>, direction: Direction) => {
if (direction === "default") {
setItems([...props.items()]);
return;
}
const newItems = [...items];
newItems.sort((a, b) => {
const labelA = a.label.toLocaleLowerCase();
const labelB = b.label.toLocaleLowerCase();
if (labelA < labelB) {
return direction === "asc" ? -1 : 1;
}
if (labelA > labelB) {
return direction === "asc" ? 1 : -1;
}
return 0;
});
setItems(newItems);
};
return (
<div class="remote-item-list">
<div class="remote-item-list-header">
<div
class="remote-item-list-name"
classList={{
hide: !showList(),
}}
role="button"
onClick={() => setShowList(!showList())}
>
{props.name}
<ChevronDownIcon />
</div>
<button
type="button"
onClick={() => {
sortItems(items(), toggleDirection());
}}
>
<Switch fallback={<ArrowsSortIcon />}>
<Match when={direction() === "asc"}>
<ArrowNarrowUpIcon />
</Match>
<Match when={direction() === "desc"}>
<ArrowNarrowDownIcon />
</Match>
</Switch>
</button>
</div>
<Show when={showList()}>
<ItemList items={items} handler={props.handler} />
</Show>
</div>
);
};
+2 -8
View File
@@ -4,17 +4,15 @@ import { render } from "solid-js/web";
import { InputField } from "#/dev/controls/InputField.tsx"; import { InputField } from "#/dev/controls/InputField.tsx";
import { RangeSlider } from "#/dev/controls/RangeSlider.tsx"; import { RangeSlider } from "#/dev/controls/RangeSlider.tsx";
import { type BrowserFolder, FileBrowser } from "#/dev/FileBrowser.tsx"; import { type BrowserFolder, FileBrowser } from "#/dev/FileBrowser.tsx";
import { ItemList, type ListItem } from "#/dev/ItemList.tsx";
import { import {
ArrowAutofitHeightIcon, ArrowAutofitHeightIcon,
ArrowAutofitWidthIcon, ArrowAutofitWidthIcon,
FileIcon, FileIcon,
ZoomScanIcon, ZoomScanIcon,
} from "#/dev/icons/index.ts"; } from "#/dev/icons/index.ts";
import type { Entity } from "#/engine/Entity";
import { currentScene, loadScene, type SceneData } from "#/engine/Scene.ts"; import { currentScene, loadScene, type SceneData } from "#/engine/Scene.ts";
import { setScale, viewport } from "#/engine/Viewport.ts"; import { setScale, viewport } from "#/engine/Viewport.ts";
import type { ListItem } from "./ItemList";
import { RemoteItemList } from "./RemoteItemList";
async function fetchBrowserData(dir: string): Promise<BrowserFolder> { async function fetchBrowserData(dir: string): Promise<BrowserFolder> {
const indexRef: string[] = await (await fetch(`/api/${dir}`)).json(); const indexRef: string[] = await (await fetch(`/api/${dir}`)).json();
@@ -160,11 +158,7 @@ const DevTools: Component<{ gameContainer: HTMLElement }> = ({
<FileBrowser root={scenes} handler={setCurrentScene} /> <FileBrowser root={scenes} handler={setCurrentScene} />
<RemoteItemList <ItemList name={"Entities"} handler={console.log} items={entities} />
name={"Entities"}
handler={console.log}
items={entities}
/>
</div> </div>
<div class="tool-bar-resize" onMouseDown={startResizing}></div> <div class="tool-bar-resize" onMouseDown={startResizing}></div>
</div> </div>