Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9e69051
Add NewTopBlock
Yxwww Aug 19, 2022
c9add62
Add new top block nodeView
Yxwww Aug 22, 2022
03e44c4
Refactor schema naming
Yxwww Aug 23, 2022
7ef537f
Add redux state in new block
Yxwww Aug 24, 2022
caaf4ea
Use decoration for selected block state
Yxwww Aug 25, 2022
882181e
Add ctrl-enter handling for block nodes
Yxwww Aug 26, 2022
685bd0c
Add block nodeview click on to select the block node
Yxwww Aug 26, 2022
644d532
Improve block nodeview selection handling
Yxwww Aug 26, 2022
6a54b8d
Ignore Dropdown toggle mutations
Yxwww Aug 30, 2022
dd3e3c3
Fix ignoreMutation and toggleButonRef
Yxwww Aug 30, 2022
7a9a9a8
Click on block control ui should select block
Yxwww Aug 30, 2022
74cad98
Update demo body margin and padding to 0
Yxwww Aug 30, 2022
1f17b35
Add move block up and down behavior to block control
Yxwww Aug 31, 2022
72efb75
Add copy tex/markdown options
Yxwww Aug 31, 2022
171a678
Ignore dist type in tsconfig
Yxwww Sep 1, 2022
c0658c7
Add plugin to options
Yxwww Sep 2, 2022
c7613ae
Remove state plugin from getPlugins
Yxwww Sep 2, 2022
783e086
Improve split block
Yxwww Sep 6, 2022
a9664ac
Fix block schema
Yxwww Sep 6, 2022
1a7bab5
Add toHTMLAsNode
Yxwww Sep 6, 2022
9e5efca
Add block to id collision avoidance logic
Yxwww Sep 6, 2022
2dad6d2
Add createEditor
Yxwww Sep 8, 2022
d47d16f
Add redux integration to createEditor
Yxwww Sep 9, 2022
0e5f90a
Add registerEdtiorState to createEdtior
Yxwww Sep 9, 2022
b14dd8b
Improve block split
Yxwww Sep 10, 2022
cae44eb
Refactor schemas with parameterized nodeGroup
Yxwww Sep 13, 2022
35f3a61
Use article schema in createEditor
Yxwww Sep 13, 2022
a7a7a72
Revive tex and plaintext example in editor
Yxwww Sep 13, 2022
a9b1f4e
Improve editor demo list rendering
Yxwww Sep 13, 2022
82298dc
Change to use react-router v5
Yxwww Sep 13, 2022
c415856
Fix callout lift not working
Yxwww Sep 14, 2022
c32d120
Ensure code inlineAction to delete figure if it exists
Yxwww Sep 14, 2022
8b87566
Fix caption enter handling
Yxwww Sep 16, 2022
5063ba9
Add test to figcaption
Yxwww Sep 16, 2022
e24cd72
Export deleteNode pm utility
Yxwww Sep 21, 2022
60339cd
update yarn.lock
Yxwww Sep 21, 2022
b139110
Fix figure tests
Yxwww Sep 21, 2022
9a51eff
Fix schema
Yxwww Sep 21, 2022
410cfb5
Improve figure test stability
Yxwww Sep 21, 2022
79d53bb
Optionally turn off callout inline action
rowanc1 Sep 22, 2022
cd7df50
⌨️ Types linting
rowanc1 Sep 26, 2022
f1dbeb9
😎 Node selection if there is a node!
rowanc1 Sep 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,230 changes: 1,199 additions & 31 deletions package-lock.json

Large diffs are not rendered by default.

65 changes: 65 additions & 0 deletions packages/editor/cypress/component/figure.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';
import { render, waitFor } from './utils';
import userEvent from '@testing-library/user-event';
import { createStore, DemoEditor } from '../../demo/NewEditorDemo';

// TODO: abstract these they are copy pasted from demo since the setup logic is shared
const stateKey = 'myEditor';
const viewId1 = 'view1';

const TABLE_CONTENT = `<table> <tr> <th>Header 1</th> <th>Header 2</th> <th>Header 3</th> </tr> <tr> <td>Data over here</td> <td colspan="2">Multiple columns!</td> </tr> </tr> <td>4</td> <td>5</td> <td>6</td> </tr> </table>`;
const FIGURE_CONTENT = `
<pre language="typescript" linenumbers><code>function sayHello() {
console.log("Hello editing!");
}</code></pre>
`;
const IMAGE_CONTENT =
'<img src="https://curvenote.dev/images/logo.png" align="center" width="70%" />';
const IFRAME_CONTENT =
'<iframe src="https://www.loom.com/embed/524085f9c64e4652a12bd81a374d58df" align="center" width="70%" ></iframe>';
function generateFigureSnippet(content: string) {
const SNIPPET_WITH_TABLE = `
<div class="block" id="first">
<figure numbered id="fig1">
${content}
<figcaption kind="fig"><p>this is caption!</p></figcaption>
</figure>
</div>
`;
return SNIPPET_WITH_TABLE;
}
const TABLE_FIGURE_SNIPPET = generateFigureSnippet(TABLE_CONTENT);
const CODEBLOCK_FIGURE_SNIPPET = generateFigureSnippet(TABLE_CONTENT);
const IMAGE_FIGURE_SNIPPET = generateFigureSnippet(IMAGE_CONTENT);
const IFRAME_FIGURE_SNIPPET = generateFigureSnippet(IFRAME_CONTENT);

it('should render curvenote link with the correct attributes', () => {
const { container } = render(<DemoEditor content={TABLE_FIGURE_SNIPPET} />);
expect(container.querySelector('table')).to.be.instanceOf(HTMLTableElement);
expect(container.querySelector('figcaption')?.innerHTML).to.be.eq('this is caption!');
});

[
TABLE_FIGURE_SNIPPET,
CODEBLOCK_FIGURE_SNIPPET,
IMAGE_FIGURE_SNIPPET,
IFRAME_FIGURE_SNIPPET,
].forEach((snippet) => {
it('should create one new paragraph below the figure if hit enter within figure caption', async () => {
const store = createStore();
const { container } = render(
<DemoEditor content={snippet} store={store} stateKey={stateKey} viewId={viewId1} />,
);
await waitFor(() => {
expect(container.querySelector('figcaption')).to.be.ok;
})
const figcaption = container.querySelector('figcaption') as HTMLElement;
userEvent.click(figcaption);
await userEvent.keyboard('hello{Enter}{Enter}', { delay: 10 });
await waitFor(() => {
expect(
container.querySelectorAll('#first > div')[1]?.querySelectorAll(':scope > p').length, // there should be one and only one paragraph gets created;
).to.eq(1);
});
});
});
148 changes: 148 additions & 0 deletions packages/editor/demo/NewEditorDemo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React, { useEffect, useMemo, useState } from 'react';
import { Provider } from 'react-redux';
import { createTheme } from '@material-ui/core';
import { ReferenceKind } from '@curvenote/schema';
import { Fragment } from 'prosemirror-model';
import type { Store, LinkResult } from '../src';
import {
middleware,
EditorMenu,
Suggestions,
actions,
createEditor,
Attributes,
InlineActions,
} from '../src';
import SuggestionSwitch from '../src/components/Suggestion/Switch';
import InlineActionSwitch from '../src/components/InlineActions/Switch';
import Editor from '../src/components/NextEditor';
import rootReducer from './reducers';
import 'codemirror/lib/codemirror.css';
import '../styles/index.scss';
import 'sidenotes/dist/sidenotes.css';
import type { Options } from '../src/connect';
import { configureStore } from '@reduxjs/toolkit';

declare global {
interface Window {
[index: string]: any;
}
}

const DEFAULT_STATE_KEY = 'myEditor';
const viewId1 = 'view1';
const docId = 'docId';
const someLinks: LinkResult[] = [
{
kind: ReferenceKind.cite,
uid: 'simpeg2015',
label: 'simpeg',
content: 'Cockett et al., 2015',
title:
'SimPEG: An open source framework for simulation and gradient based parameter estimation in geophysical applications.',
},
{
kind: ReferenceKind.link,
uid: 'https://curvenote.com',
label: null,
content: 'Curvenote',
title: 'Move ideas forward',
},
];

export function createStore(): Store {
return configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware: any) => {
return [
...middleware,
...getDefaultMiddleware({
serializableCheck: false,
immutableCheck: false,
}),
];
},
});
}

const theme = createTheme({});
function createOptions(store: Store) {
const newComment = () => {
store.dispatch(actions.addCommentToSelectedView('sidenote1'));
};
const removeComment = () => {
store.dispatch(actions.removeComment(viewId1, 'sidenote1'));
};
return {
transformKeyToId: (key) => key,
uploadImage: async (file) => {
// eslint-disable-next-line no-console
console.log(file);
return new Promise((resolve) =>
setTimeout(() => resolve('https://curvenote.dev/images/logo.png'), 2000),
);
},
getDocId() {
return docId;
},
addComment() {
newComment();
return true;
},
onDoubleClick(stateId, viewId) {
// eslint-disable-next-line no-console
console.log('Double click', stateId, viewId);
return false;
},
theme,
citationPrompt: async () => [
{
key: 'simpeg2015',
kind: ReferenceKind.cite,
text: 'Cockett et al, 2015',
label: 'simpeg',
title: '',
},
],
createLinkSearch: async () => ({ search: () => someLinks }),
getCaptionFragment: (schema) => Fragment.fromArray([schema.text('Hello caption world!')]),
nodeViews: {},
} as Options;
}

export function DemoEditor({
content,
store = createStore(),
stateKey = DEFAULT_STATE_KEY,
viewId = viewId1,
}: {
content: string;
store?: Store;
viewId?: string;
stateKey?: string;
}) {
const editor = useMemo(() => createEditor(store, createOptions(store)), []);
useEffect(() => {
window.store = store;
return () => {
window.store = undefined;
};
}, [store]);
return (
<Provider store={store}>
<React.StrictMode>
<article id={docId} className="content centered">
<EditorMenu standAlone />
<InlineActions>
<InlineActionSwitch />
</InlineActions>
<Editor editor={editor} stateKey={stateKey} viewId={viewId} initialContent={content} />
<Suggestions>
<SuggestionSwitch />
</Suggestions>
<Attributes />
</article>
</React.StrictMode>
</Provider>
);
}
2 changes: 1 addition & 1 deletion packages/editor/demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
crossorigin="anonymous"
/>
</head>
<body>
<body style="margin: 0px; padding: 0;">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div class="centered">
<h1>@curvenote/editor</h1>
Expand Down
55 changes: 53 additions & 2 deletions packages/editor/demo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,57 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { DemoEditor } from './init';
import snippet from './snippet';
import { DemoEditor as NextEditor } from './NewEditorDemo';
import snippet, { snippetNext } from './snippet';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import { styled } from '@stitches/react';

ReactDOM.render(<DemoEditor content={snippet} />, document.getElementById('root'));
const Container = styled('div', {
margin: 'auto',
width: 800,
});
const Ul = styled('ul', {
listStyle: 'none',
fontSize: 24,
marginTop: 16,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
li: {
padding: 16,
},
});

function App() {
return (
<>
<Container>
<nav>
<Ul>
<li>
<Link to="/">Legacy</Link>
</li>
<li>
<Link to="/article">Article</Link>
</li>
</Ul>
</nav>

<Switch>
<Route exact path="/">
<DemoEditor content={snippet} />
</Route>
<Route exact path="/article">
<NextEditor content={snippetNext} />
</Route>
</Switch>
</Container>
</>
);
}
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root'),
);
21 changes: 6 additions & 15 deletions packages/editor/demo/init.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,14 @@ import { Button, createTheme } from '@material-ui/core';
import { toHTML, toMarkdown, toTex, ReferenceKind, process, toText } from '@curvenote/schema';
import { Sidenote, AnchorBase } from 'sidenotes';
import { Fragment } from 'prosemirror-model';
import {
actions,
Editor,
EditorMenu,
Store,
setup,
Suggestions,
Attributes,
InlineActions,
LinkResult,
} from '../src';
import type { Store, LinkResult } from '../src';
import { actions, Editor, EditorMenu, setup, Suggestions, Attributes, InlineActions } from '../src';
import rootReducer from './reducers';
import middleware from './middleware';
import 'codemirror/lib/codemirror.css';
import '../styles/index.scss';
import 'sidenotes/dist/sidenotes.css';
import { Options } from '../src/connect';
import type { Options } from '../src/connect';
import SuggestionSwitch from '../src/components/Suggestion/Switch';
import InlineActionSwitch from '../src/components/InlineActions/Switch';

Expand Down Expand Up @@ -142,15 +133,15 @@ export function DemoEditor({ content, store = createStore() }: { content: string
}
// Update the counter
const counts = process.countState(editor.state);
const words = process.countWords(editor.state);
// const words = process.countWords(editor.state);
const updates = {
'count-sec': `${counts.sec.all.length} (${counts.sec.total})`,
'count-fig': `${counts.fig.all.length} (${counts.fig.total})`,
'count-eq': `${counts.eq.all.length} (${counts.eq.total})`,
'count-code': `${counts.code.all.length} (${counts.code.total})`,
'count-table': `${counts.table.all.length} (${counts.table.total})`,
'count-words': `${words.words}`,
'count-char': `${words.characters_including_spaces} (${words.characters_excluding_spaces})`,
// 'count-words': `${words.words}`,
// 'count-char': `${words.characters_including_spaces} (${words.characters_excluding_spaces})`,
};
Object.entries(updates).forEach(([key, count]) => {
const el = document.getElementById(key);
Expand Down
3 changes: 2 additions & 1 deletion packages/editor/demo/reducers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import runtime from '@curvenote/runtime';
import * as sidenotes from 'sidenotes';
import { combineReducers } from 'redux';
import { State, EditorActions, Reducer, reducer } from '../src';
import type { State, EditorActions, Reducer } from '../src';
import { reducer } from '../src';

const combinedReducers: Reducer = combineReducers({
editor: reducer,
Expand Down
10 changes: 9 additions & 1 deletion packages/editor/demo/snippet.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<h1>Welcome to <code>@curvenote/editor</code>!</h1>
<h1>Welcome to <code>@curvenote/editor</code>!</h1>

<p>
Feel free to ➡️ edit this document ⬅️, or for collaborative editing, check out
<a href="https://curvenote.com">curvenote.com</a>, which is a scientific writing platform that
Expand Down Expand Up @@ -32,6 +33,8 @@ <h1>Welcome to <code>@curvenote/editor</code>!</h1>
<p><code></code> for code or <code>```</code> for a code block!</p>
</li>
</ul>


<p>
You can also configure the editor to show citations, like this one from Geophysics
<cite-group><cite key="simpeg2015" inline="Cockett et al, 2015"></cite></cite-group>. Try
Expand Down Expand Up @@ -81,6 +84,8 @@ <h2>You can insert code:</h2>
<h2>You can insert footnotes:</h2>
<p>This is a footnote<span class="footnote">👋</span>. You can access it using the <code>/footnote</code> command<span class="footnote">Or the menu under the plus!</span></p>



<aside class="callout info">
<p>
Speaking of commands! Try starting with a forward slash <code>/</code> after this callout
Expand Down Expand Up @@ -128,3 +133,6 @@ <h2>Interactivity</h2>
align="center"
width="70%"
></iframe>



2 changes: 2 additions & 0 deletions packages/editor/demo/snippet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import html from './snippet.html';
import nextSnippet from './snippetNext.html';

const snippet = html;
export const snippetNext = nextSnippet;

export default snippet;
Loading