Merge branch 'isolate_editor'
This commit is contained in:
commit
acaf757ce3
@ -1,12 +1,12 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Editor from "./Editor";
|
import Explorer from "./Explorer";
|
||||||
import styles from "./App.module.css";
|
import styles from "./App.module.css";
|
||||||
import "./reset.css";
|
import "./reset.css";
|
||||||
|
|
||||||
function App({}) {
|
function App({}) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.App}>
|
<div className={styles.App}>
|
||||||
<Editor />
|
<Explorer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,4 @@
|
|||||||
.Editor {
|
.EditorTextWrapper {
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Editor-textwrapper {
|
|
||||||
flex: 1;
|
|
||||||
flex-basis: 0;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
background: white;
|
background: white;
|
||||||
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas,
|
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas,
|
||||||
@ -17,8 +8,8 @@
|
|||||||
text-align: initial;
|
text-align: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Editor-textarea,
|
.EditorTextArea,
|
||||||
.Editor-underlay {
|
.EditorUnderlay {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-weight: inherit;
|
font-weight: inherit;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
@ -28,7 +19,7 @@
|
|||||||
line-height: normal;
|
line-height: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Editor-textarea {
|
.EditorTextArea {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -43,7 +34,7 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Editor-underlay {
|
.EditorUnderlay {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@ -52,7 +43,7 @@
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
|
||||||
.highlighted {
|
.EditorHighlighted {
|
||||||
background: #ffff00;
|
background: #ffff00;
|
||||||
}
|
}
|
||||||
}
|
}
|
4
src/Editor.module.css.d.ts
vendored
Normal file
4
src/Editor.module.css.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export const EditorTextWrapper: string;
|
||||||
|
export const EditorTextArea: string;
|
||||||
|
export const EditorUnderlay: string;
|
||||||
|
export const EditorHighlighted: string;
|
@ -1,68 +1,25 @@
|
|||||||
import React, { ReactNode, useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import "./Editor.css";
|
|
||||||
import { Highlight } from "./highlight";
|
import { Highlight } from "./highlight";
|
||||||
import { buildShadow } from "./shadow";
|
import { buildShadow } from "./shadow";
|
||||||
import OrgAst, { OrgNodeReference } from "./OrgAst";
|
import styles from "./Editor.module.css";
|
||||||
import { parse_org } from "../../organic/target/wasm32-unknown-unknown/js/wasm";
|
|
||||||
|
|
||||||
const default_org_source: string = `* Welcome to the Organic Ast Explorer!
|
interface EditorProps {
|
||||||
|
value: string;
|
||||||
Type your Org [fn:1] source in this text box, and it will be parsed by Organic [fn:2] that has been compiled into wasm and embedded in this page. The resulting AST will be rendered to the right.
|
setValue: Function;
|
||||||
|
highlights: Highlight[];
|
||||||
In the AST on the right, you can:
|
clearHighlights: Function;
|
||||||
|
}
|
||||||
1. Click on an AST node to highlight the corresponding portion of the Org source on the left.
|
function Editor({
|
||||||
2. Expand/Collapse the children, properties, and standard properties.
|
value,
|
||||||
|
setValue,
|
||||||
* Footnotes
|
highlights,
|
||||||
|
clearHighlights,
|
||||||
[fn:1] https://orgmode.org/
|
}: EditorProps): React.ReactNode {
|
||||||
|
|
||||||
[fn:2] https://code.fizz.buzz/talexander/organic
|
|
||||||
`;
|
|
||||||
|
|
||||||
function Editor({ defaultValue = default_org_source }) {
|
|
||||||
function handleChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
|
function handleChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
|
||||||
setValue(event.target.value);
|
setValue(event.target.value);
|
||||||
clearHighlights();
|
clearHighlights();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [value, setValue] = useState(defaultValue);
|
|
||||||
|
|
||||||
const [highlights, setHighlights] = useState<Array<Highlight>>([]);
|
|
||||||
|
|
||||||
const astTree = useMemo(() => {
|
|
||||||
const astTree = parse_org(value);
|
|
||||||
console.log(JSON.stringify(astTree));
|
|
||||||
return astTree;
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
function setHighlight(nodes: OrgNodeReference[]) {
|
|
||||||
let new_highlights = nodes.map((node: OrgNodeReference) => {
|
|
||||||
return new Highlight(node.start - 1, node.end - 1);
|
|
||||||
});
|
|
||||||
new_highlights.sort(function (a, b) {
|
|
||||||
if (a.start < b.start) return -1;
|
|
||||||
if (a.start > b.start) return 1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
setHighlights(new_highlights);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addHighlight(start: number, end: number) {
|
|
||||||
let new_highlights = [...highlights, new Highlight(start, end)];
|
|
||||||
new_highlights.sort(function (a, b) {
|
|
||||||
if (a.start < b.start) return -1;
|
|
||||||
if (a.start > b.start) return 1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
setHighlights(new_highlights);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearHighlights() {
|
|
||||||
setHighlights([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const shadowRef = useRef<HTMLDivElement>(null);
|
const shadowRef = useRef<HTMLDivElement>(null);
|
||||||
function onTextAreaScroll() {
|
function onTextAreaScroll() {
|
||||||
@ -78,26 +35,18 @@ function Editor({ defaultValue = default_org_source }) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Editor">
|
<div className={styles.EditorTextWrapper}>
|
||||||
<div className="Editor-textwrapper">
|
<textarea
|
||||||
<textarea
|
ref={textAreaRef}
|
||||||
ref={textAreaRef}
|
onChange={handleChange}
|
||||||
onChange={handleChange}
|
className={styles.EditorTextArea}
|
||||||
className="Editor-textarea"
|
|
||||||
value={value}
|
|
||||||
onScroll={onTextAreaScroll}
|
|
||||||
/>
|
|
||||||
<div ref={shadowRef} className="Editor-underlay">
|
|
||||||
{buildShadow(highlights, value)}
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<OrgAst
|
|
||||||
setHighlight={setHighlight}
|
|
||||||
clearHighlights={clearHighlights}
|
|
||||||
value={value}
|
value={value}
|
||||||
astTree={astTree}
|
onScroll={onTextAreaScroll}
|
||||||
/>
|
/>
|
||||||
|
<div ref={shadowRef} className={styles.EditorUnderlay}>
|
||||||
|
{buildShadow(highlights, value)}
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
11
src/Explorer.module.css
Normal file
11
src/Explorer.module.css
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
.Explorer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 1;
|
||||||
|
flex-basis: 0;
|
||||||
|
}
|
||||||
|
}
|
1
src/Explorer.module.css.d.ts
vendored
Normal file
1
src/Explorer.module.css.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const Explorer: string;
|
83
src/Explorer.tsx
Normal file
83
src/Explorer.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import React, { useMemo, useState } from "react";
|
||||||
|
import styles from "./Explorer.module.css";
|
||||||
|
import { Highlight } from "./highlight";
|
||||||
|
import OrgAst, { OrgNodeReference } from "./OrgAst";
|
||||||
|
import { parse_org } from "../../organic/target/wasm32-unknown-unknown/js/wasm";
|
||||||
|
import Editor from "./Editor";
|
||||||
|
|
||||||
|
const default_org_source: string = `* Welcome to the Organic Ast Explorer!
|
||||||
|
|
||||||
|
Type your Org [fn:1] source in this text box, and it will be parsed by Organic [fn:2] that has been compiled into wasm and embedded in this page. The resulting AST will be rendered to the right.
|
||||||
|
|
||||||
|
In the AST on the right, you can:
|
||||||
|
|
||||||
|
1. Click on an AST node to highlight the corresponding portion of the Org source on the left.
|
||||||
|
2. Expand/Collapse the children, properties, and standard properties.
|
||||||
|
|
||||||
|
* Footnotes
|
||||||
|
|
||||||
|
[fn:1] https://orgmode.org/
|
||||||
|
|
||||||
|
[fn:2] https://code.fizz.buzz/talexander/organic
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface ExplorerProps {
|
||||||
|
defaultValue?: string;
|
||||||
|
}
|
||||||
|
function Explorer({ defaultValue = default_org_source }: ExplorerProps) {
|
||||||
|
const [value, setValue] = useState(defaultValue);
|
||||||
|
|
||||||
|
const [highlights, setHighlights] = useState<Array<Highlight>>([]);
|
||||||
|
|
||||||
|
const astTree = useMemo(() => {
|
||||||
|
const astTree = parse_org(value);
|
||||||
|
console.log(JSON.stringify(astTree));
|
||||||
|
return astTree;
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
function setHighlight(nodes: OrgNodeReference[]) {
|
||||||
|
let new_highlights = nodes.map((node: OrgNodeReference) => {
|
||||||
|
return new Highlight(node.start - 1, node.end - 1);
|
||||||
|
});
|
||||||
|
new_highlights.sort(function (a, b) {
|
||||||
|
if (a.start < b.start) return -1;
|
||||||
|
if (a.start > b.start) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
setHighlights(new_highlights);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHighlight(start: number, end: number) {
|
||||||
|
let new_highlights = [...highlights, new Highlight(start, end)];
|
||||||
|
new_highlights.sort(function (a, b) {
|
||||||
|
if (a.start < b.start) return -1;
|
||||||
|
if (a.start > b.start) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
setHighlights(new_highlights);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearHighlights() {
|
||||||
|
setHighlights([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.Explorer}>
|
||||||
|
<Editor
|
||||||
|
value={value}
|
||||||
|
setValue={setValue}
|
||||||
|
highlights={highlights}
|
||||||
|
clearHighlights={clearHighlights}
|
||||||
|
/>
|
||||||
|
<OrgAst
|
||||||
|
setHighlight={setHighlight}
|
||||||
|
clearHighlights={clearHighlights}
|
||||||
|
value={value}
|
||||||
|
astTree={astTree}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Explorer;
|
@ -1,5 +1,4 @@
|
|||||||
.OrgAst {
|
.OrgAst {
|
||||||
flex: 1;
|
|
||||||
background: #eeeeee;
|
background: #eeeeee;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { ReactNode, useState } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { Highlight } from "./highlight";
|
import { Highlight } from "./highlight";
|
||||||
|
import styles from "./Editor.module.css";
|
||||||
|
|
||||||
function buildShadow(highlights: Highlight[], text: string): ReactNode[] {
|
function buildShadow(highlights: Highlight[], text: string): ReactNode[] {
|
||||||
let output: ReactNode[] = [];
|
let output: ReactNode[] = [];
|
||||||
@ -22,7 +23,7 @@ function buildShadow(highlights: Highlight[], text: string): ReactNode[] {
|
|||||||
} else if (state == ShadowState.Highlight && !thisCharHighlighted) {
|
} else if (state == ShadowState.Highlight && !thisCharHighlighted) {
|
||||||
// End the span
|
// End the span
|
||||||
output.push(
|
output.push(
|
||||||
<span key={i} className="highlighted">
|
<span key={i} className={styles.EditorHighlighted}>
|
||||||
{buffer}
|
{buffer}
|
||||||
</span>,
|
</span>,
|
||||||
);
|
);
|
||||||
@ -37,7 +38,7 @@ function buildShadow(highlights: Highlight[], text: string): ReactNode[] {
|
|||||||
output.push(buffer);
|
output.push(buffer);
|
||||||
} else if (state == ShadowState.Highlight) {
|
} else if (state == ShadowState.Highlight) {
|
||||||
output.push(
|
output.push(
|
||||||
<span key={i} className="highlighted">
|
<span key={i} className={styles.EditorHighlighted}>
|
||||||
{buffer}
|
{buffer}
|
||||||
</span>,
|
</span>,
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user