|
|
|
|
@@ -9,12 +9,11 @@ import {
|
|
|
|
|
} from '@standardnotes/ui-services'
|
|
|
|
|
import { WebApplication } from '@/Application/Application'
|
|
|
|
|
import { PANEL_NAME_NOTES } from '@/Constants/Constants'
|
|
|
|
|
import { FileItem, PrefKey } from '@standardnotes/snjs'
|
|
|
|
|
import { FileItem, PrefKey, WebAppEvent } from '@standardnotes/snjs'
|
|
|
|
|
import { observer } from 'mobx-react-lite'
|
|
|
|
|
import { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react'
|
|
|
|
|
import { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react'
|
|
|
|
|
import ContentList from '@/Components/ContentListView/ContentList'
|
|
|
|
|
import NoAccountWarning from '@/Components/NoAccountWarning/NoAccountWarning'
|
|
|
|
|
import PanelResizer, { PanelSide, ResizeFinishCallback, PanelResizeType } from '@/Components/PanelResizer/PanelResizer'
|
|
|
|
|
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
|
|
|
|
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
|
|
|
|
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
|
|
|
|
@@ -24,19 +23,19 @@ import { NotesController } from '@/Controllers/NotesController/NotesController'
|
|
|
|
|
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
|
|
|
|
import { ElementIds } from '@/Constants/ElementIDs'
|
|
|
|
|
import ContentListHeader from './Header/ContentListHeader'
|
|
|
|
|
import ResponsivePaneContent from '../ResponsivePane/ResponsivePaneContent'
|
|
|
|
|
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
|
|
|
|
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
|
|
|
|
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
|
|
|
|
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
|
|
|
|
import { StreamingFileReader } from '@standardnotes/filepicker'
|
|
|
|
|
import SearchBar from '../SearchBar/SearchBar'
|
|
|
|
|
import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
|
|
|
|
|
import { classNames } from '@standardnotes/utils'
|
|
|
|
|
import { MediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
|
|
|
|
import { useFileDragNDrop } from '../FileDragNDropProvider/FileDragNDropProvider'
|
|
|
|
|
import { useFileDragNDrop } from '../FileDragNDropProvider'
|
|
|
|
|
import { LinkingController } from '@/Controllers/LinkingController'
|
|
|
|
|
import DailyContentList from './Daily/DailyContentList'
|
|
|
|
|
import { ListableContentItem } from './Types/ListableContentItem'
|
|
|
|
|
import { FeatureName } from '@/Controllers/FeatureName'
|
|
|
|
|
import { PanelResizedData } from '@/Types/PanelResizedData'
|
|
|
|
|
import { useForwardedRef } from '@/Hooks/useForwardedRef'
|
|
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
|
accountMenuController: AccountMenuController
|
|
|
|
|
@@ -49,235 +48,252 @@ type Props = {
|
|
|
|
|
selectionController: SelectedItemsController
|
|
|
|
|
searchOptionsController: SearchOptionsController
|
|
|
|
|
linkingController: LinkingController
|
|
|
|
|
className?: string
|
|
|
|
|
id: string
|
|
|
|
|
children?: React.ReactNode
|
|
|
|
|
onPanelWidthLoad: (width: number) => void
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ContentListView: FunctionComponent<Props> = ({
|
|
|
|
|
accountMenuController,
|
|
|
|
|
application,
|
|
|
|
|
filesController,
|
|
|
|
|
itemListController,
|
|
|
|
|
navigationController,
|
|
|
|
|
noAccountWarningController,
|
|
|
|
|
notesController,
|
|
|
|
|
selectionController,
|
|
|
|
|
searchOptionsController,
|
|
|
|
|
linkingController,
|
|
|
|
|
}) => {
|
|
|
|
|
const { isNotesListVisibleOnTablets, toggleAppPane } = useResponsiveAppPane()
|
|
|
|
|
const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|
|
|
|
(
|
|
|
|
|
{
|
|
|
|
|
accountMenuController,
|
|
|
|
|
application,
|
|
|
|
|
filesController,
|
|
|
|
|
itemListController,
|
|
|
|
|
navigationController,
|
|
|
|
|
noAccountWarningController,
|
|
|
|
|
notesController,
|
|
|
|
|
selectionController,
|
|
|
|
|
searchOptionsController,
|
|
|
|
|
linkingController,
|
|
|
|
|
className,
|
|
|
|
|
id,
|
|
|
|
|
children,
|
|
|
|
|
onPanelWidthLoad,
|
|
|
|
|
},
|
|
|
|
|
ref,
|
|
|
|
|
) => {
|
|
|
|
|
const { toggleAppPane, panes } = useResponsiveAppPane()
|
|
|
|
|
const { selectedUuids, selectNextItem, selectPreviousItem } = selectionController
|
|
|
|
|
const { selected: selectedTag, selectedAsTag } = navigationController
|
|
|
|
|
const {
|
|
|
|
|
completedFullSync,
|
|
|
|
|
createNewNote,
|
|
|
|
|
optionsSubtitle,
|
|
|
|
|
paginate,
|
|
|
|
|
panelTitle,
|
|
|
|
|
renderedItems,
|
|
|
|
|
items,
|
|
|
|
|
isCurrentNoteTemplate,
|
|
|
|
|
} = itemListController
|
|
|
|
|
|
|
|
|
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
|
|
|
const itemsViewPanelRef = useRef<HTMLDivElement>(null)
|
|
|
|
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
|
|
|
const innerRef = useForwardedRef(ref)
|
|
|
|
|
|
|
|
|
|
const { addDragTarget, removeDragTarget } = useFileDragNDrop()
|
|
|
|
|
const { addDragTarget, removeDragTarget } = useFileDragNDrop()
|
|
|
|
|
|
|
|
|
|
const fileDropCallback = useCallback(
|
|
|
|
|
async (files: FileItem[]) => {
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
return application.addWebEventObserver((event, data) => {
|
|
|
|
|
if (event === WebAppEvent.PanelResized) {
|
|
|
|
|
const { panel, width } = data as PanelResizedData
|
|
|
|
|
if (panel === PANEL_NAME_NOTES) {
|
|
|
|
|
if (selectedAsTag) {
|
|
|
|
|
void navigationController.setPanelWidthForTag(selectedAsTag, width)
|
|
|
|
|
} else {
|
|
|
|
|
void application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}, [application, navigationController, selectedAsTag])
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const panelWidth = selectedTag?.preferences?.panelWidth || application.getPreference(PrefKey.NotesPanelWidth)
|
|
|
|
|
if (panelWidth) {
|
|
|
|
|
onPanelWidthLoad(panelWidth)
|
|
|
|
|
}
|
|
|
|
|
}, [selectedTag, application, onPanelWidthLoad])
|
|
|
|
|
|
|
|
|
|
const fileDropCallback = useCallback(
|
|
|
|
|
async (files: FileItem[]) => {
|
|
|
|
|
const currentTag = navigationController.selected
|
|
|
|
|
|
|
|
|
|
if (!currentTag) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (navigationController.isInAnySystemView() || navigationController.isInSmartView()) {
|
|
|
|
|
console.error('Trying to link uploaded files to smart view')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
files.forEach(async (file) => {
|
|
|
|
|
await linkingController.linkItems(file, currentTag)
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
[navigationController, linkingController],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const target = innerRef.current
|
|
|
|
|
const currentTag = navigationController.selected
|
|
|
|
|
const shouldAddDropTarget = !navigationController.isInAnySystemView() && !navigationController.isInSmartView()
|
|
|
|
|
|
|
|
|
|
if (!currentTag) {
|
|
|
|
|
return
|
|
|
|
|
if (target && shouldAddDropTarget && currentTag) {
|
|
|
|
|
addDragTarget(target, {
|
|
|
|
|
tooltipText: `Drop your files to upload and link them to tag "${currentTag.title}"`,
|
|
|
|
|
callback: fileDropCallback,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (navigationController.isInAnySystemView() || navigationController.isInSmartView()) {
|
|
|
|
|
console.error('Trying to link uploaded files to smart view')
|
|
|
|
|
return
|
|
|
|
|
return () => {
|
|
|
|
|
if (target) {
|
|
|
|
|
removeDragTarget(target)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
files.forEach(async (file) => {
|
|
|
|
|
await linkingController.linkItems(file, currentTag)
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
[navigationController, linkingController],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const target = itemsViewPanelRef.current
|
|
|
|
|
const currentTag = navigationController.selected
|
|
|
|
|
const shouldAddDropTarget = !navigationController.isInAnySystemView() && !navigationController.isInSmartView()
|
|
|
|
|
|
|
|
|
|
if (target && shouldAddDropTarget && currentTag) {
|
|
|
|
|
addDragTarget(target, {
|
|
|
|
|
tooltipText: `Drop your files to upload and link them to tag "${currentTag.title}"`,
|
|
|
|
|
callback: fileDropCallback,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
if (target) {
|
|
|
|
|
removeDragTarget(target)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [addDragTarget, fileDropCallback, navigationController, navigationController.selected, removeDragTarget])
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
completedFullSync,
|
|
|
|
|
createNewNote,
|
|
|
|
|
optionsSubtitle,
|
|
|
|
|
paginate,
|
|
|
|
|
panelTitle,
|
|
|
|
|
panelWidth,
|
|
|
|
|
renderedItems,
|
|
|
|
|
items,
|
|
|
|
|
isCurrentNoteTemplate,
|
|
|
|
|
} = itemListController
|
|
|
|
|
|
|
|
|
|
const { selectedUuids, selectNextItem, selectPreviousItem } = selectionController
|
|
|
|
|
|
|
|
|
|
const { selected: selectedTag, selectedAsTag } = navigationController
|
|
|
|
|
|
|
|
|
|
const icon = selectedTag?.iconString
|
|
|
|
|
|
|
|
|
|
const isFilesSmartView = useMemo(() => navigationController.isInFilesView, [navigationController.isInFilesView])
|
|
|
|
|
|
|
|
|
|
const addNewItem = useCallback(async () => {
|
|
|
|
|
if (isFilesSmartView) {
|
|
|
|
|
if (!application.entitledToFiles) {
|
|
|
|
|
application.showPremiumModal(FeatureName.Files)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (StreamingFileReader.available()) {
|
|
|
|
|
void filesController.uploadNewFile()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileInputRef.current?.click()
|
|
|
|
|
} else {
|
|
|
|
|
await createNewNote()
|
|
|
|
|
toggleAppPane(AppPaneId.Editor)
|
|
|
|
|
}
|
|
|
|
|
}, [isFilesSmartView, filesController, createNewNote, toggleAppPane, application])
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const searchBarElement = document.getElementById(ElementIds.SearchBar)
|
|
|
|
|
/**
|
|
|
|
|
* In the browser we're not allowed to override cmd/ctrl + n, so we have to
|
|
|
|
|
* use Control modifier as well. These rules don't apply to desktop, but
|
|
|
|
|
* probably better to be consistent.
|
|
|
|
|
*/
|
|
|
|
|
return application.keyboardService.addCommandHandlers([
|
|
|
|
|
{
|
|
|
|
|
command: CREATE_NEW_NOTE_KEYBOARD_COMMAND,
|
|
|
|
|
onKeyDown: (event) => {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
void addNewItem()
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: NEXT_LIST_ITEM_KEYBOARD_COMMAND,
|
|
|
|
|
elements: [document.body, ...(searchBarElement ? [searchBarElement] : [])],
|
|
|
|
|
onKeyDown: () => {
|
|
|
|
|
if (searchBarElement === document.activeElement) {
|
|
|
|
|
searchBarElement?.blur()
|
|
|
|
|
}
|
|
|
|
|
selectNextItem()
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
|
|
|
|
|
element: document.body,
|
|
|
|
|
onKeyDown: () => {
|
|
|
|
|
selectPreviousItem()
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: SEARCH_KEYBOARD_COMMAND,
|
|
|
|
|
onKeyDown: (event) => {
|
|
|
|
|
if (searchBarElement) {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
searchBarElement.focus()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: CANCEL_SEARCH_COMMAND,
|
|
|
|
|
onKeyDown: () => {
|
|
|
|
|
if (searchBarElement) {
|
|
|
|
|
searchBarElement.blur()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: SELECT_ALL_ITEMS_KEYBOARD_COMMAND,
|
|
|
|
|
onKeyDown: (event) => {
|
|
|
|
|
const isTargetInsideContentList = (event.target as HTMLElement).closest(`#${ElementIds.ContentList}`)
|
|
|
|
|
|
|
|
|
|
if (!isTargetInsideContentList) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
selectionController.selectAll()
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, [
|
|
|
|
|
addDragTarget,
|
|
|
|
|
fileDropCallback,
|
|
|
|
|
navigationController,
|
|
|
|
|
navigationController.selected,
|
|
|
|
|
removeDragTarget,
|
|
|
|
|
innerRef,
|
|
|
|
|
])
|
|
|
|
|
}, [addNewItem, application.keyboardService, createNewNote, selectNextItem, selectPreviousItem, selectionController])
|
|
|
|
|
|
|
|
|
|
const panelResizeFinishCallback: ResizeFinishCallback = useCallback(
|
|
|
|
|
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
|
|
|
|
|
if (selectedAsTag) {
|
|
|
|
|
void navigationController.setPanelWidthForTag(selectedAsTag, width)
|
|
|
|
|
const icon = selectedTag?.iconString
|
|
|
|
|
|
|
|
|
|
const isFilesSmartView = useMemo(() => navigationController.isInFilesView, [navigationController.isInFilesView])
|
|
|
|
|
|
|
|
|
|
const addNewItem = useCallback(async () => {
|
|
|
|
|
if (isFilesSmartView) {
|
|
|
|
|
if (!application.entitledToFiles) {
|
|
|
|
|
application.showPremiumModal(FeatureName.Files)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (StreamingFileReader.available()) {
|
|
|
|
|
void filesController.uploadNewFile()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileInputRef.current?.click()
|
|
|
|
|
} else {
|
|
|
|
|
void application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error)
|
|
|
|
|
await createNewNote()
|
|
|
|
|
toggleAppPane(AppPaneId.Editor)
|
|
|
|
|
}
|
|
|
|
|
application.publishPanelDidResizeEvent(PANEL_NAME_NOTES, isCollapsed)
|
|
|
|
|
},
|
|
|
|
|
[application, selectedAsTag, navigationController],
|
|
|
|
|
)
|
|
|
|
|
}, [isFilesSmartView, filesController, createNewNote, toggleAppPane, application])
|
|
|
|
|
|
|
|
|
|
const shortcutForCreate = useMemo(
|
|
|
|
|
() => application.keyboardService.keyboardShortcutForCommand(CREATE_NEW_NOTE_KEYBOARD_COMMAND),
|
|
|
|
|
[application],
|
|
|
|
|
)
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const searchBarElement = document.getElementById(ElementIds.SearchBar)
|
|
|
|
|
/**
|
|
|
|
|
* In the browser we're not allowed to override cmd/ctrl + n, so we have to
|
|
|
|
|
* use Control modifier as well. These rules don't apply to desktop, but
|
|
|
|
|
* probably better to be consistent.
|
|
|
|
|
*/
|
|
|
|
|
return application.keyboardService.addCommandHandlers([
|
|
|
|
|
{
|
|
|
|
|
command: CREATE_NEW_NOTE_KEYBOARD_COMMAND,
|
|
|
|
|
onKeyDown: (event) => {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
void addNewItem()
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: NEXT_LIST_ITEM_KEYBOARD_COMMAND,
|
|
|
|
|
elements: [document.body, ...(searchBarElement ? [searchBarElement] : [])],
|
|
|
|
|
onKeyDown: () => {
|
|
|
|
|
if (searchBarElement === document.activeElement) {
|
|
|
|
|
searchBarElement?.blur()
|
|
|
|
|
}
|
|
|
|
|
selectNextItem()
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
|
|
|
|
|
element: document.body,
|
|
|
|
|
onKeyDown: () => {
|
|
|
|
|
selectPreviousItem()
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: SEARCH_KEYBOARD_COMMAND,
|
|
|
|
|
onKeyDown: (event) => {
|
|
|
|
|
if (searchBarElement) {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
searchBarElement.focus()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: CANCEL_SEARCH_COMMAND,
|
|
|
|
|
onKeyDown: () => {
|
|
|
|
|
if (searchBarElement) {
|
|
|
|
|
searchBarElement.blur()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
command: SELECT_ALL_ITEMS_KEYBOARD_COMMAND,
|
|
|
|
|
onKeyDown: (event) => {
|
|
|
|
|
const isTargetInsideContentList = (event.target as HTMLElement).closest(`#${ElementIds.ContentList}`)
|
|
|
|
|
|
|
|
|
|
const addButtonLabel = useMemo(() => {
|
|
|
|
|
return isFilesSmartView
|
|
|
|
|
? 'Upload file'
|
|
|
|
|
: `Create a new note in the selected tag (${shortcutForCreate && keyboardStringForShortcut(shortcutForCreate)})`
|
|
|
|
|
}, [isFilesSmartView, shortcutForCreate])
|
|
|
|
|
if (!isTargetInsideContentList) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const matchesMediumBreakpoint = useMediaQuery(MediaQueryBreakpoints.md)
|
|
|
|
|
const matchesXLBreakpoint = useMediaQuery(MediaQueryBreakpoints.xl)
|
|
|
|
|
const isTabletScreenSize = matchesMediumBreakpoint && !matchesXLBreakpoint
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
selectionController.selectAll()
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
])
|
|
|
|
|
}, [
|
|
|
|
|
addNewItem,
|
|
|
|
|
application.keyboardService,
|
|
|
|
|
createNewNote,
|
|
|
|
|
selectNextItem,
|
|
|
|
|
selectPreviousItem,
|
|
|
|
|
selectionController,
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const dailyMode = selectedAsTag?.isDailyEntry
|
|
|
|
|
const shortcutForCreate = useMemo(
|
|
|
|
|
() => application.keyboardService.keyboardShortcutForCommand(CREATE_NEW_NOTE_KEYBOARD_COMMAND),
|
|
|
|
|
[application],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const handleDailyListSelection = useCallback(
|
|
|
|
|
async (item: ListableContentItem, userTriggered: boolean) => {
|
|
|
|
|
await selectionController.selectItemWithScrollHandling(item, {
|
|
|
|
|
userTriggered: true,
|
|
|
|
|
scrollIntoView: userTriggered === false,
|
|
|
|
|
animated: false,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
[selectionController],
|
|
|
|
|
)
|
|
|
|
|
const addButtonLabel = useMemo(() => {
|
|
|
|
|
return isFilesSmartView
|
|
|
|
|
? 'Upload file'
|
|
|
|
|
: `Create a new note in the selected tag (${shortcutForCreate && keyboardStringForShortcut(shortcutForCreate)})`
|
|
|
|
|
}, [isFilesSmartView, shortcutForCreate])
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const hasEditorPane = selectedUuids.size > 0 || renderedItems.length === 0 || isCurrentNoteTemplate
|
|
|
|
|
if (!hasEditorPane) {
|
|
|
|
|
itemsViewPanelRef.current?.style.removeProperty('width')
|
|
|
|
|
}
|
|
|
|
|
}, [selectedUuids, itemsViewPanelRef, isCurrentNoteTemplate, renderedItems])
|
|
|
|
|
const dailyMode = selectedAsTag?.isDailyEntry
|
|
|
|
|
|
|
|
|
|
const hasEditorPane = selectedUuids.size > 0 || renderedItems.length === 0 || isCurrentNoteTemplate
|
|
|
|
|
const handleDailyListSelection = useCallback(
|
|
|
|
|
async (item: ListableContentItem, userTriggered: boolean) => {
|
|
|
|
|
await selectionController.selectItemWithScrollHandling(item, {
|
|
|
|
|
userTriggered: true,
|
|
|
|
|
scrollIntoView: userTriggered === false,
|
|
|
|
|
animated: false,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
[selectionController],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
id="items-column"
|
|
|
|
|
className={classNames(
|
|
|
|
|
'sn-component section app-column flex h-full flex-col overflow-hidden pt-safe-top',
|
|
|
|
|
hasEditorPane ? 'xl:w-[24rem] xsm-only:!w-full sm-only:!w-full' : 'w-full md:min-w-[400px]',
|
|
|
|
|
hasEditorPane
|
|
|
|
|
? isTabletScreenSize && !isNotesListVisibleOnTablets
|
|
|
|
|
? 'pointer-coarse:md-only:!w-0 pointer-coarse:lg-only:!w-0'
|
|
|
|
|
: 'pointer-coarse:md-only:!w-60 pointer-coarse:lg-only:!w-60'
|
|
|
|
|
: '',
|
|
|
|
|
)}
|
|
|
|
|
aria-label={'Notes & Files'}
|
|
|
|
|
ref={itemsViewPanelRef}
|
|
|
|
|
>
|
|
|
|
|
<ResponsivePaneContent className="overflow-hidden" paneId={AppPaneId.Items}>
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const hasEditorPane = panes.includes(AppPaneId.Editor)
|
|
|
|
|
if (!hasEditorPane) {
|
|
|
|
|
innerRef.current?.style.removeProperty('width')
|
|
|
|
|
}
|
|
|
|
|
}, [selectedUuids, innerRef, isCurrentNoteTemplate, renderedItems, panes])
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
id={id}
|
|
|
|
|
className={classNames(className, 'sn-component section h-full overflow-hidden pt-safe-top')}
|
|
|
|
|
aria-label={'Notes & Files'}
|
|
|
|
|
ref={innerRef}
|
|
|
|
|
>
|
|
|
|
|
<div id="items-title-bar" className="section-title-bar border-b border-solid border-border">
|
|
|
|
|
<div id="items-title-bar-container">
|
|
|
|
|
<input
|
|
|
|
|
@@ -325,14 +341,16 @@ const ContentListView: FunctionComponent<Props> = ({
|
|
|
|
|
onSelect={handleDailyListSelection}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
{!dailyMode && completedFullSync && !renderedItems.length ? (
|
|
|
|
|
<p className="empty-items-list opacity-50">No items.</p>
|
|
|
|
|
) : null}
|
|
|
|
|
|
|
|
|
|
{!dailyMode && !completedFullSync && !renderedItems.length ? (
|
|
|
|
|
<p className="empty-items-list opacity-50">Loading...</p>
|
|
|
|
|
) : null}
|
|
|
|
|
|
|
|
|
|
{!dailyMode && renderedItems.length ? (
|
|
|
|
|
<>
|
|
|
|
|
{completedFullSync && !renderedItems.length ? (
|
|
|
|
|
<p className="empty-items-list opacity-50">No items.</p>
|
|
|
|
|
) : null}
|
|
|
|
|
{!completedFullSync && !renderedItems.length ? (
|
|
|
|
|
<p className="empty-items-list opacity-50">Loading...</p>
|
|
|
|
|
) : null}
|
|
|
|
|
<ContentList
|
|
|
|
|
items={renderedItems}
|
|
|
|
|
selectedUuids={selectedUuids}
|
|
|
|
|
@@ -347,22 +365,10 @@ const ContentListView: FunctionComponent<Props> = ({
|
|
|
|
|
</>
|
|
|
|
|
) : null}
|
|
|
|
|
<div className="absolute bottom-0 h-safe-bottom w-full" />
|
|
|
|
|
</ResponsivePaneContent>
|
|
|
|
|
{hasEditorPane && itemsViewPanelRef.current && (
|
|
|
|
|
<PanelResizer
|
|
|
|
|
collapsable={true}
|
|
|
|
|
hoverable={true}
|
|
|
|
|
defaultWidth={300}
|
|
|
|
|
panel={itemsViewPanelRef.current}
|
|
|
|
|
side={PanelSide.Right}
|
|
|
|
|
type={PanelResizeType.WidthOnly}
|
|
|
|
|
resizeFinishCallback={panelResizeFinishCallback}
|
|
|
|
|
width={panelWidth}
|
|
|
|
|
left={0}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
{children}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
export default observer(ContentListView)
|
|
|
|
|
|