Skip to content

Commit

Permalink
update prosemirror dependency & modify the schema & modify the implem…
Browse files Browse the repository at this point in the history
…ent way of slash popup
  • Loading branch information
Xheldon committed Mar 3, 2021
1 parent 7756ba9 commit 1f1b488
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 113 deletions.
8 changes: 8 additions & 0 deletions app/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ class App extends Component<{}, AppState> {
}
}

componentDidMount() {
if (!this.state.editor) {
this.setState({
editor: window.NEDITOR.getEditor()
});
}
}

render() {
const {editor} = this.state;
const Com = connect((state: StateType) => {
Expand Down
43 changes: 32 additions & 11 deletions app/components/slash/slash-popup.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import React, { ReactElement } from 'react';
import { EditorView } from 'prosemirror-view';
import { EditorState } from 'prosemirror-state';
import { Node } from 'prosemirror-model';

import { slashPopupPluginKey } from '@modules/slash/plugin';
import { insertlist, insertparagraph } from '@commands';
import { StateType } from '@redux/interface';

import './style.scss';
import { compose } from 'redux';
import {connect} from 'react-redux';
import { withEditor } from '@components/context';

// make a list which has a title property to filter, a content to show and a handler to response;

Expand Down Expand Up @@ -44,32 +48,35 @@ const actionList = [
}
];

function SlashPopupView(view: EditorView, prevState: EditorState, reduxState: StateType): ReactElement {
console.log('按下箭头, 能否执行到此?');
let state = view.state; // new state;
function SlashPopupComponent(props: any) {
console.log('props:', props);
const {editor, editor: {view}, from, to, text, prevState, newState} = props;

let state = newState; // new state;
if (
prevState && prevState.doc.eq(state.doc)
&& prevState.selection.eq(state.selection)
) {
return;
return null;
}
let meta = slashPopupPluginKey.getState(state);
let style = null;
if (meta) {
let pos = view.coordsAtPos(meta.start);
if (from && to) {
let pos = view.coordsAtPos(from);
style = {
left: pos.left + 'px',
top: pos.bottom + 'px'
};
let filterText = meta.filterText && meta.filterText.slice(1) || '';
//Note: use new state to get the filterText
let filterText = state.doc.textBetween(from, to).slice(1);
console.log('filterText:', filterText);
let childList: ReactElement[] = actionList.map((arg, k) => {
if ((new RegExp(`${filterText}`, 'g')).test(arg.title)) {
// const isActive = k === 0 || reduxState.popup.options.currentSelect === arg.title;
return <div className={false ? 'active' : ''} key={arg.title} onClick={arg.handler.bind(null, {
view,
options: {
start: meta.start,
end: meta.end
start: from,
end: to
}
})}>{arg.content()}</div>
}
Expand All @@ -89,4 +96,18 @@ function SlashPopupView(view: EditorView, prevState: EditorState, reduxState: St
return null;
}

export default SlashPopupView;
// TODO: state type
const mapStateToProps = (state: any) => {
return {
from: state.popup.options.from,
to: state.popup.options.to,
text: state.popup.options.text,
prevState: state.popup.options.prevState,
newState: state.popup.options.newState,
}
};

export default compose(
withEditor,
connect(mapStateToProps),
)(SlashPopupComponent);
1 change: 1 addition & 0 deletions app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Provider } from 'react-redux';
import ReactRootApp from '@components/index';
import Editor from './editor';

// import 'prosemirror-view/dist/style/prosemirror.css';
import './style.scss';

declare global {
Expand Down
84 changes: 61 additions & 23 deletions app/modules/slash/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EditorView } from 'prosemirror-view';
import React from 'react';
import { EditorState, Plugin, PluginKey, Transaction } from 'prosemirror-state';

import { ViewReturn } from '@interfaces';
Expand All @@ -7,49 +7,87 @@ import { View } from '@components/commons/View';
import SlashPopupView from '@components/slash/slash-popup';
import { showPopup, SHOW_SLASH_POPUP } from '@redux/actions';
import { rdxDispatch } from '@redux/store';
import { ReactElement } from 'react';

export const slashPopupPluginKey = new PluginKey('slash-popup');

export const slashPopupPlugin: () => Plugin = () => new Plugin({
key: slashPopupPluginKey,
state: {
init(): any {
return null;
return {
show: false
};
},
apply(tr: Transaction, value: any, oldState: EditorState, newState: EditorState): any {
if (newState.selection.empty) {
// Note: the inputrule trigger the slash input, so we set it into the state directly to make it could update the SlashPlugin view
let meta = tr.getMeta(slashPopupPluginKey);
console.log('meta:', meta);
if (meta) {
return meta;
console.log('meta:', meta, value);
if (!meta) {
return value;
} else {
let old = slashPopupPluginKey.getState(oldState);
if (old) {
if (oldState.selection.from !== newState.selection.from) {
// after typed the 'slash', only should show the pop when the cursor is after the slash, so limit the old start must lower the cursor
if (newState.selection.from - old.start > 9 || newState.selection.from - old.start <= 0) {
return null;
} else {
// 此处 rdxDispatch 一个 action
return {
start: old.start,
end: newState.selection.from,
filterText: newState.doc.textBetween(old.start, newState.selection.from)
};
// Note: last value show && the next meta.show not fasle mean that we are selecting the node to insert now
let state = value;
if (meta.show !== false) {
state = Object.assign({}, value, meta);
console.log('state:', state);
rdxDispatch(showPopup({
type: SHOW_SLASH_POPUP,
component: React.createElement(SlashPopupView, {}, null),
options: {
type: SHOW_SLASH_POPUP,
from: state.from,
to: state.to,
text: state.text,
prevState: oldState,
newState: newState
}
} else {
return {...old};
}
}));
}
return state;
}
// if (meta === '/') {
// // NOte: dispatch rdx & set the state
// return { show: true };
// } else {
// let old = slashPopupPluginKey.getState(oldState);
// if (old) {
// if (oldState.selection.from !== newState.selection.from) {
// // after typed the 'slash', only should show the pop when the cursor is after the slash, so limit the old start must lower the cursor
// if (newState.selection.from - old.start > 9 || newState.selection.from - old.start <= 0) {
// return null;
// } else {
// // 此处 rdxDispatch 一个 action
// return {
// start: old.start,
// end: newState.selection.from,
// filterText: newState.doc.textBetween(old.start, newState.selection.from)
// };
// }
// } else {
// return {...old};
// }
// }
// }
}
}
},
props: {
handleTextInput(view, from, to, text) {
const {state: {tr}, dispatch} = view;
dispatch(tr.setMeta(slashPopupPluginKey, {from, to, text}));
const slashState = slashPopupPluginKey.getState(view.state);
// Note: 按 esc 的时候或者插入完成的时候 show 会变成 false
if (text === '/') {
tr.insertText('/', from, to);
tr.setMeta(slashPopupPluginKey, {from, to: to + 1, show: true, text});
dispatch(tr);
return true;
} else if (slashState && slashState.show) {
tr.insertText(text, from, to);
tr.setMeta(slashPopupPluginKey, {to: to + 1});
dispatch(tr);
return true;
}
return false;
}
}
Expand Down
3 changes: 1 addition & 2 deletions app/redux/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ export const SHOW_SLASH_POPUP = 'SHOW_SLASH_POPUP';
export const SELECT_SLASH_POPUP = 'SELECT_SLASH_POPUP';

export const showPopup = (props: PopupType): ActionType => {
const { type, component, view, options } = props;
const { type, component, options } = props;
return {
type,
payload: {
component,
view,
options,
type
}
Expand Down
1 change: 0 additions & 1 deletion app/redux/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,5 @@ export interface ReactViewType {
export interface PopupType {
type: string;
component: ReactElement;
view?: EditorView;
options?: any;
}
2 changes: 0 additions & 2 deletions app/redux/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ import { SHOW_SLASH_POPUP, SELECT_SLASH_POPUP } from '@redux/actions';
const popup = (state: PopupStateType = {}, action: ActionType) => {
switch (action.type) {
case SHOW_SLASH_POPUP:
console.log('show:', action.payload);
return {
...state,
...action.payload // use the text to filter the options for user selected
};
case SELECT_SLASH_POPUP:
console.log('select:', action.payload);
return {
...state,
...action.payload
Expand Down
46 changes: 46 additions & 0 deletions app/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,49 @@ body {
box-sizing: border-box;
pointer-events: none;
}

// prosemirror-view/style
.ProseMirror {
position: relative;
}

.ProseMirror {
word-wrap: break-word;
white-space: pre-wrap;
white-space: break-spaces;
-webkit-font-variant-ligatures: none;
font-variant-ligatures: none;
font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
}

.ProseMirror pre {
white-space: pre-wrap;
}

.ProseMirror li {
position: relative;
}

.ProseMirror-hideselection *::selection { background: transparent; }
.ProseMirror-hideselection *::-moz-selection { background: transparent; }
.ProseMirror-hideselection { caret-color: transparent; }

.ProseMirror-selectednode {
outline: 2px solid #8cf;
}

/* Make sure li selections wrap around markers */

li.ProseMirror-selectednode {
outline: none;
}

li.ProseMirror-selectednode:after {
content: "";
position: absolute;
left: -32px;
right: -2px; top: -2px; bottom: -2px;
border: 2px solid #8cf;
pointer-events: none;
}

Loading

0 comments on commit 1f1b488

Please sign in to comment.