WIP
This commit is contained in:
+2
-1
@@ -7,7 +7,8 @@
|
|||||||
},
|
},
|
||||||
"suspicious": {
|
"suspicious": {
|
||||||
"noTsIgnore": "off",
|
"noTsIgnore": "off",
|
||||||
"noAssignInExpressions": "off"
|
"noAssignInExpressions": "off",
|
||||||
|
"noFallthroughSwitchClause": "warn"
|
||||||
},
|
},
|
||||||
"correctness": {
|
"correctness": {
|
||||||
"useParseIntRadix": "off"
|
"useParseIntRadix": "off"
|
||||||
|
|||||||
+3
-3
@@ -3,18 +3,18 @@
|
|||||||
margin-left: calc(var(--sp-2) * -1);
|
margin-left: calc(var(--sp-2) * -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-list {
|
.file-browser-list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0 0 0 var(--sp-2);
|
padding: 0 0 0 var(--sp-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-item {
|
.file-brwoser-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-label {
|
.file-browser-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
+4
-4
@@ -31,7 +31,7 @@ export const FileBrowser: Component<{
|
|||||||
}> = ({ name, entry }) => {
|
}> = ({ name, entry }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class="file-label"
|
class="file-browser-label"
|
||||||
classList={{
|
classList={{
|
||||||
active: entry.isActive,
|
active: entry.isActive,
|
||||||
}}
|
}}
|
||||||
@@ -52,7 +52,7 @@ export const FileBrowser: Component<{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div class="file-label" onClick={() => setIsOpen(!isOpen())}>
|
<div class="file-browser-label" onClick={() => setIsOpen(!isOpen())}>
|
||||||
{isOpen() ? <FolderOpenIcon /> : <FolderIcon />}
|
{isOpen() ? <FolderOpenIcon /> : <FolderIcon />}
|
||||||
<span>{name}</span>
|
<span>{name}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,13 +66,13 @@ export const FileBrowser: Component<{
|
|||||||
|
|
||||||
const FileList: Component<{ folder: BrowserFolder }> = (props) => {
|
const FileList: Component<{ folder: BrowserFolder }> = (props) => {
|
||||||
return (
|
return (
|
||||||
<ul class="file-list">
|
<ul class="file-browser-list">
|
||||||
<For each={[...props.folder.entries()]}>
|
<For each={[...props.folder.entries()]}>
|
||||||
{(item, index) => {
|
{(item, index) => {
|
||||||
const [name, entry] = item;
|
const [name, entry] = item;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li class="file-item">
|
<li class="file-browser-item">
|
||||||
{isFolder(entry) ? (
|
{isFolder(entry) ? (
|
||||||
<FolderEntry folder={entry} name={name} open={index() < 1} />
|
<FolderEntry folder={entry} name={name} open={index() < 1} />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<ListItem>,
|
||||||
|
): (direction?: Direction) => Array<ListItem> {
|
||||||
|
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<Array<ListItem>>;
|
||||||
|
handler: ItemHandler;
|
||||||
|
}> = (props) => (
|
||||||
|
<ul class="item-list">
|
||||||
|
<For each={props.items()}>
|
||||||
|
{(item, index) => {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
class="item-list-item"
|
||||||
|
classList={{
|
||||||
|
active: item.isActive,
|
||||||
|
}}
|
||||||
|
onClick={() => props.handler(item, index())}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Array<ListItem>>;
|
||||||
|
}> = () => {
|
||||||
|
const [items, setItems] = createSignal<Array<ListItem>>([
|
||||||
|
{
|
||||||
|
label: "foo",
|
||||||
|
isActive: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "bar",
|
||||||
|
isActive: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "lol",
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const getSorted = useSort(items());
|
||||||
|
const toggleDirection = useDirection();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="remote-item-list">
|
||||||
|
<div class="remote-item-list-header">
|
||||||
|
<div class="remote-item-list-label" role="button">
|
||||||
|
Items
|
||||||
|
<ChevronDownIcon />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
setItems(getSorted(toggleDirection()));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowsSortIcon />
|
||||||
|
</button>
|
||||||
|
<button type="button">
|
||||||
|
<ReloadIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ItemList items={items} handler={console.log} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export const ArrowNarrowDownIcon = () => {
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width={2}
|
||||||
|
d="M12 5v14m4-4l-4 4m-4-4l4 4"
|
||||||
|
/>
|
||||||
|
</svg>;
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export const ArrowNarrowUpIcon = () => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width={2}
|
||||||
|
d="M12 5v14m4-10l-4-4M8 9l4-4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export const ArrowsSortIcon = () => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width={2}
|
||||||
|
d="m3 9l4-4l4 4M7 5v14m14-4l-4 4l-4-4m4 4V5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export const ChevronDownIcon = () => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width={2}
|
||||||
|
d="m6 9l6 6l6-6"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
export const ReloadIcon = () => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width={2}
|
||||||
|
>
|
||||||
|
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
||||||
|
<path d="M20 4v5h-5" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
export * from "./ArrowAutofitHeight.tsx";
|
export * from "./ArrowAutofitHeight.tsx";
|
||||||
export * from "./ArrowAutofitWidth.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 "./File.tsx";
|
||||||
export * from "./FileDelta.tsx";
|
export * from "./FileDelta.tsx";
|
||||||
export * from "./FileMusic.tsx";
|
export * from "./FileMusic.tsx";
|
||||||
@@ -16,6 +20,7 @@ export * from "./Polygon.tsx";
|
|||||||
export * from "./Refresh.tsx";
|
export * from "./Refresh.tsx";
|
||||||
export * from "./RefreshAlert.tsx";
|
export * from "./RefreshAlert.tsx";
|
||||||
export * from "./RefreshDot.tsx";
|
export * from "./RefreshDot.tsx";
|
||||||
|
export * from "./Reload.tsx";
|
||||||
export * from "./Script.tsx";
|
export * from "./Script.tsx";
|
||||||
export * from "./StackBackward.tsx";
|
export * from "./StackBackward.tsx";
|
||||||
export * from "./StackForward.tsx";
|
export * from "./StackForward.tsx";
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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 type { ListItem } from "#/dev/ItemList.tsx";
|
||||||
import {
|
import {
|
||||||
ArrowAutofitHeightIcon,
|
ArrowAutofitHeightIcon,
|
||||||
ArrowAutofitWidthIcon,
|
ArrowAutofitWidthIcon,
|
||||||
@@ -13,6 +14,7 @@ import {
|
|||||||
import type { Entity } from "#/engine/Entity";
|
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 { 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();
|
||||||
@@ -169,6 +171,8 @@ const DevTools: Component<{ gameContainer: HTMLElement }> = ({
|
|||||||
|
|
||||||
<FileBrowser root={scenes} handler={setCurrentScene} />
|
<FileBrowser root={scenes} handler={setCurrentScene} />
|
||||||
<FileBrowser root={entities} handler={setActiveEntity} />
|
<FileBrowser root={entities} handler={setActiveEntity} />
|
||||||
|
|
||||||
|
<RemoteItemList fetchItems={async () => []} />
|
||||||
</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