Skip to content

Commit

Permalink
add line numbers to editor
Browse files Browse the repository at this point in the history
  • Loading branch information
g-harel committed Jan 24, 2019
1 parent 527ce52 commit 47e36d4
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 46 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ TODO
- display uncaught network errors
- app ~ treat 5xx errors differently
- offline page for playing around ("/localhost")
- edit page line numbers
- replace application secrets (eventually)
requirements
Expand Down
3 changes: 2 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"quotemark": [true, "double"],
"ter-arrow-parens": [true, "always"],
"ter-indent": [true, 4],
"variable-name": [true, "allow-pascal-case"]
"variable-name": [true, "allow-pascal-case"],
"no-increment-decrement": false
}
}
7 changes: 4 additions & 3 deletions website/components/input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const Title = styled("span")({
});

const StyledInput = styled("input")({
border: "1px solid #ddd",
border: "1px solid #bbb",
borderRadius: "2px",
height: "1.85rem",
margin: "0.3rem 0.5rem 0",
Expand All @@ -44,7 +44,8 @@ const StyledInput = styled("input")({
width: `${width}rem`,

"&:focus": {
border: "1px solid mediumslateblue",
boxShadow: "0 0 1px 1px #eee",
borderColor: "inherit",
},

...placeholder({
Expand All @@ -67,7 +68,7 @@ const Button = styled("button")({
"&.enabled": {
pointerEvents: "all",
cursor: "pointer",
color: "mediumslateblue",
color: "inherit",
},
});

Expand Down
12 changes: 8 additions & 4 deletions website/components/page/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import {Component} from "../../internal/types";
import {styled} from "../../internal/styled";

const Wrapper = styled("div")({
height: "100%",
});

const ConstructionSign = styled("div")({
backgroundColor: "gold",
borderRadius: "0.3rem",
bottom: "2rem",
Expand Down Expand Up @@ -40,14 +44,14 @@ export const Page: Component<Props> = (props) => () => {
}

return (
<div className="page">
<Wrapper>
{document.location.hostname !== "localhost" && (
<Wrapper>
<ConstructionSign>
<i className="fas fa-tools" />
&nbsp; Under Construction
</Wrapper>
</ConstructionSign>
)}
<Component addr={props.addr} token={props.token} />
</div>
</Wrapper>
);
};
1 change: 0 additions & 1 deletion website/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ html {
body {
height: 100vh;
margin: 0;
width: 100vw;
}

* {
Expand Down
3 changes: 3 additions & 0 deletions website/internal/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ const send = (req: IRequest) => {
} else {
res.text().then(resolve);
}
}).catch((e) => {
console.warn(e.toString());
throw e;
});
};

Expand Down
14 changes: 14 additions & 0 deletions website/pages/document/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import {styled} from "../../internal/styled";

const Wrapper = styled("div")({});

const EditButton = styled("div")({
cursor: "pointer",
display: "inline-block",
float: "right",
userSelect: "none",
padding: "0.85rem 1.4rem",
fontWeight: "bold",
});

const Groups = styled("div")({});

const Group = styled("div")({});
Expand All @@ -20,6 +29,8 @@ export const Document: PageComponent<IPageData> = ({addr}, update) => {
client.page.fetch(update, err, addr);

return (data: IPageData) => {
console.log(data);

// Response not yet received.
if (!data) {
// TODO
Expand All @@ -28,6 +39,9 @@ export const Document: PageComponent<IPageData> = ({addr}, update) => {

return (
<Wrapper>
<EditButton onclick={() => app.redirect(`/${addr}/edit`)}>
edit
</EditButton>
<Groups>
{...data.groups.map((group) => (
<Group>
Expand Down
121 changes: 85 additions & 36 deletions website/pages/edit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ import {styled} from "../../internal/styled";

const editorID = "targetblank-editor";
const headerHeight = "2.9rem";
const saveDelay = 1000;
const lineHeight = "1.6rem";
const editorPadding = "2.2rem";
const saveDelay = 1400;

const Wrapper = styled("div")({});
const Wrapper = styled("div")({
minHeight: "100%",
backgroundColor: "#fafafa",
});

const Header = styled("header")({
backgroundColor: "#fff",
borderBottom: "1px solid #ddd",
height: headerHeight,
padding: "0.85rem 1.4rem",
position: "fixed",
userSelect: "none",
width: "100%",
});

const BackButton = styled("div")({
Expand All @@ -40,38 +48,45 @@ const SavedIcon = styled("i")({
});

const Editor = styled("textarea")({
lineHeight,
backgroundColor: "#fafafa",
border: "none",
fontFamily: "Inconsolata, monospace",
fontSize: "1.2rem",
height: `calc(100% - ${headerHeight})`,
lineHeight: "1.3",
padding: "2.2rem 0 1rem 2.2rem",
marginTop: headerHeight,
outline: "none",
padding: editorPadding,
paddingLeft: `calc(2 * ${editorPadding})`,
resize: "none",
whiteSpace: "pre",
width: "100%",
});

const Lines = styled("div")({
height: 0,
opacity: 0.2,
textAlign: "right",
transform: `translateY(calc(${headerHeight} + ${editorPadding}))`,
userSelect: "none",
width: `calc(1.2 * ${editorPadding})`,
});

const Line = styled("div")({
lineHeight,
});

interface Data {
page: IPageData;
status: "saving" | "saved" | "error";
error?: string;
}

export const Edit: PageComponent<Data> = ({addr}, update) => {
const callback = (data: IPageData) => {
update({
page: data,
status: "saved",
});
};

const err = (message) => {
console.warn(message);
app.redirect(`/${addr}/login`);
};

client.page.fetch(callback, err, addr);
client.page.fetch(
(data: IPageData) => update({page: data, status: "saved"}),
() => app.redirect(`/${addr}/login`),
addr,
);

return (data?: Data) => {
// Response not yet received.
Expand All @@ -80,6 +95,53 @@ export const Edit: PageComponent<Data> = ({addr}, update) => {
return "loading";
}

// Save editor contents after a delay.
let timeout: any = null;
const onInput = (e) => {
update({page: data.page, status: "saving"});
clearTimeout(timeout);
timeout = setTimeout(() => {
client.page.edit(
() => update({page: data.page, status: "saved"}),
(m) => update({page: data.page, status: "error", error: m}),
addr,
e.target.value,
);
}, saveDelay);
};

// Update editor height to match content.
const editor = document.getElementById(editorID);
if (editor) {
editor.style.height = "0";
editor.style.opacity = "1";
editor.style.height = `${editor.scrollHeight + 20}px`;
}

// Create line numbers.
let lines: number[] = [];
if (editor) {
const count = (editor as any).value.split("\n").length;
lines = Array(count);
for (let i = 0; i < count; i++) {
lines[i] = i + 1;
}
}

// Insert spaces when tab is pressed.
const onKeydown = (e) => {
if (e.key === "Tab") {
e.preventDefault();
const {target} = e;
const pos = target.selectionStart;
const before = target.value.substring(0, target.selectionStart);
const after = target.value.substring(target.selectionEnd);
target.value = `${before} ${after}`;
target.selectionEnd = pos + 4;
}
};

// Change status depending on state.
let statusContent: any = null;
if (data.status === "error") {
statusContent = data.error;
Expand All @@ -94,23 +156,6 @@ export const Edit: PageComponent<Data> = ({addr}, update) => {
);
}

let timeout: any = null;
const onChange = (e) => {
const {value} = e.target;
clearTimeout(timeout);
timeout = setTimeout(() => {
update({page: data.page, status: "saving"});

const callback = () => {
update({page: data.page, status: "saved"});
};
const err = (message) => {
update({page: data.page, status: "error", error: message});
};
client.page.edit(callback, err, addr, value);
}, saveDelay);
};

return (
<Wrapper>
<Header>
Expand All @@ -121,10 +166,14 @@ export const Edit: PageComponent<Data> = ({addr}, update) => {
{statusContent}
</Status>
</Header>
<Lines>{...lines.map((n) => <Line>{n}</Line>)}</Lines>
<Editor
id={editorID}
style="opacity: 0;"
value={data.page.raw}
oninput={onChange}
oninput={onInput}
onkeydown={onKeydown}
spellcheck={false}
/>
</Wrapper>
);
Expand Down

0 comments on commit 47e36d4

Please sign in to comment.