Skip to content

Commit

Permalink
Merge pull request #112 from MicroPad/63-top-nav
Browse files Browse the repository at this point in the history
Breadcrumb navigation
  • Loading branch information
NickGeek authored Jan 27, 2019
2 parents 45d3b13 + f736df7 commit beec0e0
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 20 deletions.
5 changes: 4 additions & 1 deletion app/src/core/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export const actions = {
restoreJsonNotepadAndLoadNote: actionCreator<RestoreJsonNotepadAndLoadNoteAction>('PARSE_JSON_NOTEPAD_AND_LOAD_NOTE'),
newNotepad: actionCreator<FlatNotepad>('NEW_NOTEPAD'),
flipFullScreenState: actionCreator<void>('FLIP_FULL_SCREEN'),
exitFullScreen: actionCreator<void>('EXIT_FULL_SCREEN'),
openBreadcrumb: actionCreator<string>('OPEN_BREADCRUMB'),
deleteNotepad: actionCreator<string>('DELETE_NOTEPAD'),
exportNotepad: actionCreator<void>('EXPORT_NOTEPAD'),
expandSection: actionCreator<string>('OPEN_SECTION'),
Expand Down Expand Up @@ -91,5 +93,6 @@ export const actions = {
selectTheme: actionCreator<ThemeName>('SELECT_THEME'),
moveNotepadObject: actionCreator<MoveNotepadObjectAction>('MOVE_NOTEPAD_OBJECT'),
quickMarkdownInsert: actionCreator<void>('QUICK_MARKDOWN_INSERT'),
quickNotepad: actionCreator<void>('QUICK_NOTEPAD')
quickNotepad: actionCreator<void>('QUICK_NOTEPAD'),
flashExplorer: actionCreator<void>('FLASH_EXPLORER')
};
50 changes: 47 additions & 3 deletions app/src/core/epics/ExplorerEpics.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { combineEpics } from 'redux-observable';
import { filter, map } from 'rxjs/operators';
import { concatMap, filter, map, startWith, tap } from 'rxjs/operators';
import { Action, isType } from 'redux-typescript-actions';
import { actions } from '../actions';
import { INotepadStoreState } from '../types/NotepadTypes';
import { isAction } from '../../react-web/util';
import { NewNotepadObjectAction } from '../types/ActionTypes';
import { IStoreState } from '../types';
import { FlatNotepad } from 'upad-parse/dist';
import { FlatNotepad, Note } from 'upad-parse/dist';
import { FlatSection } from 'upad-parse/dist/FlatNotepad';
import { Observable } from 'rxjs';
import { Store } from 'redux';
import { ThemeValues } from '../../react-web/ThemeValues';

export namespace ExplorerEpics {
export const expandAll$ = (action$, store) =>
Expand Down Expand Up @@ -36,8 +39,49 @@ export namespace ExplorerEpics {
map((newSection: FlatSection) => actions.expandSection(newSection.internalRef))
);

export const openBreadcrumb$ = (action$: Observable<Action<string>>, store: Store<IStoreState>) =>
action$.pipe(
isAction(actions.openBreadcrumb),
map(action => action.payload),
filter(() => !!store.getState().notepads.notepad && !!store.getState().notepads.notepad!.item),
map((ref: string) =>
store.getState().notepads.notepad!.item!.notes[ref]
|| store.getState().notepads.notepad!.item!.sections[ref]
),
map((notepadObj: FlatSection | Note) => {
const notepad = store.getState().notepads.notepad!.item!;
return [...notepad.pathFrom(notepadObj).slice(1), notepadObj];
}),
concatMap((path: Array<FlatSection | Note>) =>
[
actions.exitFullScreen(),
actions.collapseAllExplorer(),
...path
.filter(obj => !(obj as Note).parent)
.map(section => actions.expandSection(section.internalRef)),
actions.flashExplorer()
]
),
tap(a => console.log(a))
);

export const flashExplorer$ = (action$: Observable<Action<void>>, store: Store<IStoreState>) =>
action$.pipe(
isAction(actions.flashExplorer),
tap(() => {
const theme = ThemeValues[store.getState().app.theme];
const explorer = document.getElementById('notepad-explorer')!;

explorer.style.backgroundColor = theme.accent;
setTimeout(() => explorer.style.backgroundColor = theme.chrome, 150);
}),
filter(() => false)
);

export const explorerEpics$ = combineEpics(
expandAll$,
autoLoadNewSection$
autoLoadNewSection$,
openBreadcrumb$,
flashExplorer$
);
}
5 changes: 5 additions & 0 deletions app/src/core/reducers/AppReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export class AppReducer extends MicroPadReducer<IAppStoreState> {
...state,
isFullScreen: !state.isFullScreen
};
} else if (isType(action, actions.exitFullScreen)) {
return {
...state,
isFullScreen: false
};
} else if (isType(action, actions.updateDefaultFontSize)) {
return {
...state,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import * as React from 'react';
import { CSSProperties } from 'react';
import './NotepadBreadcrumbsComponent.css';
import { Breadcrumb, MenuItem } from 'react-materialize';
import { Breadcrumb as MaterialBreadcrumb, MenuItem, Dropdown, NavItem } from 'react-materialize';
import { generateGuid } from '../../../util';
import { ThemeName } from '../../../../core/types/Themes';

export type Breadcrumb = {
text: string;
ref?: string;
};

export interface INotepadBreadcrumbsProps {
themeName: ThemeName;
breadcrumbs: string[];
breadcrumbs: Breadcrumb[];
noteTime?: string;
focusItem?: (ref?: string) => void;
}

export default class NotepadBreadcrumbsComponent extends React.Component<INotepadBreadcrumbsProps> {
Expand All @@ -19,7 +25,7 @@ export default class NotepadBreadcrumbsComponent extends React.Component<INotepa
};

render() {
const { themeName, breadcrumbs, noteTime } = this.props;
const { themeName, breadcrumbs, noteTime, focusItem } = this.props;

const breadcrumbStyle: CSSProperties = {
position: 'fixed',
Expand All @@ -28,18 +34,18 @@ export default class NotepadBreadcrumbsComponent extends React.Component<INotepa
};

const crumbs: JSX.Element[] = [];
(breadcrumbs || []).forEach((title: string, i: number) =>
(breadcrumbs || []).forEach((crumb: Breadcrumb, i: number) =>
crumbs.push(
<MenuItem key={generateGuid()}>
{title} {i === breadcrumbs.length - 1 && !!noteTime && <span style={this.timeStyle}>{noteTime}</span>}
<MenuItem key={generateGuid()} href={!!crumb.ref ? '#!' : undefined} onClick={() => !!focusItem && focusItem(crumb.ref)}>
{crumb.text} {i === breadcrumbs.length - 1 && !!noteTime && <span style={this.timeStyle}>{noteTime}</span>}
</MenuItem>
));

return (
<div id="breadcrumb-holder" style={breadcrumbStyle}>
<Breadcrumb className={themeName}>
<MaterialBreadcrumb className={themeName}>
{crumbs}
</Breadcrumb>
</MaterialBreadcrumb>
</div>
);
}
Expand Down
39 changes: 31 additions & 8 deletions app/src/react-web/containers/header/NotepadBreadcrumbsContainer.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import { connect } from 'react-redux';
import { IStoreState } from '../../../core/types';
import NotepadBreadcrumbsComponent, { INotepadBreadcrumbsProps } from '../../components/header/NotepadBreadcrumbsComponent/NotepadBreadcrumbsComponent';
import NotepadBreadcrumbsComponent, {
Breadcrumb,
INotepadBreadcrumbsProps
} from '../../components/header/NotepadBreadcrumbsComponent/NotepadBreadcrumbsComponent';
import { INotepadStoreState } from '../../../core/types/NotepadTypes';
import { format } from 'date-fns';
import { FlatNotepad } from 'upad-parse/dist';
import { FlatSection } from 'upad-parse/dist/FlatNotepad';
import { Action, Dispatch } from 'redux';
import { actions } from '../../../core/actions';

export function mapStateToProps({ notepads, currentNote, app }: IStoreState): INotepadBreadcrumbsProps {
let breadcrumbs: string[] = [];
let breadcrumbs: Breadcrumb[] = [];
let time: string | undefined = undefined;

const makeCrumb = (title: string): Breadcrumb => ({ text: title });

if (currentNote.ref.length === 0) {
breadcrumbs.push(((notepads.notepad || <INotepadStoreState> {}).item || <FlatNotepad> {}).title
|| 'Create a quick notebook below, or open/create a notebook using the drop-down/sidebar to start');
breadcrumbs.push(
makeCrumb(
((notepads.notepad || <INotepadStoreState> {}).item || <FlatNotepad> {}).title
|| 'Create a quick notebook below, or open/create a notebook using the drop-down/sidebar to start'
)
);
} else {
const note = notepads.notepad!.item!.notes[currentNote.ref];
if (!note) return { themeName: app.theme, breadcrumbs: ['Error loading note'] };
if (!note) return { themeName: app.theme, breadcrumbs: [{ text: 'Error loading note' }] };

// Get parent list up the tree
breadcrumbs = [
...notepads.notepad!.item!.pathFrom(note).map(parent => parent.title),
note.title
...notepads.notepad!.item!.pathFrom(note).map(parent => ({
text: parent.title,
ref: (parent as FlatSection).internalRef
})),
{ text: note.title, ref: note.internalRef }
];

if (breadcrumbs.length > 1) {
Expand All @@ -34,4 +49,12 @@ export function mapStateToProps({ notepads, currentNote, app }: IStoreState): IN
};
}

export default connect<INotepadBreadcrumbsProps>(mapStateToProps)(NotepadBreadcrumbsComponent);
function mapDispatchToProps(dispatch: Dispatch<Action>): Partial<INotepadBreadcrumbsProps> {
return {
focusItem: ref => {
if (!!ref) dispatch(actions.openBreadcrumb(ref));
}
};
}

export default connect<INotepadBreadcrumbsProps>(mapStateToProps, mapDispatchToProps)(NotepadBreadcrumbsComponent);

0 comments on commit beec0e0

Please sign in to comment.