feat: nicer smart filters & folders theme (#795)

* fix: color add button and drop

* fix: color scrollbars

* fix: remove infinite scroll and fix scrollbars

* fix: plus icon center

* fix: navigation padding, structure simplif and naming

* fix: simplify scrollbars

* fix: scroll bar simplif + scheme in macos

* fix: magic variables to const

* refactor: extract panel ref state

* refactor: remove dead code, simple macos theme
This commit is contained in:
Laurent Senta
2022-01-12 13:45:41 +01:00
committed by GitHub
parent c1b7f60e35
commit 7996f4e5a2
15 changed files with 70 additions and 108 deletions

View File

@@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.5999 8.00002C13.5999 8.44185 13.2417 8.80002 12.7999 8.80002H8.7999V12.8C8.7999 13.2419 8.44173 13.6 7.9999 13.6C7.55807 13.6 7.1999 13.2419 7.1999 12.8V8.80002H3.1999C2.75807 8.80002 2.3999 8.44185 2.3999 8.00002C2.3999 7.5582 2.75807 7.20002 3.1999 7.20002H7.1999V3.20002C7.1999 2.7582 7.55807 2.40002 7.9999 2.40002C8.44173 2.40002 8.7999 2.7582 8.7999 3.20002V7.20002H12.7999C13.2417 7.20002 13.5999 7.5582 13.5999 8.00002Z" />
</svg>

Before

Width:  |  Height:  |  Size: 548 B

After

Width:  |  Height:  |  Size: 525 B

View File

@@ -58,7 +58,6 @@ import {
delayHide,
elemReady,
fileChange,
infiniteScroll,
lowercase,
selectOnFocus,
snEnter,
@@ -150,7 +149,6 @@ const startApplication: StartApplication = async function startApplication(
.directive('delayHide', delayHide)
.directive('elemReady', elemReady)
.directive('fileChange', fileChange)
.directive('infiniteScroll', [infiniteScroll])
.directive('lowercase', lowercase)
.directive('selectOnFocus', ['$window', selectOnFocus])
.directive('snEnter', snEnter);

View File

@@ -12,27 +12,35 @@ import { PANEL_NAME_NAVIGATION } from '@/views/constants';
import { PrefKey } from '@standardnotes/snjs';
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { useCallback, useEffect, useMemo, useState } from 'preact/hooks';
import { useCallback, useMemo, useState } from 'preact/hooks';
import { PremiumModalProvider } from './Premium';
type Props = {
application: WebApplication;
};
const NAVIGATION_SELECTOR = 'navigation';
const useNavigationPanelRef = (): [HTMLDivElement | null, () => void] => {
const [panelRef, setPanelRefInternal] = useState<HTMLDivElement | null>(null);
const setPanelRefPublic = useCallback(() => {
const elem = document.querySelector(
NAVIGATION_SELECTOR
) as HTMLDivElement | null;
setPanelRefInternal(elem);
}, [setPanelRefInternal]);
return [panelRef, setPanelRefPublic];
};
export const Navigation: FunctionComponent<Props> = observer(
({ application }) => {
const appState = useMemo(() => application.getAppState(), [application]);
const componentViewer = appState.foldersComponentViewer;
const enableNativeSmartTagsFeature =
appState.features.enableNativeSmartTagsFeature;
const [panelRef, setPanelRef] = useState<HTMLDivElement | null>(null);
useEffect(() => {
const elem = document.querySelector(
'navigation'
) as HTMLDivElement | null;
setPanelRef(elem);
}, [setPanelRef]);
const [panelRef, setPanelRef] = useNavigationPanelRef();
const onCreateNewTag = useCallback(() => {
appState.tags.createNewTemplate();
@@ -53,10 +61,10 @@ export const Navigation: FunctionComponent<Props> = observer(
return (
<PremiumModalProvider state={appState.features}>
<div
id="tags-column"
id="navigation"
className="sn-component section"
data-aria-label="Navigation"
ref={setPanelRef}
className="sn-component section tags"
data-aria-label="Tags"
>
{componentViewer ? (
<div className="component-view-container">
@@ -69,8 +77,8 @@ export const Navigation: FunctionComponent<Props> = observer(
</div>
</div>
) : (
<div id="tags-content" className="content">
<div className="tags-title-section section-title-bar">
<div id="navigation-content" className="content">
<div className="section-title-bar">
<div className="section-title-bar-header">
<div className="sk-h3 title">
<span className="sk-bold">Views</span>
@@ -89,10 +97,8 @@ export const Navigation: FunctionComponent<Props> = observer(
</div>
</div>
<div className="scrollable">
<div className="infinite-scroll">
<SmartTagsSection appState={appState} />
<TagsSection appState={appState} />
</div>
<SmartTagsSection appState={appState} />
<TagsSection appState={appState} />
</div>
</div>
)}

View File

@@ -50,7 +50,7 @@ export const RootTagDropZone: React.FC<Props> = observer(
<div
ref={dropRef}
className={`root-drop ${canDrop ? 'active' : ''} ${
isOver ? 'is-over' : ''
isOver ? 'is-drag-over' : ''
}`}
>
<Icon className="color-neutral" type="link-off" />

View File

@@ -13,6 +13,9 @@ type Props = {
features: FeaturesState;
};
const PADDING_BASE_PX = 14;
const PADDING_PER_LEVEL_PX = 21;
const smartTagIconType = (tag: SNSmartTag): IconType => {
if (tag.isAllTag) {
return 'notes';
@@ -95,7 +98,9 @@ export const SmartTagsListItem: FunctionComponent<Props> = observer(
isFaded ? 'faded' : ''
}`}
onClick={selectCurrentTag}
style={{ paddingLeft: `${level + 0.5}rem` }}
style={{
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
}}
>
{!tag.errorDecrypting ? (
<div className="tag-info">

View File

@@ -21,6 +21,9 @@ type Props = {
level: number;
};
const PADDING_BASE_PX = 14;
const PADDING_PER_LEVEL_PX = 21;
export const TagsListItem: FunctionComponent<Props> = observer(
({ tag, features, tagsState, level }) => {
const [title, setTitle] = useState(tag.title || '');
@@ -151,7 +154,9 @@ export const TagsListItem: FunctionComponent<Props> = observer(
}`}
onClick={selectCurrentTag}
ref={dragRef}
style={{ paddingLeft: `${level * 21 + 10}px` }}
style={{
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
}}
>
{!tag.errorDecrypting ? (
<div className="tag-info" title={title} ref={dropRef}>

View File

@@ -13,7 +13,7 @@ export const TagsSection: FunctionComponent<Props> = observer(
({ appState }) => {
return (
<section>
<div className="tags-title-section section-title-bar">
<div className="section-title-bar">
<div className="section-title-bar-header">
<TagsSectionTitle features={appState.features} />
<TagsSectionAddButton

View File

@@ -1,5 +1,4 @@
import { IconButton } from '@/components/IconButton';
import { AppState } from '@/ui_models/app_state';
import { FeaturesState } from '@/ui_models/app_state/features_state';
import { TagsState } from '@/ui_models/app_state/tags_state';
import { observer } from 'mobx-react-lite';
@@ -23,6 +22,7 @@ export const TagsSectionAddButton: FunctionComponent<Props> = observer(
focusable={true}
icon="add"
title="Create a new tag"
className="color-neutral"
onClick={() => tags.createNewTemplate()}
/>
);

View File

@@ -3,7 +3,6 @@ export { clickOutside } from './click-outside';
export { delayHide } from './delay-hide';
export { elemReady } from './elemReady';
export { fileChange } from './file-change';
export { infiniteScroll } from './infiniteScroll';
export { lowercase } from './lowercase';
export { selectOnFocus } from './selectOnFocus';
export { snEnter } from './snEnter';

View File

@@ -1,26 +0,0 @@
import { debounce } from '@/utils';
/* @ngInject */
export function infiniteScroll() {
return {
link: function (scope: ng.IScope, elem: JQLite, attrs: any) {
const scopeAny = scope as any;
const offset = parseInt(attrs.threshold) || 0;
const element = elem[0];
scopeAny.paginate = debounce(() => {
scope.$apply(attrs.infiniteScroll);
}, 10);
scopeAny.onScroll = () => {
if (
scope.$eval(attrs.canLoad) &&
element.scrollTop + element.offsetHeight >= element.scrollHeight - offset
) {
scopeAny.paginate();
}
};
elem.on('scroll', scopeAny.onScroll);
scope.$on('$destroy', () => {
elem.off('scroll', scopeAny.onScroll);
});
}
};
}

View File

@@ -203,11 +203,6 @@ $footer-height: 2rem;
position: relative;
overflow: hidden;
.scrollable {
overflow-y: auto;
overflow-x: hidden;
}
> .content {
height: 100%;
max-height: 100%;

View File

@@ -1,9 +1,13 @@
#tags-column {
width: 100%;
@import './scrollbar';
#navigation .scrollable {
@include minimal_scrollbar();
height: 100%;
background-color: var(--sn-stylekit-background-color);
}
.tags {
width: 180px;
#navigation {
width: 100%;
flex-grow: 0;
user-select: none;
@@ -12,42 +16,21 @@
-webkit-user-select: none;
&,
#tags-content {
background-color: var(--sn-stylekit-secondary-background-color);
#navigation-content {
display: flex;
flex-direction: column;
background-color: var(--sn-stylekit-secondary-background-color);
}
.tags-title-section {
.section-title-bar {
color: var(--sn-stylekit-secondary-foreground-color);
padding-top: 15px;
padding-bottom: 8px;
padding-left: 12px;
padding-right: 12px;
padding-left: 14px;
padding-right: 14px;
font-size: 12px;
}
.scrollable {
height: 100%;
}
.infinite-scroll {
overflow-x: hidden;
height: inherit;
// Autohide scrollbar on Windows.
@at-root {
.windows-web &,
.windows-desktop & {
overflow-y: hidden;
&:hover {
overflow-y: auto;
overflow-y: overlay; // overlay is not supported on ff, so keep previous statement up
}
}
}
}
.no-tags-placeholder {
padding: 0px 12px;
font-size: 12px;
@@ -80,12 +63,13 @@
}
}
.tag {
.tag,
.root-drop {
font-size: 14px;
line-height: 18px;
min-height: 30px;
padding: 5px 12px;
padding: 5px 14px;
cursor: pointer;
transition: height 0.1s ease-in-out;
position: relative;

View File

@@ -1,3 +1,5 @@
@import './scrollbar';
notes-view {
width: 350px;
}
@@ -101,25 +103,10 @@ notes-view {
}
}
.scrollable {
height: 100%;
}
.infinite-scroll {
overflow-x: hidden;
@include minimal_scrollbar();
height: inherit;
// Autohide scrollbar on Windows.
@at-root {
.windows-web &,
.windows-desktop & {
overflow-y: hidden;
&:hover {
overflow-y: auto;
overflow-y: overlay; // overlay is not supported on ff, so keep previous statement up
}
}
}
background-color: var(--sn-stylekit-background-color);
}
.note {

View File

@@ -0,0 +1,9 @@
@mixin minimal_scrollbar() {
overflow-x: hidden;
overflow-y: hidden;
&:hover {
overflow-y: auto;
overflow-y: overlay;
}
}

View File

@@ -2,7 +2,7 @@
@import 'main';
@import 'ui';
@import 'footer';
@import 'tags';
@import 'navigation';
@import 'notes';
@import 'editor';
@import 'menus';