feat: screen presentation and dismiss animations for mobile (#2073)
This commit is contained in:
@@ -52,7 +52,6 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
notesToDisplay = 0
|
||||
pageSize = 0
|
||||
panelTitle = 'Notes'
|
||||
panelWidth = 0
|
||||
renderedItems: ListableContentItem[] = []
|
||||
searchSubmitted = false
|
||||
showDisplayOptionsMenu = false
|
||||
@@ -189,7 +188,6 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
notes: observable,
|
||||
notesToDisplay: observable,
|
||||
panelTitle: observable,
|
||||
panelWidth: observable,
|
||||
items: observable,
|
||||
renderedItems: observable,
|
||||
showDisplayOptionsMenu: observable,
|
||||
@@ -439,6 +437,7 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
const activeController = this.getActiveItemController()
|
||||
|
||||
if (this.shouldLeaveSelectionUnchanged(activeController)) {
|
||||
log(LoggingDomain.Selection, 'Leaving selection unchanged')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -451,7 +450,7 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
|
||||
if (this.shouldSelectFirstItem(itemsReloadSource)) {
|
||||
log(LoggingDomain.Selection, 'Selecting next item after closing active one')
|
||||
this.selectionController.selectNextItem()
|
||||
this.selectionController.selectNextItem({ userTriggered: false })
|
||||
}
|
||||
} else if (activeItem && this.shouldSelectActiveItem(activeItem)) {
|
||||
log(LoggingDomain.Selection, 'Selecting active item')
|
||||
@@ -460,6 +459,8 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
await this.selectFirstItem()
|
||||
} else if (this.shouldSelectNextItemOrCreateNewNote(activeItem)) {
|
||||
await this.selectNextItemOrCreateNewNote()
|
||||
} else {
|
||||
log(LoggingDomain.Selection, 'No selection change')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,13 +580,6 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
this.displayOptions = newDisplayOptions
|
||||
this.webDisplayOptions = newWebDisplayOptions
|
||||
|
||||
const listColumnWidth =
|
||||
selectedTag?.preferences?.panelWidth || this.application.getPreference(PrefKey.NotesPanelWidth)
|
||||
|
||||
if (listColumnWidth && listColumnWidth !== this.panelWidth) {
|
||||
this.panelWidth = listColumnWidth
|
||||
}
|
||||
|
||||
if (!displayOptionsChanged) {
|
||||
return { didReloadItems: false }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { PopoverFileItemActionType } from '@/Components/AttachedFilesPopover/PopoverFileItemAction'
|
||||
import { NoteViewController } from '@/Components/NoteView/Controller/NoteViewController'
|
||||
import { AppPaneId } from '@/Components/ResponsivePane/AppPaneMetadata'
|
||||
import { AppPaneId } from '@/Components/Panes/AppPaneMetadata'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { createLinkFromItem } from '@/Utils/Items/Search/createLinkFromItem'
|
||||
import { ItemLink } from '@/Utils/Items/Search/ItemLink'
|
||||
|
||||
@@ -25,6 +25,7 @@ import { CrossControllerEvent } from '../CrossControllerEvent'
|
||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||
import { Persistable } from '../Abstract/Persistable'
|
||||
import { TagListSectionType } from '@/Components/Tags/TagListSection'
|
||||
import { PaneLayout } from '../PaneController/PaneLayout'
|
||||
|
||||
export class NavigationController
|
||||
extends AbstractViewController
|
||||
@@ -260,6 +261,10 @@ export class NavigationController
|
||||
return this.selectedUuid === SystemViewId.Files
|
||||
}
|
||||
|
||||
isTagFilesView(tag: AnyTag): boolean {
|
||||
return tag.uuid === SystemViewId.Files
|
||||
}
|
||||
|
||||
public isInAnySystemView(): boolean {
|
||||
return (
|
||||
this.selected instanceof SmartView && Object.values(SystemViewId).includes(this.selected.uuid as SystemViewId)
|
||||
@@ -458,10 +463,10 @@ export class NavigationController
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
const selectionHasNotChanged = this.selected_?.uuid === tag?.uuid && location === this.selectedLocation
|
||||
|
||||
if (selectionHasNotChanged) {
|
||||
return
|
||||
if (tag && this.isTagFilesView(tag)) {
|
||||
this.application.paneController.setPaneLayout(PaneLayout.FilesView)
|
||||
} else if (userTriggered) {
|
||||
this.application.paneController.setPaneLayout(PaneLayout.ItemSelection)
|
||||
}
|
||||
|
||||
this.previouslySelected_ = this.selected_
|
||||
@@ -497,7 +502,7 @@ export class NavigationController
|
||||
}
|
||||
|
||||
get filesNavigationView(): SmartView {
|
||||
return this.smartViews.find((view) => view.uuid === SystemViewId.Files) as SmartView
|
||||
return this.smartViews.find(this.isTagFilesView) as SmartView
|
||||
}
|
||||
|
||||
private setSelectedTagInstance(tag: AnyTag | undefined): void {
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
import { TOGGLE_LIST_PANE_KEYBOARD_COMMAND, TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND } from '@standardnotes/ui-services'
|
||||
import { ApplicationEvent, InternalEventBus, PrefKey } from '@standardnotes/snjs'
|
||||
import { AppPaneId } from './../Components/ResponsivePane/AppPaneMetadata'
|
||||
import { isMobileScreen } from '@/Utils'
|
||||
import { makeObservable, observable, action, computed } from 'mobx'
|
||||
import { Disposer } from '@/Types/Disposer'
|
||||
import { MediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { PANEL_NAME_NAVIGATION, PANEL_NAME_NOTES } from '@/Constants/Constants'
|
||||
|
||||
const WidthForCollapsedPanel = 5
|
||||
const MinimumNavPanelWidth = PrefDefaults[PrefKey.TagsPanelWidth]
|
||||
const MinimumNotesPanelWidth = PrefDefaults[PrefKey.NotesPanelWidth]
|
||||
|
||||
export class PaneController extends AbstractViewController {
|
||||
currentPane: AppPaneId = isMobileScreen() ? AppPaneId.Items : AppPaneId.Editor
|
||||
previousPane: AppPaneId = isMobileScreen() ? AppPaneId.Items : AppPaneId.Editor
|
||||
isInMobileView = isMobileScreen()
|
||||
protected disposers: Disposer[] = []
|
||||
|
||||
currentNavPanelWidth = 0
|
||||
currentItemsPanelWidth = 0
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
currentPane: observable,
|
||||
previousPane: observable,
|
||||
isInMobileView: observable,
|
||||
currentNavPanelWidth: observable,
|
||||
currentItemsPanelWidth: observable,
|
||||
|
||||
isListPaneCollapsed: computed,
|
||||
isNavigationPaneCollapsed: computed,
|
||||
|
||||
setCurrentPane: action,
|
||||
setPreviousPane: action,
|
||||
setIsInMobileView: action,
|
||||
toggleListPane: action,
|
||||
toggleNavigationPane: action,
|
||||
setCurrentItemsPanelWidth: action,
|
||||
setCurrentNavPanelWidth: action,
|
||||
})
|
||||
|
||||
this.setCurrentNavPanelWidth(application.getPreference(PrefKey.TagsPanelWidth, MinimumNavPanelWidth))
|
||||
this.setCurrentItemsPanelWidth(application.getPreference(PrefKey.NotesPanelWidth, MinimumNotesPanelWidth))
|
||||
|
||||
const mediaQuery = window.matchMedia(MediaQueryBreakpoints.md)
|
||||
if (mediaQuery?.addEventListener != undefined) {
|
||||
mediaQuery.addEventListener('change', this.mediumScreenMQHandler)
|
||||
} else {
|
||||
mediaQuery.addListener(this.mediumScreenMQHandler)
|
||||
}
|
||||
|
||||
this.disposers.push(
|
||||
application.addEventObserver(async () => {
|
||||
this.setCurrentNavPanelWidth(application.getPreference(PrefKey.TagsPanelWidth, MinimumNavPanelWidth))
|
||||
this.setCurrentItemsPanelWidth(application.getPreference(PrefKey.NotesPanelWidth, MinimumNotesPanelWidth))
|
||||
}, ApplicationEvent.PreferencesChanged),
|
||||
|
||||
application.keyboardService.addCommandHandler({
|
||||
command: TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault()
|
||||
this.toggleListPane()
|
||||
},
|
||||
}),
|
||||
application.keyboardService.addCommandHandler({
|
||||
command: TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault()
|
||||
this.toggleNavigationPane()
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
setCurrentNavPanelWidth(width: number) {
|
||||
this.currentNavPanelWidth = width
|
||||
}
|
||||
|
||||
setCurrentItemsPanelWidth(width: number) {
|
||||
this.currentItemsPanelWidth = width
|
||||
}
|
||||
|
||||
deinit() {
|
||||
super.deinit()
|
||||
const mq = window.matchMedia(MediaQueryBreakpoints.md)
|
||||
if (mq?.removeEventListener != undefined) {
|
||||
mq.removeEventListener('change', this.mediumScreenMQHandler)
|
||||
} else {
|
||||
mq.removeListener(this.mediumScreenMQHandler)
|
||||
}
|
||||
}
|
||||
|
||||
mediumScreenMQHandler = (event: MediaQueryListEvent) => {
|
||||
if (event.matches) {
|
||||
this.setIsInMobileView(false)
|
||||
} else {
|
||||
this.setIsInMobileView(true)
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentPane(pane: AppPaneId): void {
|
||||
this.currentPane = pane
|
||||
}
|
||||
|
||||
setPreviousPane(pane: AppPaneId): void {
|
||||
this.previousPane = pane
|
||||
}
|
||||
|
||||
setIsInMobileView(isInMobileView: boolean) {
|
||||
this.isInMobileView = isInMobileView
|
||||
}
|
||||
|
||||
toggleListPane = () => {
|
||||
const currentItemsPanelWidth = this.application.getPreference(PrefKey.NotesPanelWidth, MinimumNotesPanelWidth)
|
||||
|
||||
const isCollapsed = currentItemsPanelWidth <= WidthForCollapsedPanel
|
||||
if (isCollapsed) {
|
||||
void this.application.setPreference(PrefKey.NotesPanelWidth, MinimumNotesPanelWidth)
|
||||
} else {
|
||||
void this.application.setPreference(PrefKey.NotesPanelWidth, WidthForCollapsedPanel)
|
||||
}
|
||||
|
||||
this.application.publishPanelDidResizeEvent(PANEL_NAME_NOTES, !isCollapsed)
|
||||
}
|
||||
|
||||
toggleNavigationPane = () => {
|
||||
const currentNavPanelWidth = this.application.getPreference(PrefKey.TagsPanelWidth, MinimumNavPanelWidth)
|
||||
|
||||
const isCollapsed = currentNavPanelWidth <= WidthForCollapsedPanel
|
||||
if (isCollapsed) {
|
||||
void this.application.setPreference(PrefKey.TagsPanelWidth, MinimumNavPanelWidth)
|
||||
} else {
|
||||
void this.application.setPreference(PrefKey.TagsPanelWidth, WidthForCollapsedPanel)
|
||||
}
|
||||
|
||||
this.application.publishPanelDidResizeEvent(PANEL_NAME_NAVIGATION, !isCollapsed)
|
||||
}
|
||||
|
||||
get isListPaneCollapsed() {
|
||||
return this.currentItemsPanelWidth > WidthForCollapsedPanel
|
||||
}
|
||||
|
||||
get isNavigationPaneCollapsed() {
|
||||
return this.currentNavPanelWidth > WidthForCollapsedPanel
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
import {
|
||||
TOGGLE_FOCUS_MODE_COMMAND,
|
||||
TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
|
||||
TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND,
|
||||
} from '@standardnotes/ui-services'
|
||||
import { ApplicationEvent, InternalEventBus, PrefKey, removeFromArray } from '@standardnotes/snjs'
|
||||
import { AppPaneId } from '../../Components/Panes/AppPaneMetadata'
|
||||
import { isMobileScreen } from '@/Utils'
|
||||
import { makeObservable, observable, action, computed } from 'mobx'
|
||||
import { Disposer } from '@/Types/Disposer'
|
||||
import { MediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
import { PaneLayout } from './PaneLayout'
|
||||
import { panesForLayout } from './panesForLayout'
|
||||
import { getIsTabletOrMobileScreen } from '@/Hooks/useIsTabletOrMobileScreen'
|
||||
|
||||
const MinimumNavPanelWidth = PrefDefaults[PrefKey.TagsPanelWidth]
|
||||
const MinimumNotesPanelWidth = PrefDefaults[PrefKey.NotesPanelWidth]
|
||||
const FOCUS_MODE_CLASS_NAME = 'focus-mode'
|
||||
const DISABLING_FOCUS_MODE_CLASS_NAME = 'disable-focus-mode'
|
||||
const FOCUS_MODE_ANIMATION_DURATION = 1255
|
||||
|
||||
export class PaneController extends AbstractViewController {
|
||||
isInMobileView = isMobileScreen()
|
||||
protected disposers: Disposer[] = []
|
||||
panes: AppPaneId[] = []
|
||||
|
||||
currentNavPanelWidth = 0
|
||||
currentItemsPanelWidth = 0
|
||||
focusModeEnabled = false
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
panes: observable,
|
||||
isInMobileView: observable,
|
||||
currentNavPanelWidth: observable,
|
||||
currentItemsPanelWidth: observable,
|
||||
focusModeEnabled: observable,
|
||||
|
||||
currentPane: computed,
|
||||
previousPane: computed,
|
||||
isListPaneCollapsed: computed,
|
||||
isNavigationPaneCollapsed: computed,
|
||||
|
||||
setIsInMobileView: action,
|
||||
toggleListPane: action,
|
||||
toggleNavigationPane: action,
|
||||
setCurrentItemsPanelWidth: action,
|
||||
setCurrentNavPanelWidth: action,
|
||||
presentPane: action,
|
||||
dismissLastPane: action,
|
||||
replacePanes: action,
|
||||
popToPane: action,
|
||||
removePane: action,
|
||||
insertPaneAtIndex: action,
|
||||
setPaneLayout: action,
|
||||
setFocusModeEnabled: action,
|
||||
})
|
||||
|
||||
this.setCurrentNavPanelWidth(application.getPreference(PrefKey.TagsPanelWidth, MinimumNavPanelWidth))
|
||||
this.setCurrentItemsPanelWidth(application.getPreference(PrefKey.NotesPanelWidth, MinimumNotesPanelWidth))
|
||||
|
||||
const screen = getIsTabletOrMobileScreen(application)
|
||||
|
||||
this.panes = screen.isTabletOrMobile
|
||||
? [AppPaneId.Navigation, AppPaneId.Items]
|
||||
: [AppPaneId.Navigation, AppPaneId.Items, AppPaneId.Editor]
|
||||
|
||||
const mediaQuery = window.matchMedia(MediaQueryBreakpoints.md)
|
||||
if (mediaQuery?.addEventListener != undefined) {
|
||||
mediaQuery.addEventListener('change', this.mediumScreenMQHandler)
|
||||
} else {
|
||||
mediaQuery.addListener(this.mediumScreenMQHandler)
|
||||
}
|
||||
|
||||
this.disposers.push(
|
||||
application.addEventObserver(async () => {
|
||||
this.setCurrentNavPanelWidth(application.getPreference(PrefKey.TagsPanelWidth, MinimumNavPanelWidth))
|
||||
this.setCurrentItemsPanelWidth(application.getPreference(PrefKey.NotesPanelWidth, MinimumNotesPanelWidth))
|
||||
}, ApplicationEvent.PreferencesChanged),
|
||||
|
||||
application.keyboardService.addCommandHandler({
|
||||
command: TOGGLE_FOCUS_MODE_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault()
|
||||
this.setFocusModeEnabled(!this.focusModeEnabled)
|
||||
return true
|
||||
},
|
||||
}),
|
||||
application.keyboardService.addCommandHandler({
|
||||
command: TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault()
|
||||
this.toggleListPane()
|
||||
},
|
||||
}),
|
||||
application.keyboardService.addCommandHandler({
|
||||
command: TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault()
|
||||
this.toggleNavigationPane()
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
setCurrentNavPanelWidth(width: number) {
|
||||
this.currentNavPanelWidth = width
|
||||
}
|
||||
|
||||
setCurrentItemsPanelWidth(width: number) {
|
||||
this.currentItemsPanelWidth = width
|
||||
}
|
||||
|
||||
deinit() {
|
||||
super.deinit()
|
||||
const mq = window.matchMedia(MediaQueryBreakpoints.md)
|
||||
if (mq?.removeEventListener != undefined) {
|
||||
mq.removeEventListener('change', this.mediumScreenMQHandler)
|
||||
} else {
|
||||
mq.removeListener(this.mediumScreenMQHandler)
|
||||
}
|
||||
}
|
||||
|
||||
get currentPane(): AppPaneId {
|
||||
return this.panes[this.panes.length - 1] || this.panes[0]
|
||||
}
|
||||
|
||||
get previousPane(): AppPaneId {
|
||||
return this.panes[this.panes.length - 2] || this.panes[0]
|
||||
}
|
||||
|
||||
mediumScreenMQHandler = (event: MediaQueryListEvent) => {
|
||||
if (event.matches) {
|
||||
this.setIsInMobileView(false)
|
||||
} else {
|
||||
this.setIsInMobileView(true)
|
||||
}
|
||||
}
|
||||
|
||||
setIsInMobileView = (isInMobileView: boolean) => {
|
||||
this.isInMobileView = isInMobileView
|
||||
}
|
||||
|
||||
setPaneLayout = (layout: PaneLayout) => {
|
||||
log(LoggingDomain.Panes, 'Set pane layout', layout)
|
||||
|
||||
this.replacePanes(panesForLayout(layout, this.application))
|
||||
}
|
||||
|
||||
replacePanes = (panes: AppPaneId[]) => {
|
||||
log(LoggingDomain.Panes, 'Replacing panes', panes)
|
||||
|
||||
this.panes = panes
|
||||
}
|
||||
|
||||
presentPane = (pane: AppPaneId) => {
|
||||
log(LoggingDomain.Panes, 'Presenting pane', pane)
|
||||
|
||||
if (pane === this.currentPane) {
|
||||
return
|
||||
}
|
||||
|
||||
if (pane === AppPaneId.Items && this.currentPane === AppPaneId.Editor) {
|
||||
this.dismissLastPane()
|
||||
return
|
||||
}
|
||||
|
||||
if (this.currentPane !== pane) {
|
||||
this.panes.push(pane)
|
||||
}
|
||||
}
|
||||
|
||||
insertPaneAtIndex = (pane: AppPaneId, index: number) => {
|
||||
log(LoggingDomain.Panes, 'Inserting pane', pane, 'at index', index)
|
||||
|
||||
this.panes.splice(index, 0, pane)
|
||||
}
|
||||
|
||||
dismissLastPane = (): AppPaneId | undefined => {
|
||||
log(LoggingDomain.Panes, 'Dismissing last pane')
|
||||
|
||||
return this.panes.pop()
|
||||
}
|
||||
|
||||
removePane = (pane: AppPaneId) => {
|
||||
log(LoggingDomain.Panes, 'Removing pane', pane)
|
||||
|
||||
removeFromArray(this.panes, pane)
|
||||
}
|
||||
|
||||
popToPane = (pane: AppPaneId) => {
|
||||
log(LoggingDomain.Panes, 'Popping to pane', pane)
|
||||
|
||||
let index = this.panes.length - 1
|
||||
while (index >= 0) {
|
||||
if (this.panes[index] === pane) {
|
||||
break
|
||||
}
|
||||
|
||||
this.dismissLastPane()
|
||||
index--
|
||||
}
|
||||
}
|
||||
|
||||
toggleListPane = () => {
|
||||
if (this.panes.includes(AppPaneId.Items)) {
|
||||
this.removePane(AppPaneId.Items)
|
||||
} else {
|
||||
if (this.panes.includes(AppPaneId.Navigation)) {
|
||||
this.insertPaneAtIndex(AppPaneId.Items, 1)
|
||||
} else {
|
||||
this.insertPaneAtIndex(AppPaneId.Items, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toggleNavigationPane = () => {
|
||||
if (this.panes.includes(AppPaneId.Navigation)) {
|
||||
this.removePane(AppPaneId.Navigation)
|
||||
} else {
|
||||
this.insertPaneAtIndex(AppPaneId.Navigation, 0)
|
||||
}
|
||||
}
|
||||
|
||||
get isListPaneCollapsed() {
|
||||
return !this.panes.includes(AppPaneId.Items)
|
||||
}
|
||||
|
||||
get isNavigationPaneCollapsed() {
|
||||
return !this.panes.includes(AppPaneId.Navigation)
|
||||
}
|
||||
|
||||
setFocusModeEnabled = (enabled: boolean): void => {
|
||||
this.focusModeEnabled = enabled
|
||||
|
||||
if (enabled) {
|
||||
document.body.classList.add(FOCUS_MODE_CLASS_NAME)
|
||||
return
|
||||
}
|
||||
|
||||
if (document.body.classList.contains(FOCUS_MODE_CLASS_NAME)) {
|
||||
document.body.classList.add(DISABLING_FOCUS_MODE_CLASS_NAME)
|
||||
document.body.classList.remove(FOCUS_MODE_CLASS_NAME)
|
||||
|
||||
setTimeout(() => {
|
||||
document.body.classList.remove(DISABLING_FOCUS_MODE_CLASS_NAME)
|
||||
}, FOCUS_MODE_ANIMATION_DURATION)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export enum PaneLayout {
|
||||
TagSelection = 'tag-selection',
|
||||
ItemSelection = 'item-selection',
|
||||
FilesView = 'files-view',
|
||||
Editing = 'editing',
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { AppPaneId } from '../../Components/Panes/AppPaneMetadata'
|
||||
import { PaneLayout } from './PaneLayout'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { getIsTabletOrMobileScreen } from '@/Hooks/useIsTabletOrMobileScreen'
|
||||
|
||||
export function panesForLayout(layout: PaneLayout, application: WebApplication): AppPaneId[] {
|
||||
const screen = getIsTabletOrMobileScreen(application)
|
||||
if (screen.isTablet) {
|
||||
if (layout === PaneLayout.TagSelection) {
|
||||
return [AppPaneId.Navigation, AppPaneId.Items]
|
||||
} else if (
|
||||
layout === PaneLayout.ItemSelection ||
|
||||
layout === PaneLayout.Editing ||
|
||||
layout === PaneLayout.FilesView
|
||||
) {
|
||||
return [AppPaneId.Items, AppPaneId.Editor]
|
||||
}
|
||||
} else if (screen.isMobile) {
|
||||
if (layout === PaneLayout.TagSelection) {
|
||||
return [AppPaneId.Navigation]
|
||||
} else if (layout === PaneLayout.ItemSelection || layout === PaneLayout.FilesView) {
|
||||
return [AppPaneId.Navigation, AppPaneId.Items]
|
||||
} else if (layout === PaneLayout.Editing) {
|
||||
return [AppPaneId.Navigation, AppPaneId.Items, AppPaneId.Editor]
|
||||
}
|
||||
} else {
|
||||
if (layout === PaneLayout.FilesView) {
|
||||
return [AppPaneId.Navigation, AppPaneId.Items]
|
||||
} else {
|
||||
return [AppPaneId.Navigation, AppPaneId.Items, AppPaneId.Editor]
|
||||
}
|
||||
}
|
||||
|
||||
throw Error(`Unhandled pane layout ${layout}`)
|
||||
}
|
||||
|
||||
export function isPanesChangeLeafDismiss(from: AppPaneId[], to: AppPaneId[]): boolean {
|
||||
const fromWithoutLast = from.slice(0, from.length - 1)
|
||||
|
||||
return fromWithoutLast.length === to.length && fromWithoutLast.every((pane, index) => pane === to[index])
|
||||
}
|
||||
|
||||
export function isPanesChangePush(from: AppPaneId[], to: AppPaneId[]): boolean {
|
||||
const toWithoutLast = to.slice(0, to.length - 1)
|
||||
|
||||
return toWithoutLast.length === from.length && toWithoutLast.every((pane, index) => pane === from[index])
|
||||
}
|
||||
@@ -2,13 +2,10 @@ import { InternalEventBus } from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { action, makeObservable, observable } from 'mobx'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
import { TOGGLE_FOCUS_MODE_COMMAND } from '@standardnotes/ui-services'
|
||||
import { toggleFocusMode } from '@/Utils/toggleFocusMode'
|
||||
|
||||
export class QuickSettingsController extends AbstractViewController {
|
||||
open = false
|
||||
shouldAnimateCloseMenu = false
|
||||
focusModeEnabled = false
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
super(application, eventBus)
|
||||
@@ -16,25 +13,12 @@ export class QuickSettingsController extends AbstractViewController {
|
||||
makeObservable(this, {
|
||||
open: observable,
|
||||
shouldAnimateCloseMenu: observable,
|
||||
focusModeEnabled: observable,
|
||||
|
||||
setOpen: action,
|
||||
setShouldAnimateCloseMenu: action,
|
||||
setFocusModeEnabled: action,
|
||||
toggle: action,
|
||||
closeQuickSettingsMenu: action,
|
||||
})
|
||||
|
||||
this.disposers.push(
|
||||
application.keyboardService.addCommandHandler({
|
||||
command: TOGGLE_FOCUS_MODE_COMMAND,
|
||||
onKeyDown: (event) => {
|
||||
event.preventDefault()
|
||||
this.setFocusModeEnabled(!this.focusModeEnabled)
|
||||
return true
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
setOpen = (open: boolean): void => {
|
||||
@@ -45,12 +29,6 @@ export class QuickSettingsController extends AbstractViewController {
|
||||
this.shouldAnimateCloseMenu = shouldAnimate
|
||||
}
|
||||
|
||||
setFocusModeEnabled = (enabled: boolean): void => {
|
||||
this.focusModeEnabled = enabled
|
||||
|
||||
toggleFocusMode(enabled)
|
||||
}
|
||||
|
||||
toggle = (): void => {
|
||||
if (this.open) {
|
||||
this.closeQuickSettingsMenu()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isMobileScreen } from '@/Utils'
|
||||
import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
import {
|
||||
@@ -18,6 +19,7 @@ import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
import { Persistable } from './Abstract/Persistable'
|
||||
import { CrossControllerEvent } from './CrossControllerEvent'
|
||||
import { ItemListController } from './ItemList/ItemListController'
|
||||
import { PaneLayout } from './PaneController/PaneLayout'
|
||||
|
||||
export class SelectedItemsController
|
||||
extends AbstractViewController
|
||||
@@ -139,6 +141,7 @@ export class SelectedItemsController
|
||||
}
|
||||
|
||||
setSelectedUuids = (selectedUuids: Set<UuidString>) => {
|
||||
log(LoggingDomain.Selection, 'Setting selected uuids', selectedUuids)
|
||||
this.selectedUuids = new Set(selectedUuids)
|
||||
this.setSelectedItems()
|
||||
}
|
||||
@@ -150,6 +153,7 @@ export class SelectedItemsController
|
||||
}
|
||||
|
||||
public deselectItem = (item: { uuid: ListableContentItem['uuid'] }): void => {
|
||||
log(LoggingDomain.Selection, 'Deselecting item', item.uuid)
|
||||
this.removeSelectedItem(item.uuid)
|
||||
|
||||
if (item.uuid === this.lastSelectedItem?.uuid) {
|
||||
@@ -228,7 +232,7 @@ export class SelectedItemsController
|
||||
this.lastSelectedItem = undefined
|
||||
}
|
||||
|
||||
openSingleSelectedItem = async () => {
|
||||
openSingleSelectedItem = async ({ userTriggered } = { userTriggered: true }) => {
|
||||
if (this.selectedItemsCount === 1) {
|
||||
const item = this.firstSelectedItem
|
||||
|
||||
@@ -237,6 +241,10 @@ export class SelectedItemsController
|
||||
} else if (item.content_type === ContentType.File) {
|
||||
await this.itemListController.openFile(item.uuid)
|
||||
}
|
||||
|
||||
if (!this.application.paneController.isInMobileView || userTriggered) {
|
||||
void this.application.paneController.setPaneLayout(PaneLayout.Editing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +262,7 @@ export class SelectedItemsController
|
||||
}
|
||||
}
|
||||
|
||||
log(LoggingDomain.Selection, 'selectItem', item.uuid)
|
||||
log(LoggingDomain.Selection, 'Select item', item.uuid)
|
||||
|
||||
const hasMeta = this.keyboardService.activeModifiers.has(KeyboardModifier.Meta)
|
||||
const hasCtrl = this.keyboardService.activeModifiers.has(KeyboardModifier.Ctrl)
|
||||
@@ -278,7 +286,7 @@ export class SelectedItemsController
|
||||
}
|
||||
}
|
||||
|
||||
await this.openSingleSelectedItem()
|
||||
await this.openSingleSelectedItem({ userTriggered: userTriggered ?? false })
|
||||
|
||||
return {
|
||||
didSelect: this.selectedUuids.has(uuid),
|
||||
@@ -293,7 +301,9 @@ export class SelectedItemsController
|
||||
): Promise<void> => {
|
||||
const { didSelect } = await this.selectItem(item.uuid, userTriggered)
|
||||
|
||||
if (didSelect && scrollIntoView) {
|
||||
const avoidMobileScrollingDueToIncompatibilityWithPaneAnimations = isMobileScreen()
|
||||
|
||||
if (didSelect && scrollIntoView && !avoidMobileScrollingDueToIncompatibilityWithPaneAnimations) {
|
||||
this.scrollToItem(item, animated)
|
||||
}
|
||||
}
|
||||
@@ -319,11 +329,11 @@ export class SelectedItemsController
|
||||
this.setSelectedUuids(new Set(Uuids(itemsForUuids)))
|
||||
|
||||
if (itemsForUuids.length === 1) {
|
||||
void this.openSingleSelectedItem()
|
||||
void this.openSingleSelectedItem({ userTriggered })
|
||||
}
|
||||
}
|
||||
|
||||
selectNextItem = () => {
|
||||
selectNextItem = ({ userTriggered } = { userTriggered: true }) => {
|
||||
const displayableItems = this.itemListController.items
|
||||
|
||||
const currentIndex = displayableItems.findIndex((candidate) => {
|
||||
@@ -341,7 +351,7 @@ export class SelectedItemsController
|
||||
continue
|
||||
}
|
||||
|
||||
this.selectItemWithScrollHandling(nextItem, { userTriggered: true }).catch(console.error)
|
||||
this.selectItemWithScrollHandling(nextItem, { userTriggered }).catch(console.error)
|
||||
|
||||
const nextNoteElement = document.getElementById(nextItem.uuid)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PaneController } from './PaneController'
|
||||
import { PaneController } from './PaneController/PaneController'
|
||||
import {
|
||||
PersistedStateValue,
|
||||
PersistenceKey,
|
||||
|
||||
Reference in New Issue
Block a user