diff --git a/biome.json b/biome.json
index 96555ae..7c3d6da 100644
--- a/biome.json
+++ b/biome.json
@@ -7,7 +7,8 @@
},
"suspicious": {
"noTsIgnore": "off",
- "noAssignInExpressions": "off"
+ "noAssignInExpressions": "off",
+ "noFallthroughSwitchClause": "warn"
},
"correctness": {
"useParseIntRadix": "off"
diff --git a/dev/FileBrowser.css b/dev/FileBrowser.css
index b94cbdb..a6b9e7f 100644
--- a/dev/FileBrowser.css
+++ b/dev/FileBrowser.css
@@ -3,18 +3,18 @@
margin-left: calc(var(--sp-2) * -1);
}
-.file-list {
+.file-browser-list {
margin: 0;
list-style: none;
padding: 0 0 0 var(--sp-2);
}
-.file-item {
+.file-brwoser-item {
display: flex;
flex-flow: column nowrap;
}
-.file-label {
+.file-browser-label {
display: flex;
justify-content: flex-start;
align-items: center;
diff --git a/dev/FileBrowser.tsx b/dev/FileBrowser.tsx
index 6e72a67..465971e 100644
--- a/dev/FileBrowser.tsx
+++ b/dev/FileBrowser.tsx
@@ -31,7 +31,7 @@ export const FileBrowser: Component<{
}> = ({ name, entry }) => {
return (
-
setIsOpen(!isOpen())}>
+
setIsOpen(!isOpen())}>
{isOpen() ? : }
{name}
@@ -66,13 +66,13 @@ export const FileBrowser: Component<{
const FileList: Component<{ folder: BrowserFolder }> = (props) => {
return (
-
+
{(item, index) => {
const [name, entry] = item;
return (
- -
+
-
{isFolder(entry) ? (
) : (
diff --git a/dev/ItemList.css b/dev/ItemList.css
new file mode 100644
index 0000000..42cce0f
--- /dev/null
+++ b/dev/ItemList.css
@@ -0,0 +1,18 @@
+.item-list {
+ background: var(--c-bg-1);
+ color: var(--c-fg-1);
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.item-list-item {
+ padding: var(--sp-1) var(--sp-2);
+ border-radius: var(--sp-1);
+ cursor: pointer;
+
+ &.active,
+ &:hover {
+ background: var(--c-bg-3);
+ }
+}
diff --git a/dev/ItemList.tsx b/dev/ItemList.tsx
new file mode 100644
index 0000000..06176d3
--- /dev/null
+++ b/dev/ItemList.tsx
@@ -0,0 +1,83 @@
+import "./ItemList.css";
+import { type Accessor, type Component, For } from "solid-js";
+
+export type ListItem = { label: string; isActive: boolean };
+
+export type ItemHandler = (item: ListItem, index: number) => void;
+
+export type Direction = undefined | "asc" | "desc";
+
+export function useSort(
+ items: Array,
+): (direction?: Direction) => Array {
+ const itemsRef = [...items];
+
+ return (direction) => {
+ if (!direction) {
+ return [...itemsRef];
+ }
+ const newItems = [...itemsRef];
+
+ 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;
+ });
+
+ return newItems;
+ };
+}
+
+export function useDirection(initialDirection?: Direction): () => Direction {
+ let direction: Direction = initialDirection;
+
+ return () => {
+ switch (direction) {
+ case "asc": {
+ direction = "desc";
+ break;
+ }
+ case "desc": {
+ direction = undefined;
+ break;
+ }
+ default: {
+ direction = "asc";
+ }
+ }
+
+ return direction;
+ };
+}
+
+export const ItemList: Component<{
+ items: Accessor>;
+ handler: ItemHandler;
+}> = (props) => (
+
+
+ {(item, index) => {
+ return (
+ - props.handler(item, index())}
+ >
+ {item.label}
+
+ );
+ }}
+
+
+);
diff --git a/dev/RemoteItemList.css b/dev/RemoteItemList.css
new file mode 100644
index 0000000..c580c65
--- /dev/null
+++ b/dev/RemoteItemList.css
@@ -0,0 +1,50 @@
+.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: grid;
+ grid-template-columns: 1fr auto auto;
+ border-bottom: 1px solid var(--c-fg-3);
+}
+
+.remote-item-list-label {
+ 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;
+ }
+}
diff --git a/dev/RemoteItemList.tsx b/dev/RemoteItemList.tsx
new file mode 100644
index 0000000..b394ce6
--- /dev/null
+++ b/dev/RemoteItemList.tsx
@@ -0,0 +1,59 @@
+import "./RemoteItemList.css";
+import { type Component, createSignal } from "solid-js";
+import {
+ ItemList,
+ type ListItem,
+ useDirection,
+ useSort,
+} from "#/dev/ItemList.tsx";
+import {
+ ArrowNarrowDownIcon,
+ ArrowNarrowUpIcon,
+ ArrowsSortIcon,
+ ChevronDownIcon,
+ ReloadIcon,
+} from "#/dev/icons/index.ts";
+
+export const RemoteItemList: Component<{
+ fetchItems: () => Promise>;
+}> = () => {
+ const [items, setItems] = createSignal>([
+ {
+ label: "foo",
+ isActive: false,
+ },
+ {
+ label: "bar",
+ isActive: false,
+ },
+ {
+ label: "lol",
+ isActive: true,
+ },
+ ]);
+ const getSorted = useSort(items());
+ const toggleDirection = useDirection();
+
+ return (
+
+
+
+
+ );
+};
diff --git a/dev/icons/ArrowNarrowDown.tsx b/dev/icons/ArrowNarrowDown.tsx
new file mode 100644
index 0000000..721343c
--- /dev/null
+++ b/dev/icons/ArrowNarrowDown.tsx
@@ -0,0 +1,17 @@
+export const ArrowNarrowDownIcon = () => {
+ ;
+};
diff --git a/dev/icons/ArrowNarrowUp.tsx b/dev/icons/ArrowNarrowUp.tsx
new file mode 100644
index 0000000..ed8239d
--- /dev/null
+++ b/dev/icons/ArrowNarrowUp.tsx
@@ -0,0 +1,17 @@
+export const ArrowNarrowUpIcon = () => (
+
+);
diff --git a/dev/icons/ArrowsSort.tsx b/dev/icons/ArrowsSort.tsx
new file mode 100644
index 0000000..937341c
--- /dev/null
+++ b/dev/icons/ArrowsSort.tsx
@@ -0,0 +1,17 @@
+export const ArrowsSortIcon = () => (
+
+);
diff --git a/dev/icons/ChevronDown.tsx b/dev/icons/ChevronDown.tsx
new file mode 100644
index 0000000..fa13551
--- /dev/null
+++ b/dev/icons/ChevronDown.tsx
@@ -0,0 +1,17 @@
+export const ChevronDownIcon = () => (
+
+);
diff --git a/dev/icons/Reload.tsx b/dev/icons/Reload.tsx
new file mode 100644
index 0000000..361782a
--- /dev/null
+++ b/dev/icons/Reload.tsx
@@ -0,0 +1,19 @@
+export const ReloadIcon = () => (
+
+);
diff --git a/dev/icons/index.ts b/dev/icons/index.ts
index c245b88..1e05d1f 100644
--- a/dev/icons/index.ts
+++ b/dev/icons/index.ts
@@ -1,5 +1,9 @@
export * from "./ArrowAutofitHeight.tsx";
export * from "./ArrowAutofitWidth.tsx";
+export * from "./ArrowNarrowDown.tsx";
+export * from "./ArrowNarrowUp.tsx";
+export * from "./ArrowsSort.tsx";
+export * from "./ChevronDown.tsx";
export * from "./File.tsx";
export * from "./FileDelta.tsx";
export * from "./FileMusic.tsx";
@@ -16,6 +20,7 @@ export * from "./Polygon.tsx";
export * from "./Refresh.tsx";
export * from "./RefreshAlert.tsx";
export * from "./RefreshDot.tsx";
+export * from "./Reload.tsx";
export * from "./Script.tsx";
export * from "./StackBackward.tsx";
export * from "./StackForward.tsx";
diff --git a/dev/main.tsx b/dev/main.tsx
index ac534a8..8520a83 100644
--- a/dev/main.tsx
+++ b/dev/main.tsx
@@ -4,6 +4,7 @@ import { render } from "solid-js/web";
import { InputField } from "#/dev/controls/InputField.tsx";
import { RangeSlider } from "#/dev/controls/RangeSlider.tsx";
import { type BrowserFolder, FileBrowser } from "#/dev/FileBrowser.tsx";
+import type { ListItem } from "#/dev/ItemList.tsx";
import {
ArrowAutofitHeightIcon,
ArrowAutofitWidthIcon,
@@ -13,6 +14,7 @@ import {
import type { Entity } from "#/engine/Entity";
import { currentScene, loadScene, type SceneData } from "#/engine/Scene.ts";
import { setScale, viewport } from "#/engine/Viewport.ts";
+import { RemoteItemList } from "./RemoteItemList";
async function fetchBrowserData(dir: string): Promise {
const indexRef: string[] = await (await fetch(`/api/${dir}`)).json();
@@ -169,6 +171,8 @@ const DevTools: Component<{ gameContainer: HTMLElement }> = ({
+
+ []} />