274 lines
8.1 KiB
TypeScript
274 lines
8.1 KiB
TypeScript
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
|
|
|
|
listPaneExplicitelyCollapsed = false
|
|
navigationPaneExplicitelyCollapsed = 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)
|
|
|
|
const panes = panesForLayout(layout, this.application)
|
|
|
|
if (panes.includes(AppPaneId.Items) && this.listPaneExplicitelyCollapsed) {
|
|
removeFromArray(panes, AppPaneId.Items)
|
|
}
|
|
|
|
if (panes.includes(AppPaneId.Navigation) && this.navigationPaneExplicitelyCollapsed) {
|
|
removeFromArray(panes, AppPaneId.Navigation)
|
|
}
|
|
|
|
this.replacePanes(panes)
|
|
}
|
|
|
|
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)
|
|
this.listPaneExplicitelyCollapsed = true
|
|
} else {
|
|
if (this.panes.includes(AppPaneId.Navigation)) {
|
|
this.insertPaneAtIndex(AppPaneId.Items, 1)
|
|
} else {
|
|
this.insertPaneAtIndex(AppPaneId.Items, 0)
|
|
}
|
|
this.listPaneExplicitelyCollapsed = false
|
|
}
|
|
}
|
|
|
|
toggleNavigationPane = () => {
|
|
if (this.panes.includes(AppPaneId.Navigation)) {
|
|
this.removePane(AppPaneId.Navigation)
|
|
this.navigationPaneExplicitelyCollapsed = true
|
|
} else {
|
|
this.insertPaneAtIndex(AppPaneId.Navigation, 0)
|
|
this.navigationPaneExplicitelyCollapsed = false
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
}
|