fix: style
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
Braces,
|
||||||
Download,
|
Download,
|
||||||
Expand,
|
Expand,
|
||||||
Keyboard,
|
Keyboard,
|
||||||
@@ -11,12 +12,13 @@ import {
|
|||||||
Save,
|
Save,
|
||||||
Undo2,
|
Undo2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import type { TransformMode } from "@/types/editor";
|
import type { MapNode, TransformMode } from "@/types/editor";
|
||||||
|
|
||||||
interface EditorControlsProps {
|
interface EditorControlsProps {
|
||||||
transformMode: TransformMode;
|
transformMode: TransformMode;
|
||||||
onTransformModeChange: (mode: TransformMode) => void;
|
onTransformModeChange: (mode: TransformMode) => void;
|
||||||
selectedNodeIndex: number | null;
|
selectedNodeIndex: number | null;
|
||||||
|
mapNodes: MapNode[];
|
||||||
nodesCount: number;
|
nodesCount: number;
|
||||||
selectedNodeName: string | null;
|
selectedNodeName: string | null;
|
||||||
undoCount: number;
|
undoCount: number;
|
||||||
@@ -33,6 +35,7 @@ export function EditorControls({
|
|||||||
transformMode,
|
transformMode,
|
||||||
onTransformModeChange,
|
onTransformModeChange,
|
||||||
selectedNodeIndex,
|
selectedNodeIndex,
|
||||||
|
mapNodes,
|
||||||
nodesCount,
|
nodesCount,
|
||||||
selectedNodeName,
|
selectedNodeName,
|
||||||
undoCount,
|
undoCount,
|
||||||
@@ -46,6 +49,7 @@ export function EditorControls({
|
|||||||
}: EditorControlsProps): React.JSX.Element {
|
}: EditorControlsProps): React.JSX.Element {
|
||||||
const cameraPosition = [0, 50, 100];
|
const cameraPosition = [0, 50, 100];
|
||||||
const viewModeLabel = isPlayerMode ? "View locked" : "Lock view";
|
const viewModeLabel = isPlayerMode ? "View locked" : "Lock view";
|
||||||
|
const jsonPreview = getJsonPreview(mapNodes, selectedNodeIndex);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -230,7 +234,100 @@ export function EditorControls({
|
|||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="editor-json-section" aria-labelledby="json-heading">
|
||||||
|
<div className="editor-section-heading">
|
||||||
|
<h3 id="json-heading">JSON</h3>
|
||||||
|
<span>{jsonPreview.label}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pre className="editor-json-view" aria-label={jsonPreview.label}>
|
||||||
|
{jsonPreview.lines.map((line) => (
|
||||||
|
<code
|
||||||
|
key={line.number}
|
||||||
|
className={line.isSelected ? "is-selected" : undefined}
|
||||||
|
>
|
||||||
|
<span>{line.number}</span>
|
||||||
|
{line.content || " "}
|
||||||
|
</code>
|
||||||
|
))}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<div className="editor-json-hint">
|
||||||
|
<Braces size={14} aria-hidden="true" />
|
||||||
|
{selectedNodeIndex === null
|
||||||
|
? "Raw map JSON"
|
||||||
|
: `Selected node ${selectedNodeIndex + 1} raw lines`}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</aside>
|
</aside>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface JsonPreviewLine {
|
||||||
|
number: number;
|
||||||
|
content: string;
|
||||||
|
isSelected: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface JsonPreview {
|
||||||
|
label: string;
|
||||||
|
lines: JsonPreviewLine[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJsonPreview(
|
||||||
|
mapNodes: MapNode[],
|
||||||
|
selectedNodeIndex: number | null,
|
||||||
|
): JsonPreview {
|
||||||
|
const { lines, ranges } = formatMapNodesWithRanges(mapNodes);
|
||||||
|
|
||||||
|
if (selectedNodeIndex === null || !ranges[selectedNodeIndex]) {
|
||||||
|
return {
|
||||||
|
label: `${lines.length} raw lines`,
|
||||||
|
lines: lines.map((content, index) => ({
|
||||||
|
number: index + 1,
|
||||||
|
content,
|
||||||
|
isSelected: false,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const range = ranges[selectedNodeIndex];
|
||||||
|
const selectedLines = lines.slice(range.start - 1, range.end);
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: `Lines ${range.start}-${range.end}`,
|
||||||
|
lines: selectedLines.map((content, index) => ({
|
||||||
|
number: range.start + index,
|
||||||
|
content,
|
||||||
|
isSelected: true,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMapNodesWithRanges(mapNodes: MapNode[]): {
|
||||||
|
lines: string[];
|
||||||
|
ranges: Array<{ start: number; end: number }>;
|
||||||
|
} {
|
||||||
|
const lines = ["["];
|
||||||
|
const ranges: Array<{ start: number; end: number }> = [];
|
||||||
|
|
||||||
|
mapNodes.forEach((node, index) => {
|
||||||
|
const objectLines = JSON.stringify(node, null, 2)
|
||||||
|
.split("\n")
|
||||||
|
.map((line) => ` ${line}`);
|
||||||
|
|
||||||
|
if (index < mapNodes.length - 1) {
|
||||||
|
objectLines[objectLines.length - 1] += ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = lines.length + 1;
|
||||||
|
lines.push(...objectLines);
|
||||||
|
ranges.push({ start, end: lines.length });
|
||||||
|
});
|
||||||
|
|
||||||
|
lines.push("]");
|
||||||
|
|
||||||
|
return { lines, ranges };
|
||||||
|
}
|
||||||
|
|||||||
@@ -262,6 +262,8 @@ canvas {
|
|||||||
border-radius: 28px;
|
border-radius: 28px;
|
||||||
box-shadow: 0 24px 90px rgba(0, 0, 0, 0.45);
|
box-shadow: 0 24px 90px rgba(0, 0, 0, 0.45);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
backdrop-filter: blur(22px);
|
backdrop-filter: blur(22px);
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #3a3a3a transparent;
|
scrollbar-color: #3a3a3a transparent;
|
||||||
@@ -541,6 +543,81 @@ canvas {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-json-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 240px;
|
||||||
|
padding: 14px 12px 12px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.09);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-json-view {
|
||||||
|
flex: 1;
|
||||||
|
max-height: 320px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 8px 0;
|
||||||
|
overflow: auto;
|
||||||
|
background: #050505;
|
||||||
|
border: 1px solid #1f1f1f;
|
||||||
|
border-radius: 16px;
|
||||||
|
color: #d7d7d7;
|
||||||
|
font-family: "SFMono-Regular", "Courier New", monospace;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #3a3a3a transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-json-view::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-json-view::-webkit-scrollbar-thumb {
|
||||||
|
background: #3a3a3a;
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-json-view code {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 34px max-content;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 100%;
|
||||||
|
padding: 0 12px;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
font-family: inherit;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-json-view code span {
|
||||||
|
color: #5f5f5f;
|
||||||
|
text-align: right;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-json-view code.is-selected {
|
||||||
|
background: #111111;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-json-view code.is-selected * {
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-json-view code.is-selected span {
|
||||||
|
color: #8a8a8a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-json-hint {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #8d8d8d;
|
||||||
|
font-size: 0.74rem;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.editor-error h2 {
|
.editor-error h2 {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
@@ -567,4 +644,8 @@ canvas {
|
|||||||
max-height: 46vh;
|
max-height: 46vh;
|
||||||
border-radius: 22px;
|
border-radius: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-json-section {
|
||||||
|
min-height: 180px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ export function EditorPage(): React.JSX.Element {
|
|||||||
transformMode={transformMode}
|
transformMode={transformMode}
|
||||||
onTransformModeChange={handleTransformModeChange}
|
onTransformModeChange={handleTransformModeChange}
|
||||||
selectedNodeIndex={selectedNodeIndex}
|
selectedNodeIndex={selectedNodeIndex}
|
||||||
|
mapNodes={sceneData.mapNodes}
|
||||||
nodesCount={sceneData.mapNodes.length}
|
nodesCount={sceneData.mapNodes.length}
|
||||||
selectedNodeName={
|
selectedNodeName={
|
||||||
selectedNodeIndex !== null && sceneData.mapNodes[selectedNodeIndex]
|
selectedNodeIndex !== null && sceneData.mapNodes[selectedNodeIndex]
|
||||||
|
|||||||
Reference in New Issue
Block a user