Skip to content

Commit

Permalink
refactor post-mount logic into ToggleEditor (see #335)
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmanuel Schanzer committed Apr 13, 2021
1 parent f5b042a commit a8af1a5
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 54 deletions.
38 changes: 10 additions & 28 deletions src/ui/BlockEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {commitChanges} from '../edits/commitChanges';
import {speculateChanges, getTempCM} from '../edits/speculateChanges';
import {pos} from '../types';
import DragAndDropEditor from './DragAndDropEditor';
import {poscmp, say, resetNodeCounter, minpos, maxpos,
import {poscmp, resetNodeCounter, minpos, maxpos,
validateRanges, BlockError} from '../utils';
import BlockComponent from '../components/BlockComponent';
import { defaultKeyMap, keyDown } from '../keymap';
Expand Down Expand Up @@ -146,7 +146,6 @@ class BlockEditor extends Component {
setAST: PropTypes.func.isRequired,
setCursor: PropTypes.func.isRequired,
setQuarantine: PropTypes.func.isRequired,
setAnnouncer: PropTypes.func.isRequired,
clearFocus: PropTypes.func.isRequired,
activateByNid: PropTypes.func.isRequired,
search: PropTypes.shape({
Expand Down Expand Up @@ -248,41 +247,25 @@ class BlockEditor extends Component {
}

handleEditorDidMount = ed => {
const wrapper = ed.getWrapperElement();
wrapper.setAttribute('role', 'tree');
wrapper.setAttribute('aria-label', 'Block Editor');

const scroller = ed.getScrollerElement();
scroller.setAttribute('role', 'presentation');

const announcements = document.createElement('span');
announcements.setAttribute('role', 'log');
announcements.setAttribute('aria-live', 'assertive');
wrapper.appendChild(announcements);
const {passedAST:ast, setAST, search, options, onMount} = this.props;
ed.on('beforeChange', this.handleBeforeChange);
ed.on('changes', this.handleChanges);

var ast = this.props.passedAST;
this.props.setAST(ast);
if (this.props.options.collapseAll) {
this.props.dispatch({type: 'COLLAPSE_ALL'});
}
this.props.setAnnouncer(announcements);
say("Block Mode Enabled", 500);
// set AST and searchg properties and collapse preferences
setAST(ast); search.setCM(ed);
if (options.collapseAll) { this.props.dispatch({type: 'COLLAPSE_ALL'}); }

// if we have nodes, default to the first one. Note that does NOT
// activate a node; only when the editor is focused, the focused node will be
// active
// When the editor receives focus, select the first root (if it exists)
if (ast.rootNodes.length > 0) {
this.props.dispatch({type: 'SET_FOCUS', focusId: ast.rootNodes[0].id});
}
// a tree element should know how many roots it has
// Set extra aria attributes
const wrapper = ed.getWrapperElement();
wrapper.setAttribute('role', 'tree');
wrapper.setAttribute('aria-setsize', ast.rootNodes.length);
this.props.search.setCM(ed);

// pass the block-mode CM editor, API, and current AST
SHARED.cm = ed;
this.props.onMount(ed, this.buildAPI(ed), ast);
onMount(ed, this.buildAPI(ed), ast);
}

executeAction(action) {
Expand Down Expand Up @@ -628,7 +611,6 @@ const mapStateToProps = ({ast, cur, quarantine}) => ({
const mapDispatchToProps = dispatch => ({
dispatch,
setAST: ast => dispatch({type: 'SET_AST', ast}),
setAnnouncer: announcer => dispatch({type: 'SET_ANNOUNCER', announcer}),
setCursor: (_, cur) => dispatch(setCursor(cur)),
clearFocus: () => {
//console.log('BlockEditor:671 calling SET_FOCUS with focusId null');
Expand Down
14 changes: 0 additions & 14 deletions src/ui/TextEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,7 @@ class TextEditor extends Component {
}

handleEditorDidMount = ed => {
const wrapper = ed.getWrapperElement();
wrapper.setAttribute('aria-label', 'Text Editor');

const scroller = ed.getScrollerElement();
scroller.setAttribute('role', 'presentation');

const announcements = document.createElement('span');
announcements.setAttribute('role', 'log');
announcements.setAttribute('aria-live', 'assertive');
wrapper.appendChild(announcements);
this.props.setAnnouncer(announcements);
say("Text Mode Enabled", 500);

// pass the text-mode CM editor, API, and current AST
SHARED.cm = ed;
this.props.onMount(ed, this.buildAPI(ed), this.props.passedAST);
}

Expand Down
26 changes: 21 additions & 5 deletions src/ui/ToggleEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import ByString from './searchers/ByString';
import ByBlock from './searchers/ByBlock';
import attachSearch from './Search';
import Toolbar from './Toolbar';
import {ToggleButton, BugButton} from './EditorButtons';
import TrashCan from './TrashCan';
import { ToggleButton, BugButton } from './EditorButtons';
import { say } from '../utils';
import TrashCan from './TrashCan';
import SHARED from '../shared';
import './ToggleEditor.less';

Expand Down Expand Up @@ -45,7 +46,7 @@ const codeMirrorAPI = ['getValue', 'setValue', 'getRange', 'replaceRange', 'getL
export default @CMBContext class ToggleEditor extends Component {
state = {
blockMode: false,
dialog: false
dialog: false,
}

static propTypes = {
Expand Down Expand Up @@ -73,9 +74,14 @@ export default @CMBContext class ToggleEditor extends Component {
this.cmOptions = Object.assign(defaultCmOptions, props.cmOptions);
this.language = props.language;
this.parser = this.language.getParser();

this.toolbarRef = createRef();

// construct announcer DOM node
const announcements = document.createElement('div');
announcements.setAttribute('aria-live', 'assertive');
announcements.setAttribute('aria-atomic', 'true');
SHARED.announcer = announcements;

let defaultOptions = {
parser: this.parser,
incrementalRendering: true,
Expand All @@ -88,7 +94,7 @@ export default @CMBContext class ToggleEditor extends Component {

// make sure 'this' always refers to ToggleEditor
// see https://reactjs.org/docs/handling-events.html
this.showDialog = this.showDialog.bind(this);
this.showDialog = this.showDialog.bind(this);
this.closeDialog = this.closeDialog.bind(this);
}

Expand Down Expand Up @@ -130,10 +136,17 @@ export default @CMBContext class ToggleEditor extends Component {

// After a mode switch, rebuild the API and re-assign events
handleEditorMounted = (ed, api, ast) => {
// set CM aria attributes, and add announcer
const mode = this.state.blockMode ? 'Block' : 'Text';
ed.getScrollerElement().setAttribute('role', 'presentation');
ed.getWrapperElement().setAttribute('aria-label', mode+' Editor');
ed.getWrapperElement().appendChild(SHARED.announcer);
// Rebuild the API and assign re-events
Object.assign(this.props.api, this.buildAPI(ed), api);
Object.keys(this.eventHandlers).forEach(type => {
this.eventHandlers[type].forEach(h => ed.on(type, h));
});

// once the DOM has loaded, reconstitute any marks and render them
// see https://stackoverflow.com/questions/26556436/react-after-render-code/28748160#28748160
window.requestAnimationFrame( () => setTimeout(() => {
Expand All @@ -142,6 +155,9 @@ export default @CMBContext class ToggleEditor extends Component {
this.props.api.markText(node.from, node.to, m.options);
});
}, 0));
// save the editor, and announce completed mode switch
SHARED.cm = ed;
say(mode + " Mode Enabled", 500);
}

componentDidMount() { this.hasMounted = true; }
Expand Down
13 changes: 6 additions & 7 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import SHARED from './shared';
import {store} from './store';
import objToStableString from 'fast-json-stable-stringify';

// Use reliable object->string library to generate a
// pseudohash, then hash the string so we don't have giant
// "hashes" eating memory (see https://stackoverflow.com/a/7616484/12026982
// and https://anchortagdev.com/consistent-object-hashing-using-stable-stringification/ )
// Use reliable object->string library to generate a pseudohash,
// then hash the string so we don't have giant "hashes" eating memory
// (see https://stackoverflow.com/a/7616484/12026982 and
// https://anchortagdev.com/consistent-object-hashing-using-stable-stringification/ )
export function hashObject(obj) {
const str = objToStableString(obj);
var hash = 0, i, chr;
Expand Down Expand Up @@ -110,16 +110,15 @@ store.queuedAnnouncement = false;
// This handles _everything_else_.
export function say(text, delay=200, allowOverride=false) {
const announcement = document.createTextNode(text + ', ');
let state = store.getState();
const announcer = state.announcer;
const announcer = SHARED.announcer;
if (store.muteAnnouncements || !announcer) return; // if nothing to do, bail
clearTimeout(store.queuedAnnouncement); // clear anything overrideable
if(allowOverride) { // enqueue overrideable announcements
store.queuedAnnouncement = setTimeout(() => say('Use enter to edit', 0), delay);
} else { // otherwise write it to the DOM,
announcer.childNodes.forEach( c => c.remove() ); // remove the children
console.log('say:', text); // then erase it 10ms later
setTimeout(() => announcer.appendChild(announcement), delay);
setTimeout(() => announcer.removeChild(announcement), delay + 10);
}
}

Expand Down

0 comments on commit a8af1a5

Please sign in to comment.