feat: move display options to icon button in header (#1138)
This commit is contained in:
@@ -66,7 +66,7 @@ const ContentList: FunctionComponent<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="infinite-scroll focus:shadow-none focus:outline-none"
|
className="infinite-scroll border-solid border-0 border-t-1px border-main focus:shadow-none focus:outline-none"
|
||||||
id={ElementIds.ContentList}
|
id={ElementIds.ContentList}
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
|
|||||||
@@ -17,10 +17,6 @@ import ContentList from '@/Components/ContentListView/ContentList'
|
|||||||
import NoAccountWarning from '@/Components/NoAccountWarning/NoAccountWarning'
|
import NoAccountWarning from '@/Components/NoAccountWarning/NoAccountWarning'
|
||||||
import SearchOptions from '@/Components/SearchOptions/SearchOptions'
|
import SearchOptions from '@/Components/SearchOptions/SearchOptions'
|
||||||
import PanelResizer, { PanelSide, ResizeFinishCallback, PanelResizeType } from '@/Components/PanelResizer/PanelResizer'
|
import PanelResizer, { PanelSide, ResizeFinishCallback, PanelResizeType } from '@/Components/PanelResizer/PanelResizer'
|
||||||
import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure'
|
|
||||||
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'
|
|
||||||
import ContentListOptionsMenu from './ContentListOptionsMenu'
|
|
||||||
import Icon from '@/Components/Icon/Icon'
|
|
||||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||||
@@ -31,6 +27,7 @@ import { NoAccountWarningController } from '@/Controllers/NoAccountWarningContro
|
|||||||
import { NotesController } from '@/Controllers/NotesController'
|
import { NotesController } from '@/Controllers/NotesController'
|
||||||
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
||||||
import { ElementIds } from '@/Constants/ElementIDs'
|
import { ElementIds } from '@/Constants/ElementIDs'
|
||||||
|
import ContentListHeader from './Header/ContentListHeader'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
accountMenuController: AccountMenuController
|
accountMenuController: AccountMenuController
|
||||||
@@ -58,32 +55,28 @@ const ContentListView: FunctionComponent<Props> = ({
|
|||||||
selectionController,
|
selectionController,
|
||||||
}) => {
|
}) => {
|
||||||
const itemsViewPanelRef = useRef<HTMLDivElement>(null)
|
const itemsViewPanelRef = useRef<HTMLDivElement>(null)
|
||||||
const displayOptionsMenuRef = useRef<HTMLDivElement>(null)
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
clearFilterText,
|
||||||
completedFullSync,
|
completedFullSync,
|
||||||
|
createNewNote,
|
||||||
noteFilterText,
|
noteFilterText,
|
||||||
|
onFilterEnter,
|
||||||
optionsSubtitle,
|
optionsSubtitle,
|
||||||
|
paginate,
|
||||||
panelTitle,
|
panelTitle,
|
||||||
|
panelWidth,
|
||||||
renderedItems,
|
renderedItems,
|
||||||
setNoteFilterText,
|
|
||||||
searchBarElement,
|
searchBarElement,
|
||||||
selectNextItem,
|
selectNextItem,
|
||||||
selectPreviousItem,
|
selectPreviousItem,
|
||||||
onFilterEnter,
|
setNoteFilterText,
|
||||||
clearFilterText,
|
|
||||||
paginate,
|
|
||||||
panelWidth,
|
|
||||||
createNewNote,
|
|
||||||
} = itemListController
|
} = itemListController
|
||||||
|
|
||||||
const { selectedItems } = selectionController
|
const { selectedItems } = selectionController
|
||||||
|
|
||||||
const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false)
|
|
||||||
const [focusedSearch, setFocusedSearch] = useState(false)
|
const [focusedSearch, setFocusedSearch] = useState(false)
|
||||||
|
|
||||||
const [closeDisplayOptMenuOnBlur] = useCloseOnBlur(displayOptionsMenuRef, setShowDisplayOptionsMenu)
|
|
||||||
|
|
||||||
const isFilesSmartView = useMemo(
|
const isFilesSmartView = useMemo(
|
||||||
() => navigationController.selected?.uuid === SystemViewId.Files,
|
() => navigationController.selected?.uuid === SystemViewId.Files,
|
||||||
[navigationController.selected?.uuid],
|
[navigationController.selected?.uuid],
|
||||||
@@ -205,10 +198,6 @@ const ContentListView: FunctionComponent<Props> = ({
|
|||||||
noteTagsController.reloadTagsContainerMaxWidth()
|
noteTagsController.reloadTagsContainerMaxWidth()
|
||||||
}, [noteTagsController])
|
}, [noteTagsController])
|
||||||
|
|
||||||
const toggleDisplayOptionsMenu = useCallback(() => {
|
|
||||||
setShowDisplayOptionsMenu(!showDisplayOptionsMenu)
|
|
||||||
}, [showDisplayOptionsMenu])
|
|
||||||
|
|
||||||
const addButtonLabel = useMemo(
|
const addButtonLabel = useMemo(
|
||||||
() => (isFilesSmartView ? 'Upload file' : 'Create a new note in the selected tag'),
|
() => (isFilesSmartView ? 'Upload file' : 'Create a new note in the selected tag'),
|
||||||
[isFilesSmartView],
|
[isFilesSmartView],
|
||||||
@@ -224,17 +213,14 @@ const ContentListView: FunctionComponent<Props> = ({
|
|||||||
<div className="content">
|
<div className="content">
|
||||||
<div id="items-title-bar" className="section-title-bar">
|
<div id="items-title-bar" className="section-title-bar">
|
||||||
<div id="items-title-bar-container">
|
<div id="items-title-bar-container">
|
||||||
<div className="section-title-bar-header">
|
<ContentListHeader
|
||||||
<div className="sk-h2 font-semibold title">{panelTitle}</div>
|
application={application}
|
||||||
<button
|
panelTitle={panelTitle}
|
||||||
className="flex items-center px-5 py-1 bg-contrast hover:brightness-130 color-text border-0 cursor-pointer"
|
addButtonLabel={addButtonLabel}
|
||||||
title={addButtonLabel}
|
addNewItem={addNewItem}
|
||||||
aria-label={addButtonLabel}
|
isFilesSmartView={isFilesSmartView}
|
||||||
onClick={addNewItem}
|
optionsSubtitle={optionsSubtitle}
|
||||||
>
|
/>
|
||||||
<Icon type="add" className="w-3.5 h-3.5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="filter-section" role="search">
|
<div className="filter-section" role="search">
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
@@ -268,38 +254,6 @@ const ContentListView: FunctionComponent<Props> = ({
|
|||||||
noAccountWarningController={noAccountWarningController}
|
noAccountWarningController={noAccountWarningController}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div id="items-menu-bar" className="sn-component" ref={displayOptionsMenuRef}>
|
|
||||||
<div className="sk-app-bar no-edges">
|
|
||||||
<div className="left">
|
|
||||||
<Disclosure open={showDisplayOptionsMenu} onChange={toggleDisplayOptionsMenu}>
|
|
||||||
<DisclosureButton
|
|
||||||
className={`sk-app-bar-item bg-contrast color-text border-0 focus:shadow-none ${
|
|
||||||
showDisplayOptionsMenu ? 'selected' : ''
|
|
||||||
}`}
|
|
||||||
onBlur={closeDisplayOptMenuOnBlur}
|
|
||||||
>
|
|
||||||
<div className="sk-app-bar-item-column">
|
|
||||||
<div className="sk-label">Options</div>
|
|
||||||
</div>
|
|
||||||
<div className="sk-app-bar-item-column">
|
|
||||||
<div className="sk-sublabel">{optionsSubtitle}</div>
|
|
||||||
</div>
|
|
||||||
</DisclosureButton>
|
|
||||||
<DisclosurePanel onBlur={closeDisplayOptMenuOnBlur}>
|
|
||||||
{showDisplayOptionsMenu && (
|
|
||||||
<ContentListOptionsMenu
|
|
||||||
application={application}
|
|
||||||
closeDisplayOptionsMenu={toggleDisplayOptionsMenu}
|
|
||||||
closeOnBlur={closeDisplayOptMenuOnBlur}
|
|
||||||
isOpen={showDisplayOptionsMenu}
|
|
||||||
navigationController={navigationController}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</DisclosurePanel>
|
|
||||||
</Disclosure>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{completedFullSync && !renderedItems.length ? <p className="empty-items-list faded">No items.</p> : null}
|
{completedFullSync && !renderedItems.length ? <p className="empty-items-list faded">No items.</p> : null}
|
||||||
{!completedFullSync && !renderedItems.length ? <p className="empty-items-list faded">Loading...</p> : null}
|
{!completedFullSync && !renderedItems.length ? <p className="empty-items-list faded">Loading...</p> : null}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { WebApplication } from '@/Application/Application'
|
||||||
|
import { Disclosure, DisclosurePanel } from '@reach/disclosure'
|
||||||
|
import { memo, useCallback, useRef, useState } from 'react'
|
||||||
|
import Icon from '../../Icon/Icon'
|
||||||
|
import { DisplayOptionsMenuPositionProps } from './DisplayOptionsMenuProps'
|
||||||
|
import DisplayOptionsMenuPortal from './DisplayOptionsMenuPortal'
|
||||||
|
import StyledDisplayOptionsButton from './StyledDisplayOptionsButton'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
application: {
|
||||||
|
getPreference: WebApplication['getPreference']
|
||||||
|
setPreference: WebApplication['setPreference']
|
||||||
|
}
|
||||||
|
panelTitle: string
|
||||||
|
addButtonLabel: string
|
||||||
|
addNewItem: () => void
|
||||||
|
isFilesSmartView: boolean
|
||||||
|
optionsSubtitle?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContentListHeader = ({
|
||||||
|
application,
|
||||||
|
panelTitle,
|
||||||
|
addButtonLabel,
|
||||||
|
addNewItem,
|
||||||
|
isFilesSmartView,
|
||||||
|
optionsSubtitle,
|
||||||
|
}: Props) => {
|
||||||
|
const [displayOptionsMenuPosition, setDisplayOptionsMenuPosition] = useState<DisplayOptionsMenuPositionProps>()
|
||||||
|
const displayOptionsContainerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const displayOptionsButtonRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
|
const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false)
|
||||||
|
|
||||||
|
const toggleDisplayOptionsMenu = useCallback(() => {
|
||||||
|
if (displayOptionsButtonRef.current) {
|
||||||
|
const buttonBoundingRect = displayOptionsButtonRef.current.getBoundingClientRect()
|
||||||
|
setDisplayOptionsMenuPosition({
|
||||||
|
top: buttonBoundingRect.bottom,
|
||||||
|
left: buttonBoundingRect.right - buttonBoundingRect.width,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowDisplayOptionsMenu((show) => !show)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="section-title-bar-header">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="text-lg font-semibold title">{panelTitle}</div>
|
||||||
|
{optionsSubtitle && <div className="text-xs color-passive-0">{optionsSubtitle}</div>}
|
||||||
|
</div>
|
||||||
|
<div className="flex">
|
||||||
|
<div className="relative" ref={displayOptionsContainerRef}>
|
||||||
|
<Disclosure open={showDisplayOptionsMenu} onChange={toggleDisplayOptionsMenu}>
|
||||||
|
<StyledDisplayOptionsButton pressed={showDisplayOptionsMenu} ref={displayOptionsButtonRef}>
|
||||||
|
<Icon type="sort-descending" className="w-5 h-5" />
|
||||||
|
</StyledDisplayOptionsButton>
|
||||||
|
<DisclosurePanel>
|
||||||
|
{showDisplayOptionsMenu && displayOptionsMenuPosition && (
|
||||||
|
<DisplayOptionsMenuPortal
|
||||||
|
application={application}
|
||||||
|
closeDisplayOptionsMenu={toggleDisplayOptionsMenu}
|
||||||
|
containerRef={displayOptionsContainerRef}
|
||||||
|
isOpen={showDisplayOptionsMenu}
|
||||||
|
isFilesSmartView={isFilesSmartView}
|
||||||
|
top={displayOptionsMenuPosition.top}
|
||||||
|
left={displayOptionsMenuPosition.left}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DisclosurePanel>
|
||||||
|
</Disclosure>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="flex justify-center items-center min-w-8 h-8 ml-3 bg-info hover:brightness-130 color-info-contrast border-1 border-solid border-transparent rounded-full cursor-pointer"
|
||||||
|
title={addButtonLabel}
|
||||||
|
aria-label={addButtonLabel}
|
||||||
|
onClick={addNewItem}
|
||||||
|
>
|
||||||
|
<Icon type="add" className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ContentListHeader)
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { WebApplication } from '@/Application/Application'
|
import { CollectionSort, CollectionSortProperty, PrefKey } from '@standardnotes/snjs'
|
||||||
import { CollectionSort, CollectionSortProperty, PrefKey, SystemViewId } from '@standardnotes/snjs'
|
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { FunctionComponent, useCallback, useState } from 'react'
|
import { FunctionComponent, useCallback, useState } from 'react'
|
||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
@@ -7,22 +6,13 @@ import Menu from '@/Components/Menu/Menu'
|
|||||||
import MenuItem from '@/Components/Menu/MenuItem'
|
import MenuItem from '@/Components/Menu/MenuItem'
|
||||||
import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
|
import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
|
||||||
import { MenuItemType } from '@/Components/Menu/MenuItemType'
|
import { MenuItemType } from '@/Components/Menu/MenuItemType'
|
||||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
import { DisplayOptionsMenuProps } from './DisplayOptionsMenuProps'
|
||||||
|
|
||||||
type Props = {
|
const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||||
application: WebApplication
|
|
||||||
closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void
|
|
||||||
closeDisplayOptionsMenu: () => void
|
|
||||||
isOpen: boolean
|
|
||||||
navigationController: NavigationController
|
|
||||||
}
|
|
||||||
|
|
||||||
const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|
||||||
closeDisplayOptionsMenu,
|
closeDisplayOptionsMenu,
|
||||||
closeOnBlur,
|
|
||||||
application,
|
application,
|
||||||
isOpen,
|
isOpen,
|
||||||
navigationController,
|
isFilesSmartView,
|
||||||
}) => {
|
}) => {
|
||||||
const [sortBy, setSortBy] = useState(() => application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt))
|
const [sortBy, setSortBy] = useState(() => application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt))
|
||||||
const [sortReverse, setSortReverse] = useState(() => application.getPreference(PrefKey.SortNotesReverse, false))
|
const [sortReverse, setSortReverse] = useState(() => application.getPreference(PrefKey.SortNotesReverse, false))
|
||||||
@@ -109,9 +99,9 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
className={
|
className={
|
||||||
'sn-dropdown sn-dropdown--animated min-w-70 overflow-y-auto \
|
'py-1 sn-dropdown sn-dropdown--animated min-w-70 overflow-y-auto \
|
||||||
border-1 border-solid border-main text-sm z-index-dropdown-menu \
|
border-1 border-solid border-main text-sm z-index-dropdown-menu \
|
||||||
flex flex-col py-2 top-full left-2 absolute'
|
flex flex-col'
|
||||||
}
|
}
|
||||||
a11yLabel="Notes list options menu"
|
a11yLabel="Notes list options menu"
|
||||||
closeMenu={closeDisplayOptionsMenu}
|
closeMenu={closeDisplayOptionsMenu}
|
||||||
@@ -123,7 +113,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
type={MenuItemType.RadioButton}
|
type={MenuItemType.RadioButton}
|
||||||
onClick={toggleSortByDateModified}
|
onClick={toggleSortByDateModified}
|
||||||
checked={sortBy === CollectionSort.UpdatedAt}
|
checked={sortBy === CollectionSort.UpdatedAt}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
<div className="flex flex-grow items-center justify-between ml-2">
|
<div className="flex flex-grow items-center justify-between ml-2">
|
||||||
<span>Date modified</span>
|
<span>Date modified</span>
|
||||||
@@ -141,7 +130,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
type={MenuItemType.RadioButton}
|
type={MenuItemType.RadioButton}
|
||||||
onClick={toggleSortByCreationDate}
|
onClick={toggleSortByCreationDate}
|
||||||
checked={sortBy === CollectionSort.CreatedAt}
|
checked={sortBy === CollectionSort.CreatedAt}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
<div className="flex flex-grow items-center justify-between ml-2">
|
<div className="flex flex-grow items-center justify-between ml-2">
|
||||||
<span>Creation date</span>
|
<span>Creation date</span>
|
||||||
@@ -159,7 +147,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
type={MenuItemType.RadioButton}
|
type={MenuItemType.RadioButton}
|
||||||
onClick={toggleSortByTitle}
|
onClick={toggleSortByTitle}
|
||||||
checked={sortBy === CollectionSort.Title}
|
checked={sortBy === CollectionSort.Title}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
<div className="flex flex-grow items-center justify-between ml-2">
|
<div className="flex flex-grow items-center justify-between ml-2">
|
||||||
<span>Title</span>
|
<span>Title</span>
|
||||||
@@ -174,13 +161,12 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItemSeparator />
|
<MenuItemSeparator />
|
||||||
<div className="px-3 py-1 text-xs font-semibold color-text uppercase">View</div>
|
<div className="px-3 py-1 text-xs font-semibold color-text uppercase">View</div>
|
||||||
{navigationController.selectedUuid !== SystemViewId.Files && (
|
{!isFilesSmartView && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
type={MenuItemType.SwitchButton}
|
type={MenuItemType.SwitchButton}
|
||||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||||
checked={!hidePreview}
|
checked={!hidePreview}
|
||||||
onChange={toggleHidePreview}
|
onChange={toggleHidePreview}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
<div className="flex flex-col max-w-3/4">Show note preview</div>
|
<div className="flex flex-col max-w-3/4">Show note preview</div>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -190,7 +176,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||||
checked={!hideDate}
|
checked={!hideDate}
|
||||||
onChange={toggleHideDate}
|
onChange={toggleHideDate}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
Show date
|
Show date
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -199,7 +184,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||||
checked={!hideTags}
|
checked={!hideTags}
|
||||||
onChange={toggleHideTags}
|
onChange={toggleHideTags}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
Show tags
|
Show tags
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -208,7 +192,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||||
checked={!hideEditorIcon}
|
checked={!hideEditorIcon}
|
||||||
onChange={toggleEditorIcon}
|
onChange={toggleEditorIcon}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
Show icon
|
Show icon
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -219,7 +202,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||||
checked={!hidePinned}
|
checked={!hidePinned}
|
||||||
onChange={toggleHidePinned}
|
onChange={toggleHidePinned}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
Show pinned
|
Show pinned
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -228,7 +210,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||||
checked={!hideProtected}
|
checked={!hideProtected}
|
||||||
onChange={toggleHideProtected}
|
onChange={toggleHideProtected}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
Show protected
|
Show protected
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -237,7 +218,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||||
checked={showArchived}
|
checked={showArchived}
|
||||||
onChange={toggleShowArchived}
|
onChange={toggleShowArchived}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
Show archived
|
Show archived
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -246,7 +226,6 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||||
checked={showTrashed}
|
checked={showTrashed}
|
||||||
onChange={toggleShowTrashed}
|
onChange={toggleShowTrashed}
|
||||||
onBlur={closeOnBlur}
|
|
||||||
>
|
>
|
||||||
Show trashed
|
Show trashed
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -254,4 +233,4 @@ const ContentListOptionsMenu: FunctionComponent<Props> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default observer(ContentListOptionsMenu)
|
export default observer(DisplayOptionsMenu)
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { createPortal } from 'react-dom'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { DisplayOptionsMenuPositionProps, DisplayOptionsMenuProps } from './DisplayOptionsMenuProps'
|
||||||
|
import DisplayOptionsMenu from './DisplayOptionsMenu'
|
||||||
|
import { useRef, useEffect, RefObject } from 'react'
|
||||||
|
|
||||||
|
type Props = DisplayOptionsMenuProps &
|
||||||
|
DisplayOptionsMenuPositionProps & {
|
||||||
|
containerRef: RefObject<HTMLDivElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
const PositionedOptionsMenu = styled.div<DisplayOptionsMenuPositionProps>`
|
||||||
|
position: absolute;
|
||||||
|
top: ${(props) => props.top}px;
|
||||||
|
left: ${(props) => props.left}px;
|
||||||
|
z-index: var(--z-index-dropdown-menu);
|
||||||
|
`
|
||||||
|
|
||||||
|
const DisplayOptionsMenuPortal = ({
|
||||||
|
application,
|
||||||
|
closeDisplayOptionsMenu,
|
||||||
|
containerRef,
|
||||||
|
isFilesSmartView,
|
||||||
|
isOpen,
|
||||||
|
top,
|
||||||
|
left,
|
||||||
|
}: Props) => {
|
||||||
|
const menuRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const closeIfClickedOutside = (event: MouseEvent) => {
|
||||||
|
const isDescendantOfMenu = menuRef.current?.contains(event.target as Node)
|
||||||
|
const isDescendantOfContainer = containerRef.current?.contains(event.target as Node)
|
||||||
|
|
||||||
|
if (!isDescendantOfMenu && !isDescendantOfContainer) {
|
||||||
|
closeDisplayOptionsMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('click', closeIfClickedOutside, { capture: true })
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('click', closeIfClickedOutside, {
|
||||||
|
capture: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [closeDisplayOptionsMenu, containerRef])
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<PositionedOptionsMenu top={top} left={left} ref={menuRef}>
|
||||||
|
<div className="sn-component">
|
||||||
|
<DisplayOptionsMenu
|
||||||
|
application={application}
|
||||||
|
closeDisplayOptionsMenu={closeDisplayOptionsMenu}
|
||||||
|
isFilesSmartView={isFilesSmartView}
|
||||||
|
isOpen={isOpen}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</PositionedOptionsMenu>,
|
||||||
|
document.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DisplayOptionsMenuPortal
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { WebApplication } from '@/Application/Application'
|
||||||
|
|
||||||
|
export type DisplayOptionsMenuPositionProps = {
|
||||||
|
top: number
|
||||||
|
left: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DisplayOptionsMenuProps = {
|
||||||
|
application: {
|
||||||
|
getPreference: WebApplication['getPreference']
|
||||||
|
setPreference: WebApplication['setPreference']
|
||||||
|
}
|
||||||
|
closeDisplayOptionsMenu: () => void
|
||||||
|
isOpen: boolean
|
||||||
|
isFilesSmartView: boolean
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { DisclosureButton } from '@reach/disclosure'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
const StyledDisplayOptionsButton = styled(DisclosureButton).attrs(() => ({
|
||||||
|
className:
|
||||||
|
'flex justify-center items-center min-w-8 h-8 bg-color-padding hover:bg-contrast focus:bg-contrast color-neutral border-1 border-solid border-main rounded-full cursor-pointer',
|
||||||
|
}))<{
|
||||||
|
pressed: boolean
|
||||||
|
}>`
|
||||||
|
background-color: ${(props) => (props.pressed ? 'var(--sn-stylekit-contrast-background-color)' : 'transparent')};
|
||||||
|
`
|
||||||
|
|
||||||
|
export default StyledDisplayOptionsButton
|
||||||
@@ -72,6 +72,7 @@ import {
|
|||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
SignInIcon,
|
SignInIcon,
|
||||||
SignOutIcon,
|
SignOutIcon,
|
||||||
|
SortDescendingIcon,
|
||||||
SpreadsheetsIcon,
|
SpreadsheetsIcon,
|
||||||
StarIcon,
|
StarIcon,
|
||||||
SyncIcon,
|
SyncIcon,
|
||||||
@@ -121,6 +122,7 @@ export const ICONS = {
|
|||||||
'menu-arrow-down': MenuArrowDownIcon,
|
'menu-arrow-down': MenuArrowDownIcon,
|
||||||
'menu-arrow-right': MenuArrowRightIcon,
|
'menu-arrow-right': MenuArrowRightIcon,
|
||||||
'menu-close': MenuCloseIcon,
|
'menu-close': MenuCloseIcon,
|
||||||
|
'sort-descending': SortDescendingIcon,
|
||||||
'pencil-filled': PencilFilledIcon,
|
'pencil-filled': PencilFilledIcon,
|
||||||
'pencil-off': PencilOffIcon,
|
'pencil-off': PencilOffIcon,
|
||||||
'pin-filled': PinFilledIcon,
|
'pin-filled': PinFilledIcon,
|
||||||
|
|||||||
@@ -503,38 +503,18 @@ export class ItemListController extends AbstractViewController implements Intern
|
|||||||
return this.createNewNote()
|
return this.createNewNote()
|
||||||
}
|
}
|
||||||
|
|
||||||
get optionsSubtitle(): string {
|
get optionsSubtitle(): string | undefined {
|
||||||
let base = ''
|
if (!this.displayOptions.includePinned && !this.displayOptions.includeProtected) {
|
||||||
|
return 'Excluding pinned and protected'
|
||||||
if (this.displayOptions.sortBy === CollectionSort.CreatedAt) {
|
|
||||||
base += ' Date Added'
|
|
||||||
} else if (this.displayOptions.sortBy === CollectionSort.UpdatedAt) {
|
|
||||||
base += ' Date Modified'
|
|
||||||
} else if (this.displayOptions.sortBy === CollectionSort.Title) {
|
|
||||||
base += ' Title'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.displayOptions.includeArchived) {
|
|
||||||
base += ' | + Archived'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.displayOptions.includeTrashed) {
|
|
||||||
base += ' | + Trashed'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.displayOptions.includePinned) {
|
if (!this.displayOptions.includePinned) {
|
||||||
base += ' | – Pinned'
|
return 'Excluding pinned'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.displayOptions.includeProtected) {
|
if (!this.displayOptions.includeProtected) {
|
||||||
base += ' | – Protected'
|
return 'Excluding protected'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.displayOptions.sortDirection === 'asc') {
|
return undefined
|
||||||
base += ' | Reversed'
|
|
||||||
}
|
|
||||||
|
|
||||||
return base
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paginate = () => {
|
paginate = () => {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
$z-index-editor-content: 10;
|
$z-index-editor-content: 10;
|
||||||
|
|
||||||
$z-index-editor-title-bar: 100;
|
$z-index-editor-title-bar: 100;
|
||||||
$z-index-dropdown-menu: 1002;
|
|
||||||
|
|
||||||
$z-index-resizer-overlay: 1000;
|
$z-index-resizer-overlay: 1000;
|
||||||
|
|
||||||
@@ -20,6 +19,7 @@ $z-index-modal: 10000;
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
--z-index-panel-resizer: 1001;
|
--z-index-panel-resizer: 1001;
|
||||||
|
--z-index-dropdown-menu: 1002;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
float: left;
|
float: left;
|
||||||
min-width: 160px;
|
min-width: 160px;
|
||||||
z-index: $z-index-dropdown-menu;
|
z-index: var(--z-index-dropdown-menu);
|
||||||
|
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
width: 280px;
|
width: 280px;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
@extend .rounded;
|
@extend .rounded;
|
||||||
@extend .box-shadow;
|
@extend .box-shadow;
|
||||||
|
|
||||||
z-index: $z-index-dropdown-menu;
|
z-index: var(--z-index-dropdown-menu);
|
||||||
|
|
||||||
&.sn-dropdown--anchor-right {
|
&.sn-dropdown--anchor-right {
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user