Skip to content

Commit

Permalink
feat: add change editor button (#874)
Browse files Browse the repository at this point in the history
  • Loading branch information
amanharwara authored Feb 16, 2022
1 parent 7f341c7 commit 1983b94
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 18 deletions.
138 changes: 138 additions & 0 deletions app/assets/javascripts/components/ChangeEditorButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state';
import { MENU_MARGIN_FROM_APP_BORDER } from '@/views/constants';
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from '@reach/disclosure';
import VisuallyHidden from '@reach/visually-hidden';
import { ComponentArea, SNComponent } from '@standardnotes/snjs';
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { useEffect, useRef, useState } from 'preact/hooks';
import { Icon } from './Icon';
import { ChangeEditorMenu } from './NotesOptions/changeEditor/ChangeEditorMenu';
import { createEditorMenuGroups } from './NotesOptions/changeEditor/createEditorMenuGroups';
import { EditorMenuGroup } from './NotesOptions/ChangeEditorOption';
import { useCloseOnBlur } from './utils';

type Props = {
application: WebApplication;
appState: AppState;
onClickPreprocessing?: () => Promise<void>;
};

export const ChangeEditorButton: FunctionComponent<Props> = observer(
({ application, appState, onClickPreprocessing }) => {
const note = Object.values(appState.notes.selectedNotes)[0];
const [open, setOpen] = useState(false);
const [position, setPosition] = useState({
top: 0,
right: 0,
});
const [maxHeight, setMaxHeight] = useState<number | 'auto'>('auto');
const buttonRef = useRef<HTMLButtonElement>(null);
const panelRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const [closeOnBlur] = useCloseOnBlur(containerRef, setOpen);
const [editors] = useState<SNComponent[]>(() =>
application.componentManager
.componentsForArea(ComponentArea.Editor)
.sort((a, b) => {
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
})
);
const [editorMenuGroups, setEditorMenuGroups] = useState<EditorMenuGroup[]>(
[]
);
const [currentEditor, setCurrentEditor] = useState<SNComponent>();

useEffect(() => {
setEditorMenuGroups(createEditorMenuGroups(editors));
}, [editors]);

useEffect(() => {
if (note) {
setCurrentEditor(application.componentManager.editorForNote(note));
}
}, [application, note]);

const toggleChangeEditorMenu = async () => {
const rect = buttonRef.current?.getBoundingClientRect();
if (rect) {
const { clientHeight } = document.documentElement;
const footerElementRect = document
.getElementById('footer-bar')
?.getBoundingClientRect();
const footerHeightInPx = footerElementRect?.height;

if (footerHeightInPx) {
setMaxHeight(
clientHeight -
rect.bottom -
footerHeightInPx -
MENU_MARGIN_FROM_APP_BORDER
);
}

setPosition({
top: rect.bottom,
right: document.body.clientWidth - rect.right,
});

const newOpenState = !open;
if (newOpenState && onClickPreprocessing) {
await onClickPreprocessing();
}

setOpen(newOpenState);
}
};

return (
<div ref={containerRef}>
<Disclosure open={open} onChange={toggleChangeEditorMenu}>
<DisclosureButton
onKeyDown={(event) => {
if (event.key === 'Escape') {
setOpen(false);
}
}}
onBlur={closeOnBlur}
ref={buttonRef}
className="sn-icon-button"
>
<VisuallyHidden>Change editor</VisuallyHidden>
<Icon type="dashboard" className="block" />
</DisclosureButton>
<DisclosurePanel
onKeyDown={(event) => {
if (event.key === 'Escape') {
setOpen(false);
buttonRef.current?.focus();
}
}}
ref={panelRef}
style={{
...position,
maxHeight,
}}
className="sn-dropdown sn-dropdown--animated min-w-68 max-h-120 max-w-xs flex flex-col overflow-y-auto fixed"
onBlur={closeOnBlur}
>
<ChangeEditorMenu
closeOnBlur={closeOnBlur}
application={application}
isOpen={open}
currentEditor={currentEditor}
setSelectedEditor={setCurrentEditor}
note={note}
groups={editorMenuGroups}
/>
</DisclosurePanel>
</Disclosure>
</div>
);
}
);
2 changes: 2 additions & 0 deletions app/assets/javascripts/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
CloudOffIcon,
CodeIcon,
CopyIcon,
DashboardIcon,
DownloadIcon,
EditorIcon,
EmailIcon,
Expand Down Expand Up @@ -105,6 +106,7 @@ const ICONS = {
close: CloseIcon,
code: CodeIcon,
copy: CopyIcon,
dashboard: DashboardIcon,
download: DownloadIcon,
editor: EditorIcon,
email: EmailIcon,
Expand Down
30 changes: 19 additions & 11 deletions app/assets/javascripts/components/NoteView/NoteView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { ActionsMenu } from '../ActionsMenu';
import { ComponentView } from '../ComponentView';
import { PanelSide, PanelResizer, PanelResizeType } from '../PanelResizer';
import { ElementIds } from '@/element_ids';
import { ChangeEditorButton } from '../ChangeEditorButton';

const MINIMUM_STATUS_DURATION = 400;
const TEXTAREA_DEBOUNCE = 100;
Expand Down Expand Up @@ -1070,23 +1071,30 @@ export class NoteView extends PureComponent<Props, State> {
<div className="desc">{this.state.noteStatus.desc}</div>
)}
</div>
<>
<div className="mr-3">
<PinNoteButton
appState={this.appState}
onClickPreprocessing={
this.ensureNoteIsInsertedBeforeUIAction
}
/>
</div>
<NotesOptionsPanel
<div className="mr-3">
<ChangeEditorButton
application={this.application}
appState={this.appState}
onClickPreprocessing={
this.ensureNoteIsInsertedBeforeUIAction
}
/>
</>
</div>
<div className="mr-3">
<PinNoteButton
appState={this.appState}
onClickPreprocessing={
this.ensureNoteIsInsertedBeforeUIAction
}
/>
</div>
<NotesOptionsPanel
application={this.application}
appState={this.appState}
onClickPreprocessing={
this.ensureNoteIsInsertedBeforeUIAction
}
/>
</div>
</div>
<NoteTagsContainer appState={this.appState} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,10 @@ export const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({
newMenuPosition.top !== 'auto'
) {
setChangeEditorMenuMaxHeight(
clientHeight - newMenuPosition.top - footerHeightInPx - 2
clientHeight -
newMenuPosition.top -
footerHeightInPx -
MENU_MARGIN_FROM_APP_BORDER
);
}

Expand All @@ -225,7 +228,7 @@ export const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({
className="sn-dropdown-item justify-between"
>
<div className="flex items-center">
<Icon type="editor" className="color-neutral mr-2" />
<Icon type="dashboard" className="color-neutral mr-2" />
Change editor
</div>
<Icon type="chevron-right" className="color-neutral" />
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"pug-loader": "^2.4.0",
"sass-loader": "^12.2.0",
"serve-static": "^1.14.1",
"@standardnotes/stylekit": "5.6.0",
"@standardnotes/stylekit": "5.7.0",
"svg-jest": "^1.0.1",
"ts-jest": "^27.0.7",
"ts-loader": "^9.2.6",
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2887,10 +2887,10 @@
"@standardnotes/settings" "^1.11.3"
"@standardnotes/sncrypto-common" "^1.7.0"

"@standardnotes/stylekit@5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.6.0.tgz#52ff63e9bfc823817832786d54e21e8522f061ac"
integrity sha512-F6PxkVSKJBZMTYkGT96hVPDvcmpvevAC36KwMnaMtnjMQGfOjMLYJRgef5bJunvOeqo2bvoPX5xcd4EwacTBBw==
"@standardnotes/stylekit@5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.7.0.tgz#3dc9c22ad554ec4f2cc58462fa6199a3339a4ab0"
integrity sha512-zc3XiRa/oKOZMc1BHo/sP9ncAdFOXaRGGa87hVtMcQ9gUbouuWvBbkL1ZLV0sb63J5SuPPScEnfK/XqVld8ChA==
dependencies:
"@reach/listbox" "^0.15.0"
"@reach/menu-button" "^0.15.1"
Expand Down

0 comments on commit 1983b94

Please sign in to comment.