Skip to content

Commit

Permalink
fix: make menus scrollable when there's not enough space
Browse files Browse the repository at this point in the history
  • Loading branch information
Antonella Sgarlatta committed May 20, 2021
1 parent c7be130 commit 6a9d54c
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 49 deletions.
12 changes: 6 additions & 6 deletions app/assets/javascripts/components/NotesContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ const NotesContextMenu = observer(({ appState }: Props) => {
return appState.notes.contextMenuOpen ? (
<div
ref={contextMenuRef}
className="sn-dropdown max-w-80 flex flex-col py-2 absolute"
style={{ ...appState.notes.contextMenuPosition }}
className="sn-dropdown max-h-120 max-w-80 flex flex-col py-2 overflow-y-scroll fixed"
style={{
...appState.notes.contextMenuPosition,
maxHeight: appState.notes.contextMenuMaxHeight,
}}
>
<NotesOptions
appState={appState}
closeOnBlur={closeOnBlur}
/>
<NotesOptions appState={appState} closeOnBlur={closeOnBlur} />
</div>
) : null;
});
Expand Down
54 changes: 27 additions & 27 deletions app/assets/javascripts/components/NotesOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@ type Props = {
export const NotesOptions = observer(
({ appState, closeOnBlur, onSubmenuChange }: Props) => {
const [tagsMenuOpen, setTagsMenuOpen] = useState(false);
const [tagsMenuPosition, setTagsMenuPosition] = useState({
const [tagsMenuPosition, setTagsMenuPosition] = useState<{
top: number;
right?: number;
left?: number;
}>({
top: 0,
right: 0,
});
const [tagsMenuMaxHeight, setTagsMenuMaxHeight] = useState<number | 'auto'>('auto');
const [tagsMenuMaxHeight, setTagsMenuMaxHeight] =
useState<number | 'auto'>('auto');

const toggleOn = (condition: (note: SNNote) => boolean) => {
const notesMatchingAttribute = notes.filter(condition);
Expand Down Expand Up @@ -62,24 +67,27 @@ export const NotesOptions = observer(
const defaultFontSize = window.getComputedStyle(
document.documentElement
).fontSize;
const maxTagsMenuSize = parseFloat(defaultFontSize) * 20;
const { clientWidth, clientHeight } = document.body;
const maxTagsMenuSize = parseFloat(defaultFontSize) * 30;
const { clientWidth, clientHeight } = document.documentElement;
const buttonRect = tagsButtonRef.current.getBoundingClientRect();
const { offsetTop, offsetWidth } = tagsButtonRef.current;
const footerHeight = 32;

if (buttonRect.top + maxTagsMenuSize > clientHeight - footerHeight) {
if ((buttonRect.top + maxTagsMenuSize) > (clientHeight - footerHeight)) {
setTagsMenuMaxHeight(clientHeight - buttonRect.top - footerHeight - 2);
}

setTagsMenuPosition({
top: offsetTop,
right:
buttonRect.right + maxTagsMenuSize >
clientWidth
? offsetWidth
: -offsetWidth,
});
if ((buttonRect.right + maxTagsMenuSize) > clientWidth) {
setTagsMenuPosition({
top: buttonRect.top,
right: clientWidth - buttonRect.left,
});
} else {
setTagsMenuPosition({
top: buttonRect.top,
left: buttonRect.right,
});
}


setTagsMenuOpen(!tagsMenuOpen);
};
Expand Down Expand Up @@ -127,10 +135,7 @@ export const NotesOptions = observer(
</Switch>
<div className="h-1px my-2 bg-border"></div>
{appState.tags.tagsCount > 0 && (
<Disclosure
open={tagsMenuOpen}
onChange={openTagsMenu}
>
<Disclosure open={tagsMenuOpen} onChange={openTagsMenu}>
<DisclosureButton
onKeyUp={(event) => {
if (event.key === 'Escape') {
Expand All @@ -145,10 +150,7 @@ export const NotesOptions = observer(
<Icon type="hashtag" className={iconClass} />
{'Add tag'}
</div>
<Icon
type="chevron-right"
className="color-neutral"
/>
<Icon type="chevron-right" className="color-neutral" />
</DisclosureButton>
<DisclosurePanel
onKeyUp={(event) => {
Expand All @@ -160,8 +162,9 @@ export const NotesOptions = observer(
style={{
...tagsMenuPosition,
maxHeight: tagsMenuMaxHeight,
position: 'fixed',
}}
className="sn-dropdown sn-dropdown-anchor-right flex flex-col py-2 max-h-80 absolute overflow-y-scroll"
className="sn-dropdown flex flex-col py-2 max-h-120 max-w-80 fixed overflow-y-scroll"
>
{appState.tags.tags.map((tag) => (
<button
Expand Down Expand Up @@ -278,10 +281,7 @@ export const NotesOptions = observer(
}}
>
<div className="flex items-start">
<Icon
type="trash-sweep"
className="color-danger mr-2"
/>
<Icon type="trash-sweep" className="color-danger mr-2" />
<div className="flex-row">
<div className="color-danger">Empty Trash</div>
<div className="text-xs">
Expand Down
7 changes: 6 additions & 1 deletion app/assets/javascripts/components/NotesOptionsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const NotesOptionsPanel = observer(({ appState }: Props) => {
top: 0,
right: 0,
});
const [maxHeight, setMaxHeight] = useState<number | 'auto'>('auto');
const buttonRef = useRef<HTMLButtonElement>();
const panelRef = useRef<HTMLDivElement>();
const [closeOnBlur] = useCloseOnBlur(panelRef, setOpen);
Expand All @@ -35,6 +36,9 @@ export const NotesOptionsPanel = observer(({ appState }: Props) => {
open={open}
onChange={() => {
const rect = buttonRef.current.getBoundingClientRect();
const { clientHeight } = document.documentElement;
const footerHeight = 32;
setMaxHeight(clientHeight - rect.bottom - footerHeight - 2);
setPosition({
top: rect.bottom,
right: document.body.clientWidth - rect.right,
Expand Down Expand Up @@ -65,8 +69,9 @@ export const NotesOptionsPanel = observer(({ appState }: Props) => {
ref={panelRef}
style={{
...position,
maxHeight
}}
className="sn-dropdown flex flex-col py-2"
className="sn-dropdown max-h-120 max-w-80 flex flex-col py-2 overflow-y-scroll fixed"
>
{open && (
<NotesOptions
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/components/SearchOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const SearchOptions = observer(({ appState }: Props) => {
style={{
top: optionsPanelTop,
}}
className="sn-dropdown sn-dropdown-anchor-right grid gap-2 py-2"
className="sn-dropdown sn-dropdown-anchor-right absolute grid gap-2 py-2"
>
<Switch
className="h-10"
Expand Down
6 changes: 6 additions & 0 deletions app/assets/javascripts/ui_models/app_state/notes_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class NotesState {
top: 0,
left: 0,
};
contextMenuMaxHeight: number | 'auto' = 'auto';
showProtectedWarning = false;

constructor(
Expand All @@ -45,6 +46,7 @@ export class NotesState {

setContextMenuOpen: action,
setContextMenuPosition: action,
setContextMenuMaxHeight: action,
setShowProtectedWarning: action,
unselectNotes: action,
});
Expand Down Expand Up @@ -181,6 +183,10 @@ export class NotesState {
this.contextMenuPosition = position;
}

setContextMenuMaxHeight(maxHeight: number | 'auto'): void {
this.contextMenuMaxHeight = maxHeight;
}

async changeSelectedNotes(
mutate: (mutator: NoteMutator) => void
): Promise<void> {
Expand Down
44 changes: 33 additions & 11 deletions app/assets/javascripts/views/notes/notes_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,36 +308,58 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
await this.selectNote(note);
}
if (this.state.selectedNotes[note.uuid]) {
const clientHeight = document.documentElement.clientHeight;
const { clientHeight } = document.documentElement;
const defaultFontSize = window.getComputedStyle(
document.documentElement
).fontSize;
const maxContextMenuHeight = parseFloat(defaultFontSize) * 20;
const maxContextMenuHeight = parseFloat(defaultFontSize) * 30;
const footerHeight = 32;

// Open up-bottom is default behavior
let openUpBottom = true;

const bottomSpace = clientHeight - footerHeight - e.clientY;
const upSpace = e.clientY;

// If not enough space to open up-bottom
if (maxContextMenuHeight > bottomSpace) {
// If there's enough space, open bottom-up
if (upSpace > maxContextMenuHeight) {
this.appState.notes.setContextMenuPosition({
bottom: clientHeight - e.clientY,
left: e.clientX,
});
// Else, open on top of screen
openUpBottom = false;
this.appState.notes.setContextMenuMaxHeight(
'auto'
);
// Else, reduce max height (menu will be scrollable) and open in whichever direction there's more space
} else {
this.appState.notes.setContextMenuPosition({
top: 2,
left: e.clientX,
});
if (upSpace > bottomSpace) {
this.appState.notes.setContextMenuMaxHeight(
upSpace - 2
);
openUpBottom = false;
} else {
this.appState.notes.setContextMenuMaxHeight(
bottomSpace - 2
);
}
}
} else {
this.appState.notes.setContextMenuMaxHeight(
'auto'
);
}

if (openUpBottom) {
this.appState.notes.setContextMenuPosition({
top: e.clientY,
left: e.clientX,
});
} else {
this.appState.notes.setContextMenuPosition({
bottom: clientHeight - e.clientY,
left: e.clientX,
});
}

this.appState.notes.setContextMenuOpen(true);
}
}
Expand Down
13 changes: 10 additions & 3 deletions app/assets/stylesheets/_sn.scss
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@
max-width: 15rem;
}

.max-w-80 {
max-width: 20rem;
}

.h-1px {
height: 1px;
}
Expand All @@ -151,8 +155,12 @@
height: 2.5rem;
}

.max-h-80 {
max-height: 20rem;
.max-h-120 {
max-height: 30rem;
}

.fixed {
position: fixed;
}

.overflow-y-scroll {
Expand Down Expand Up @@ -198,7 +206,6 @@
}

.sn-dropdown {
@extend .absolute;
@extend .bg-default;
@extend .min-w-80;
@extend .transition-transform;
Expand Down

0 comments on commit 6a9d54c

Please sign in to comment.