Fix the item-list situation
This commit is contained in:
+59
-2
@@ -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
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user