Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nav enhancements #54

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

clozach
Copy link

@clozach clozach commented May 14, 2020

As mentioned here, I decided to roll feedback from @Stvad into a new branch with additional navigation improvements in the form of arrow key navigation.

These small tweaks make my own experience in Roam feel much less stilted, and I hope you feel the same after trying them out!

Feedback welcome, esp. regarding…

  • Do you see/experience any bugs here?
  • Is there anything about the code structure, naming, and/or organization you'd like to see changed before considering a merge?

…no regular nodes are currently active OR
…the title is active

Problem: When no nodes are selected, or the title is currently being edited, hitting `Enter` is a no-op.

Solution: Hitting `Enter` in these two cases now puts the cursor in a new node at the top of the page.
Problem: The arrow keys stop doing anything useful when either no nodes are active or the cursor is in the title or top node

Solution:
- When no nodes are active, Up Arrow (↑) activates the title
- From the top node, Up Arrow (↑) activates the title
- From the title, Down Arrow (↓) activates the top node
- When no nodes are active, Up Arrow (↓) activates the top node
When the top node starts with a tag, `activateTopBlock` places the cursor _after_ the tag by default. This tweak corrects that strange behavior.

# Steps to reproduce
I have a page where the first node looks like this:

    #character #father #grandfather #[[Book(0)]]

Before this commit, hitting Enter from within the title would place the cursor here…

    #character↓ #father #grandfather #[[Book(0)]]

…instead of here…

    ↓#character #father #grandfather #[[Book(0)]]
if (isInSearch) return;

switch (event.key) {
case 'Enter':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed in previous PR, the Enter is probably redundant

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean here. In the other PR, I had const enter = 'Enter'. Here there's no intermediary storage, just the raw string. ¯_(ツ)_/¯

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, I meant this whole shortcut overall, as the standard cmd-enter exists

@@ -20,6 +21,47 @@ const dispatchMap = new Map([

browser.runtime.onMessage.addListener(command => dispatchMap.get(command)?.())

const enhanceNavigation = (event: KeyboardEvent) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this deserves to be it's own "feature" now with the ability to enable/disable it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I'll put it behind a feature toggle.

@@ -20,6 +21,47 @@ const dispatchMap = new Map([

browser.runtime.onMessage.addListener(command => dispatchMap.get(command)?.())

const enhanceNavigation = (event: KeyboardEvent) => {
const isEditingTitle = event.target?.parentElement instanceof HTMLHeadingElement
const isNoBlockActive = !Roam.getActiveRoamNode()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

notInRoamBlock?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started out as an iOS dev, where there's a standard naming convention of phrasing boolean names with "is", as in, "Is no block active?" However, I can see that it's just causing confusion here, so I'll avoid it in future, and find something better in this case, should this PR remain open. 🙃

const isEditingTitle = event.target?.parentElement instanceof HTMLHeadingElement
const isNoBlockActive = !Roam.getActiveRoamNode()
const isTopBlockActive = getActiveEditElement() === getFirstTopLevelBlock()
const isInSearch = event.target instanceof HTMLInputElement
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like an unreliable way to check for search. But I suppose you probably don't want to mess with any input element in general. So probably rename variable to reflect that

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

break
case 'ArrowUp':
if (isNoBlockActive || isTopBlockActive) {
if (event.getModifierState("Shift")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems indicative of potentially larger surface area of impact (e.g. this is gonna break alt+up or cmd+up/etc in top block, maybe other things).
Makes me hesitate about overriding things that are so fundamental. How about just adding a shortcut to edit the title?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take your point, and am considering extending the modifier check to include the full set of modifiers listed here so that it's impossible for there to be a conflict with existing shortcuts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not entirely sure. seems like a rather intimate set of things to override and things can go wrong... that said, I think it can still be ok to include it. assuming we have a way to disable it and do careful testing that we don't introduce unexpected behaviors.
What about just scrolling the page with the keyboard? (I don't use it as I use vim mappings, but seems plausibly like something people use?)

Copy link
Author

@clozach clozach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, @Stvad! I hope to have your issues completely addressed in the next day or two.

@@ -20,6 +21,47 @@ const dispatchMap = new Map([

browser.runtime.onMessage.addListener(command => dispatchMap.get(command)?.())

const enhanceNavigation = (event: KeyboardEvent) => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I'll put it behind a feature toggle.

@@ -20,6 +21,47 @@ const dispatchMap = new Map([

browser.runtime.onMessage.addListener(command => dispatchMap.get(command)?.())

const enhanceNavigation = (event: KeyboardEvent) => {
const isEditingTitle = event.target?.parentElement instanceof HTMLHeadingElement
const isNoBlockActive = !Roam.getActiveRoamNode()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started out as an iOS dev, where there's a standard naming convention of phrasing boolean names with "is", as in, "Is no block active?" However, I can see that it's just causing confusion here, so I'll avoid it in future, and find something better in this case, should this PR remain open. 🙃

const isEditingTitle = event.target?.parentElement instanceof HTMLHeadingElement
const isNoBlockActive = !Roam.getActiveRoamNode()
const isTopBlockActive = getActiveEditElement() === getFirstTopLevelBlock()
const isInSearch = event.target instanceof HTMLInputElement
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

if (isInSearch) return;

switch (event.key) {
case 'Enter':
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean here. In the other PR, I had const enter = 'Enter'. Here there's no intermediary storage, just the raw string. ¯_(ツ)_/¯

break
case 'ArrowUp':
if (isNoBlockActive || isTopBlockActive) {
if (event.getModifierState("Shift")) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take your point, and am considering extending the modifier check to include the full set of modifiers listed here so that it's impossible for there to be a conflict with existing shortcuts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants