diff --git a/packages/models/src/Domain/Syncable/UserPrefs/PrefDefaults.ts b/packages/models/src/Domain/Syncable/UserPrefs/PrefDefaults.ts index d943fc7a7b7..cb0607766f0 100644 --- a/packages/models/src/Domain/Syncable/UserPrefs/PrefDefaults.ts +++ b/packages/models/src/Domain/Syncable/UserPrefs/PrefDefaults.ts @@ -27,6 +27,7 @@ export const PrefDefaults = { [PrefKey.NotesHideDate]: false, [PrefKey.NotesHideTags]: false, [PrefKey.NotesHideEditorIcon]: false, + [PrefKey.NotesListLayout]: 'rows', [PrefKey.DEPRECATED_UseSystemColorScheme]: false, [PrefKey.DEPRECATED_UseTranslucentUI]: true, [PrefKey.DEPRECATED_AutoLightThemeIdentifier]: 'Default', diff --git a/packages/models/src/Domain/Syncable/UserPrefs/PrefKey.ts b/packages/models/src/Domain/Syncable/UserPrefs/PrefKey.ts index 58970f36ab9..cf35066b006 100644 --- a/packages/models/src/Domain/Syncable/UserPrefs/PrefKey.ts +++ b/packages/models/src/Domain/Syncable/UserPrefs/PrefKey.ts @@ -24,6 +24,7 @@ export enum PrefKey { NotesHideDate = 'hideDate', NotesHideTags = 'hideTags', NotesHideEditorIcon = 'hideEditorIcon', + NotesListLayout = 'notesListLayout', NoteAddToParentFolders = 'noteAddToParentFolders', NewNoteTitleFormat = 'newNoteTitleFormat', CustomNoteTitleFormat = 'customNoteTitleFormat', @@ -73,6 +74,7 @@ export type PrefValue = { [PrefKey.NotesHideDate]: boolean [PrefKey.NotesHideTags]: boolean [PrefKey.NotesHideEditorIcon]: boolean + [PrefKey.NotesListLayout]: 'rows' | 'tiles' [PrefKey.DEPRECATED_ActiveThemes]: string[] [PrefKey.DEPRECATED_UseSystemColorScheme]: boolean [PrefKey.DEPRECATED_UseTranslucentUI]: boolean diff --git a/packages/web/src/javascripts/Components/ContentListView/ContentList.tsx b/packages/web/src/javascripts/Components/ContentListView/ContentList.tsx index c76f12fc823..0774a2956f6 100644 --- a/packages/web/src/javascripts/Components/ContentListView/ContentList.tsx +++ b/packages/web/src/javascripts/Components/ContentListView/ContentList.tsx @@ -22,11 +22,12 @@ const ContentList: FunctionComponent = ({ application, items, selectedUui const { filesController, itemListController, navigationController, notesController } = application const { selectPreviousItem, selectNextItem } = itemListController - const { hideTags, hideDate, hideNotePreview, hideEditorIcon } = itemListController.webDisplayOptions + const { hideTags, hideDate, hideNotePreview, hideEditorIcon, notesListLayout } = itemListController.webDisplayOptions const { sortBy } = itemListController.displayOptions const selectedTag = navigationController.selected const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) + const isTiledLayout = notesListLayout === 'tiles' const onScroll: UIEventHandler = useCallback( (e) => { @@ -86,8 +87,8 @@ const ContentList: FunctionComponent = ({ application, items, selectedUui className={classNames( 'infinite-scroll overflow-y-auto overflow-x-hidden focus:shadow-none focus:outline-none', 'md:max-h-full pointer-coarse:md:overflow-y-auto', - 'flex-grow', isMobileScreen ? !itemListController.isMultipleSelectionMode && 'pb-safe-bottom' : 'pb-2', + isTiledLayout ? 'grid grid-cols-2 gap-3 px-3 py-3' : '', )} id={ElementIds.ContentList} onScroll={onScroll} @@ -110,6 +111,7 @@ const ContentList: FunctionComponent = ({ application, items, selectedUui onSelect={selectItem} tags={getTagsForItem(item)} notesController={notesController} + isTiled={isTiledLayout} /> ) })} diff --git a/packages/web/src/javascripts/Components/ContentListView/FileListItem.tsx b/packages/web/src/javascripts/Components/ContentListView/FileListItem.tsx index 6646e935af0..59699a7f6e5 100644 --- a/packages/web/src/javascripts/Components/ContentListView/FileListItem.tsx +++ b/packages/web/src/javascripts/Components/ContentListView/FileListItem.tsx @@ -25,6 +25,7 @@ const FileListItemCard: FunctionComponent> = selected, sortBy, tags, + isTiled, }) => { const { setPaneLayout } = useResponsiveAppPane() const application = useApplication() @@ -85,7 +86,8 @@ const FileListItemCard: FunctionComponent> = role="button" className={classNames( 'content-list-item flex w-full cursor-pointer items-stretch text-text', - selected && 'selected border-l-2px border-solid border-info', + isTiled ? 'rounded-lg border border-border' : 'border-l-2px border-solid', + selected ? 'selected border-info' : isTiled ? 'border-border' : 'border-transparent', )} id={file.uuid} onClick={onClick} @@ -97,7 +99,7 @@ const FileListItemCard: FunctionComponent> = ) : (
)} -
+
{file.title}
@@ -106,7 +108,7 @@ const FileListItemCard: FunctionComponent> =
- +
) } diff --git a/packages/web/src/javascripts/Components/ContentListView/Header/DisplayOptionsMenu.tsx b/packages/web/src/javascripts/Components/ContentListView/Header/DisplayOptionsMenu.tsx index 06e55b7db1a..accc6601a3b 100644 --- a/packages/web/src/javascripts/Components/ContentListView/Header/DisplayOptionsMenu.tsx +++ b/packages/web/src/javascripts/Components/ContentListView/Header/DisplayOptionsMenu.tsx @@ -27,6 +27,7 @@ import { Pill } from '@/Components/Preferences/PreferencesComponents/Content' import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery' import { PaneLayout } from '@/Controllers/PaneController/PaneLayout' import MenuSection from '@/Components/Menu/MenuSection' +import usePreference from '@/Hooks/usePreference' const DailyEntryModeEnabled = true @@ -99,6 +100,7 @@ const DisplayOptionsMenu: FunctionComponent = ({ const [preferences, setPreferences] = useState({}) const controlsDisabled = currentMode === 'tag' && !hasSubscription const isDailyEntry = selectedTagPreferences?.entryMode === 'daily' + const notesListLayout = usePreference(PrefKey.NotesListLayout) const reloadPreferences = useCallback(() => { const globalValues: TagPreferences = { @@ -281,6 +283,11 @@ const DisplayOptionsMenu: FunctionComponent = ({ } }, [preferences.useTableView, changePreferences, paneController]) + const toggleNotesListLayout = useCallback(() => { + const nextLayout = notesListLayout === 'tiles' ? 'rows' : 'tiles' + application.setPreference(PrefKey.NotesListLayout, nextLayout).catch(console.error) + }, [application, notesListLayout]) + const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) const isTableViewEnabled = Boolean(isFilesSmartView || preferences.useTableView) const shouldHideNonApplicableOptions = isTableViewEnabled && !isMobileScreen @@ -390,6 +397,14 @@ const DisplayOptionsMenu: FunctionComponent = ({ > Show icon + + Tiled list layout + {!shouldHideNonApplicableOptions && ( diff --git a/packages/web/src/javascripts/Components/ContentListView/NoteListItem.tsx b/packages/web/src/javascripts/Components/ContentListView/NoteListItem.tsx index d43a1a0e042..56e4666a062 100644 --- a/packages/web/src/javascripts/Components/ContentListView/NoteListItem.tsx +++ b/packages/web/src/javascripts/Components/ContentListView/NoteListItem.tsx @@ -31,6 +31,7 @@ const NoteListItem: FunctionComponent> = ({ selected, sortBy, tags, + isTiled, isPreviousItemTiled, isNextItemTiled, }) => { @@ -95,7 +96,7 @@ const NoteListItem: FunctionComponent> = ({ log(LoggingDomain.ItemsList, 'Rendering note list item', item.title) - const hasOffsetBorder = !isNextItemTiled + const hasOffsetBorder = !isTiled && !isNextItemTiled const dragPreview = useRef() @@ -129,14 +130,17 @@ const NoteListItem: FunctionComponent> = ({ ref={listItemRef} role="button" className={classNames( - 'content-list-item flex w-full cursor-pointer items-stretch border-l-2 text-text', + 'content-list-item flex w-full cursor-pointer items-stretch text-text', selected ? `selected ${ application.itemListController.isMultipleSelectionMode ? 'border-info' : `border-accessory-tint-${tint}` }` + : isTiled + ? '' : 'border-transparent', - isPreviousItemTiled && 'mt-3 border-t border-t-border', - isNextItemTiled && 'mb-3 border-b border-b-border', + isTiled ? 'rounded-lg border border-border' : 'border-l-2', + !isTiled && isPreviousItemTiled && 'mt-3 border-t border-t-border', + !isTiled && isNextItemTiled && 'mb-3 border-b border-b-border', )} id={item.uuid} onClick={onClick} @@ -169,7 +173,12 @@ const NoteListItem: FunctionComponent> = ({ ) : (
)} -
+
@@ -177,7 +186,7 @@ const NoteListItem: FunctionComponent> = ({
- +
) } diff --git a/packages/web/src/javascripts/Components/ContentListView/Types/AbstractListItemProps.ts b/packages/web/src/javascripts/Components/ContentListView/Types/AbstractListItemProps.ts index 53398a183dc..d6e4f53e919 100644 --- a/packages/web/src/javascripts/Components/ContentListView/Types/AbstractListItemProps.ts +++ b/packages/web/src/javascripts/Components/ContentListView/Types/AbstractListItemProps.ts @@ -19,6 +19,7 @@ export type AbstractListItemProps = { selected: boolean sortBy: keyof SortableItem | undefined tags: SNTag[] + isTiled?: boolean isPreviousItemTiled?: boolean isNextItemTiled?: boolean } @@ -35,6 +36,7 @@ export function doListItemPropsMeritRerender( 'hidePreview', 'selected', 'sortBy', + 'isTiled', 'isPreviousItemTiled', 'isNextItemTiled', ] diff --git a/packages/web/src/javascripts/Controllers/ItemList/ItemListController.ts b/packages/web/src/javascripts/Controllers/ItemList/ItemListController.ts index 9c06856cd09..19303fe4a84 100644 --- a/packages/web/src/javascripts/Controllers/ItemList/ItemListController.ts +++ b/packages/web/src/javascripts/Controllers/ItemList/ItemListController.ts @@ -94,6 +94,7 @@ export class ItemListController hideDate: false, hideNotePreview: false, hideEditorIcon: false, + notesListLayout: 'rows', } isTableViewEnabled = false private reloadItemsPromise?: Promise @@ -711,6 +712,11 @@ export class ItemListController this.preferences.getValue(PrefKey.NotesHideEditorIcon, PrefDefaults[PrefKey.NotesHideEditorIcon]), ) + newWebDisplayOptions.notesListLayout = this.preferences.getValue( + PrefKey.NotesListLayout, + PrefDefaults[PrefKey.NotesListLayout], + ) + const displayOptionsChanged = newDisplayOptions.sortBy !== this.displayOptions.sortBy || newDisplayOptions.sortDirection !== this.displayOptions.sortDirection || diff --git a/packages/web/src/javascripts/Controllers/ItemList/WebDisplayOptions.ts b/packages/web/src/javascripts/Controllers/ItemList/WebDisplayOptions.ts index 1b8eb010410..a30e9777fa9 100644 --- a/packages/web/src/javascripts/Controllers/ItemList/WebDisplayOptions.ts +++ b/packages/web/src/javascripts/Controllers/ItemList/WebDisplayOptions.ts @@ -3,4 +3,5 @@ export type WebDisplayOptions = { hideDate: boolean hideNotePreview: boolean hideEditorIcon: boolean + notesListLayout: 'rows' | 'tiles' }