Revert "feat: New notes list design (#780)"

This reverts commit 7dd4a60595.
This commit is contained in:
Karol Sójko
2022-01-05 16:11:34 +01:00
parent b57350c899
commit d76c636e54
20 changed files with 155 additions and 296 deletions

View File

@@ -13,7 +13,6 @@ import { useState } from 'preact/hooks';
export type DropdownItem = {
icon?: IconType;
iconClassName?: string;
label: string;
value: string;
};
@@ -26,7 +25,10 @@ type DropdownProps = {
onChange: (value: string) => void;
};
type ListboxButtonProps = DropdownItem & {
type ListboxButtonProps = {
icon?: IconType;
value: string | null;
label: string;
isExpanded: boolean;
};
@@ -34,13 +36,12 @@ const CustomDropdownButton: FunctionComponent<ListboxButtonProps> = ({
label,
isExpanded,
icon,
iconClassName = '',
}) => (
<>
<div className="sn-dropdown-button-label">
{icon ? (
<div className="flex mr-2">
<Icon type={icon} className={`sn-icon--small ${iconClassName}`} />
<Icon type={icon} className="sn-icon--small" />
</div>
) : null}
<div className="dropdown-selected-label">{label}</div>
@@ -84,13 +85,11 @@ export const Dropdown: FunctionComponent<DropdownProps> = ({
children={({ value, label, isExpanded }) => {
const current = items.find((item) => item.value === value);
const icon = current ? current?.icon : null;
const iconClassName = current ? current?.iconClassName : null;
return CustomDropdownButton({
value: value ? value : label.toLowerCase(),
value,
label,
isExpanded,
...(icon ? { icon } : null),
...(iconClassName ? { iconClassName } : null),
});
}}
/>
@@ -105,10 +104,7 @@ export const Dropdown: FunctionComponent<DropdownProps> = ({
>
{item.icon ? (
<div className="flex mr-3">
<Icon
type={item.icon}
className={`sn-icon--small ${item.iconClassName ?? ''}`}
/>
<Icon type={item.icon} className="sn-icon--small" />
</div>
) : null}
<div className="text-input">{item.label}</div>

View File

@@ -3,9 +3,7 @@ import PencilOffIcon from '../../icons/ic-pencil-off.svg';
import PlainTextIcon from '../../icons/ic-text-paragraph.svg';
import RichTextIcon from '../../icons/ic-text-rich.svg';
import TrashIcon from '../../icons/ic-trash.svg';
import TrashFilledIcon from '../../icons/ic-trash-filled.svg';
import PinIcon from '../../icons/ic-pin.svg';
import PinFilledIcon from '../../icons/ic-pin-filled.svg';
import UnpinIcon from '../../icons/ic-pin-off.svg';
import ArchiveIcon from '../../icons/ic-archive.svg';
import UnarchiveIcon from '../../icons/ic-unarchive.svg';
@@ -54,7 +52,6 @@ import ServerIcon from '../../icons/ic-server.svg';
import EyeIcon from '../../icons/ic-eye.svg';
import EyeOffIcon from '../../icons/ic-eye-off.svg';
import LockIcon from '../../icons/ic-lock.svg';
import LockFilledIcon from '../../icons/ic-lock-filled.svg';
import ArrowsSortUpIcon from '../../icons/ic-arrows-sort-up.svg';
import ArrowsSortDownIcon from '../../icons/ic-arrows-sort-down.svg';
import WindowIcon from '../../icons/ic-window.svg';
@@ -72,7 +69,6 @@ const ICONS = {
'arrows-sort-up': ArrowsSortUpIcon,
'arrows-sort-down': ArrowsSortDownIcon,
lock: LockIcon,
'lock-filled': LockFilledIcon,
eye: EyeIcon,
'eye-off': EyeOffIcon,
server: ServerIcon,
@@ -93,9 +89,7 @@ const ICONS = {
spreadsheets: SpreadsheetsIcon,
tasks: TasksIcon,
trash: TrashIcon,
'trash-filled': TrashFilledIcon,
pin: PinIcon,
'pin-filled': PinFilledIcon,
unpin: UnpinIcon,
archive: ArchiveIcon,
unarchive: UnarchiveIcon,
@@ -136,22 +130,11 @@ export type IconType = keyof typeof ICONS;
type Props = {
type: IconType;
className?: string;
ariaLabel?: string;
};
export const Icon: FunctionalComponent<Props> = ({
type,
className = '',
ariaLabel,
}) => {
export const Icon: FunctionalComponent<Props> = ({ type, className = '' }) => {
const IconComponent = ICONS[type];
return (
<IconComponent
className={`sn-icon ${className}`}
role="img"
{...(ariaLabel ? { 'aria-label': ariaLabel } : {})}
/>
);
return <IconComponent className={`sn-icon ${className}`} />;
};
export const IconDirective = toDirective<Props>(Icon, {

View File

@@ -1,4 +1,3 @@
import { WebApplication } from '@/ui_models/application';
import { KeyboardKey } from '@/services/ioService';
import { AppState } from '@/ui_models/app_state';
import { DisplayOptions } from '@/ui_models/app_state/notes_view_state';
@@ -8,7 +7,6 @@ import { FunctionComponent } from 'preact';
import { NotesListItem } from './NotesListItem';
type Props = {
application: WebApplication;
appState: AppState;
notes: SNNote[];
selectedNotes: Record<string, SNNote>;
@@ -20,30 +18,23 @@ const FOCUSABLE_BUT_NOT_TABBABLE = -1;
const NOTES_LIST_SCROLL_THRESHOLD = 200;
export const NotesList: FunctionComponent<Props> = observer(
({
application,
appState,
notes,
selectedNotes,
displayOptions,
paginate,
}) => {
({ appState, notes, selectedNotes, displayOptions, paginate }) => {
const { selectPreviousNote, selectNextNote } = appState.notesView;
const { hideTags, hideDate, hideNotePreview, sortBy } = displayOptions;
const tagsForNote = (note: SNNote): string[] => {
const tagsStringForNote = (note: SNNote): string => {
if (hideTags) {
return [];
return '';
}
const selectedTag = appState.selectedTag;
if (!selectedTag) {
return [];
return '';
}
const tags = appState.getNoteTags(note);
if (!selectedTag.isSmartTag && tags.length === 1) {
return [];
return '';
}
return tags.map((tag) => tag.title);
return tags.map((tag) => `#${tag.title}`).join(' ');
};
const openNoteContextMenu = (posX: number, posY: number) => {
@@ -55,9 +46,11 @@ export const NotesList: FunctionComponent<Props> = observer(
appState.notes.setContextMenuOpen(true);
};
const onContextMenu = (note: SNNote, posX: number, posY: number) => {
appState.notes.selectNote(note.uuid, true);
openNoteContextMenu(posX, posY);
const onContextMenu = async (note: SNNote, posX: number, posY: number) => {
await appState.notes.selectNote(note.uuid, true);
if (selectedNotes[note.uuid]) {
openNoteContextMenu(posX, posY);
}
};
const onScroll = (e: Event) => {
@@ -91,10 +84,9 @@ export const NotesList: FunctionComponent<Props> = observer(
>
{notes.map((note) => (
<NotesListItem
application={application}
key={note.uuid}
note={note}
tags={tagsForNote(note)}
tags={tagsStringForNote(note)}
selected={!!selectedNotes[note.uuid]}
hideDate={hideDate}
hidePreview={hideNotePreview}

View File

@@ -1,17 +1,13 @@
import { getIconAndTintForEditor } from '@/preferences/panes/general-segments';
import { WebApplication } from '@/ui_models/application';
import {
CollectionSort,
sanitizeHtmlString,
SNNote,
} from '@standardnotes/snjs';
import { FunctionComponent } from 'preact';
import { Icon } from './Icon';
type Props = {
application: WebApplication;
note: SNNote;
tags: string[];
tags: string;
hideDate: boolean;
hidePreview: boolean;
hideTags: boolean;
@@ -28,6 +24,30 @@ type NoteFlag = {
const flagsForNote = (note: SNNote) => {
const flags = [] as NoteFlag[];
if (note.pinned) {
flags.push({
text: 'Pinned',
class: 'info',
});
}
if (note.archived) {
flags.push({
text: 'Archived',
class: 'warning',
});
}
if (note.locked) {
flags.push({
text: 'Editing Disabled',
class: 'neutral',
});
}
if (note.trashed) {
flags.push({
text: 'Deleted',
class: 'danger',
});
}
if (note.conflictOf) {
flags.push({
text: 'Conflicted Copy',
@@ -57,7 +77,6 @@ const flagsForNote = (note: SNNote) => {
};
export const NotesListItem: FunctionComponent<Props> = ({
application,
hideDate,
hidePreview,
hideTags,
@@ -70,9 +89,6 @@ export const NotesListItem: FunctionComponent<Props> = ({
}) => {
const flags = flagsForNote(note);
const showModifiedDate = sortedBy === CollectionSort.UpdatedAt;
const editorForNote = application.componentManager.editorForNote(note);
const editorName = editorForNote?.name ?? 'Plain editor';
const [icon, tint] = getIconAndTintForEditor(editorForNote?.identifier);
return (
<div
@@ -81,107 +97,52 @@ export const NotesListItem: FunctionComponent<Props> = ({
onClick={onClick}
onContextMenu={onContextMenu}
>
<div className="icon">
<Icon
ariaLabel={`Icon for ${editorName}`}
type={icon}
className={`color-accessory-tint-${tint}`}
/>
</div>
<div className="meta">
<div className="name">
<div>{note.title}</div>
<div className="flag-icons">
{note.locked && (
<span title="Editing Disabled">
<Icon
ariaLabel="Editing Disabled"
type="pencil-off"
className="sn-icon--small color-info"
/>
</span>
)}
{note.trashed && (
<span title="Trashed">
<Icon
ariaLabel="Trashed"
type="trash-filled"
className="sn-icon--small color-danger"
/>
</span>
)}
{note.archived && (
<span title="Archived">
<Icon
ariaLabel="Archived"
type="archive"
className="sn-icon--mid color-accessory-tint-3"
/>
</span>
)}
{note.pinned && (
<span title="Pinned">
<Icon
ariaLabel="Pinned"
type="pin-filled"
className="sn-icon--small color-info"
/>
</span>
)}
</div>
{flags && flags.length > 0 ? (
<div className="note-flags flex flex-wrap">
{flags.map((flag) => (
<div className={`flag ${flag.class}`}>
<div className="label">{flag.text}</div>
</div>
))}
</div>
{!hidePreview && !note.hidePreview && !note.protected && (
<div className="note-preview">
{note.preview_html && (
<div
className="html-preview"
dangerouslySetInnerHTML={{
__html: sanitizeHtmlString(note.preview_html),
}}
></div>
)}
{!note.preview_html && note.preview_plain && (
<div className="plain-preview">{note.preview_plain}</div>
)}
{!note.preview_html && !note.preview_plain && note.text && (
<div className="default-preview">{note.text}</div>
)}
</div>
)}
{!hideDate || note.protected ? (
<div className="bottom-info faded">
{note.protected && <span>Protected {hideDate ? '' : ' • '}</span>}
{!hideDate && showModifiedDate && (
<span>Modified {note.updatedAtString || 'Now'}</span>
)}
{!hideDate && !showModifiedDate && (
<span>{note.createdAtString || 'Now'}</span>
)}
</div>
) : null}
{!hideTags && tags.length ? (
<div className="tags-string">
{tags.map((tag) => (
<span className="tag color-foreground">
<Icon
type="hashtag"
className="sn-icon--small color-grey-1 mr-1"
/>
<span>{tag}</span>
</span>
))}
</div>
) : null}
{flags.length ? (
<div className="note-flags flex flex-wrap">
{flags.map((flag) => (
<div className={`flag ${flag.class}`}>
<div className="label">{flag.text}</div>
</div>
))}
</div>
) : null}
</div>
) : null}
<div className="name">{note.title}</div>
{!hidePreview && !note.hidePreview && !note.protected ? (
<div className="note-preview">
{note.preview_html ? (
<div
className="html-preview"
dangerouslySetInnerHTML={{
__html: sanitizeHtmlString(note.preview_html),
}}
></div>
) : null}
{!note.preview_html && note.preview_plain ? (
<div className="plain-preview">{note.preview_plain}</div>
) : null}
{!note.preview_html && !note.preview_plain ? (
<div className="default-preview">{note.text}</div>
) : null}
</div>
) : null}
{!hideDate || note.protected ? (
<div className="bottom-info faded">
{note.protected ? (
<span>Protected {hideDate ? '' : ' • '}</span>
) : null}
{!hideDate && showModifiedDate ? (
<span>Modified {note.updatedAtString || 'Now'}</span>
) : null}
{!hideDate && !showModifiedDate ? (
<span>{note.createdAtString || 'Now'}</span>
) : null}
</div>
) : null}
{!hideTags && (
<div className="tags-string">
<div className="faded">{tags}</div>
</div>
)}
</div>
);
};

View File

@@ -230,7 +230,6 @@ const NotesView: FunctionComponent<Props> = observer(
<NotesList
notes={renderedNotes}
selectedNotes={selectedNotes}
application={application}
appState={appState}
displayOptions={displayOptions}
paginate={paginate}

View File

@@ -21,33 +21,32 @@ type Props = {
application: WebApplication;
};
type EditorOption = DropdownItem & {
type EditorOption = {
icon?: IconType;
label: string;
value: FeatureIdentifier | 'plain-editor';
};
export const getIconAndTintForEditor = (
identifier: FeatureIdentifier | undefined
): [IconType, number] => {
const getEditorIconType = (identifier: string): IconType | null => {
switch (identifier) {
case FeatureIdentifier.BoldEditor:
case FeatureIdentifier.PlusEditor:
return ['rich-text', 1];
return 'rich-text';
case FeatureIdentifier.MarkdownBasicEditor:
case FeatureIdentifier.MarkdownMathEditor:
case FeatureIdentifier.MarkdownMinimistEditor:
case FeatureIdentifier.MarkdownProEditor:
return ['markdown', 2];
return 'markdown';
case FeatureIdentifier.TokenVaultEditor:
return ['authenticator', 6];
return 'authenticator';
case FeatureIdentifier.SheetsEditor:
return ['spreadsheets', 5];
return 'spreadsheets';
case FeatureIdentifier.TaskEditor:
return ['tasks', 3];
return 'tasks';
case FeatureIdentifier.CodeEditor:
return ['code', 4];
default:
return ['plain-text', 1];
return 'code';
}
return null;
};
const makeEditorDefault = (
@@ -92,19 +91,17 @@ export const Defaults: FunctionComponent<Props> = ({ application }) => {
.componentsForArea(ComponentArea.Editor)
.map((editor): EditorOption => {
const identifier = editor.package_info.identifier;
const [iconType, tint] = getIconAndTintForEditor(identifier);
const iconType = getEditorIconType(identifier);
return {
label: editor.name,
value: identifier,
...(iconType ? { icon: iconType } : null),
...(tint ? { iconClassName: `color-accessory-tint-${tint}` } : null),
};
})
.concat([
{
icon: 'plain-text',
iconClassName: `color-accessory-tint-1`,
label: 'Plain Editor',
value: 'plain-editor',
},