fix: remove tablet specific responsive UI (#1411)

* fix: revert tablet responsiveness UI

* fix: don't let the "Saving..." text container hide the note title on tablets

* chore: better visibility of css classes list

* fix: fixed width of notes container column on Tablets
This commit is contained in:
Vardan Hakobyan
2022-08-17 15:39:56 +04:00
committed by GitHub
parent f14f105703
commit a25923f084
13 changed files with 291 additions and 372 deletions

View File

@@ -21,6 +21,7 @@ import ResponsivePaneContent from '../ResponsivePane/ResponsivePaneContent'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata' import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider' import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { StreamingFileReader } from '@standardnotes/filepicker' import { StreamingFileReader } from '@standardnotes/filepicker'
import { classNames } from '@/Utils/ConcatenateClassNames'
type Props = { type Props = {
accountMenuController: AccountMenuController accountMenuController: AccountMenuController
@@ -179,7 +180,10 @@ const ContentListView: FunctionComponent<Props> = ({
return ( return (
<div <div
id="items-column" id="items-column"
className="sn-component section app-column flex flex-col border-b border-solid border-border xl:w-87.5 xsm-only:!w-full sm-only:!w-full md-only:!w-52 lg-only:!w-52" className={classNames(
'sn-component section app-column flex flex-col border-b border-solid border-border ',
'xl:w-87.5 xsm-only:!w-full sm-only:!w-full md-only:!w-52 lg-only:!w-52',
)}
aria-label={'Notes & Files'} aria-label={'Notes & Files'}
ref={itemsViewPanelRef} ref={itemsViewPanelRef}
> >

View File

@@ -10,7 +10,6 @@ import SearchBar from '@/Components/SearchBar/SearchBar'
import ResponsivePaneContent from '@/Components/ResponsivePane/ResponsivePaneContent' import ResponsivePaneContent from '@/Components/ResponsivePane/ResponsivePaneContent'
import { AppPaneId } from '@/Components/ResponsivePane/AppPaneMetadata' import { AppPaneId } from '@/Components/ResponsivePane/AppPaneMetadata'
import { classNames } from '@/Utils/ConcatenateClassNames' import { classNames } from '@/Utils/ConcatenateClassNames'
import Icon from '@/Components/Icon/Icon'
type Props = { type Props = {
application: WebApplication application: WebApplication
@@ -20,7 +19,6 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
const viewControllerManager = useMemo(() => application.getViewControllerManager(), [application]) const viewControllerManager = useMemo(() => application.getViewControllerManager(), [application])
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const [panelWidth, setPanelWidth] = useState<number>(0) const [panelWidth, setPanelWidth] = useState<number>(0)
const [isPanelExpanded, setIsPanelExpanded] = useState(true)
useEffect(() => { useEffect(() => {
const removeObserver = application.addEventObserver(async () => { const removeObserver = application.addEventObserver(async () => {
@@ -51,37 +49,21 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
return ( return (
<div <div
id="navigation" id="navigation"
className={classNames( className={'sn-component section app-column w-[220px] xsm-only:!w-full sm-only:!w-full'}
'sn-component section app-column xl:w-[220px] xsm-only:!w-full sm-only:!w-full md-only:transition-width lg-only:transition-width',
isPanelExpanded ? 'md-only:!w-[220px] lg-only:!w-[220px]' : 'md-only:!w-18 lg-only:!w-18',
)}
ref={ref} ref={ref}
> >
<ResponsivePaneContent paneId={AppPaneId.Navigation} contentElementId="navigation-content"> <ResponsivePaneContent paneId={AppPaneId.Navigation} contentElementId="navigation-content">
{isPanelExpanded && ( <SearchBar
<SearchBar itemListController={viewControllerManager.itemListController}
itemListController={viewControllerManager.itemListController} searchOptionsController={viewControllerManager.searchOptionsController}
searchOptionsController={viewControllerManager.searchOptionsController} selectedViewTitle={viewControllerManager.navigationController.selected?.title}
selectedViewTitle={viewControllerManager.navigationController.selected?.title} />
/> <div className={'section-title-bar'}>
)} <div className="section-title-bar-header">
<div className={'flex justify-end'}> <div className="title text-sm">
<div className={classNames('section-title-bar block w-full xl:block', !isPanelExpanded && 'hidden')}> <span className="font-bold">Views</span>
<div className="section-title-bar-header">
<div className="title text-sm">
<span className="font-bold">Views</span>
</div>
</div> </div>
</div> </div>
<div
className={classNames(
'hidden items-end self-end rounded-full rounded-tr-none rounded-br-none bg-default p-1 text-foreground md:flex xl:hidden',
isPanelExpanded ? 'mb-1 w-fit' : 'mt-4.5 mb-3 ml-3 w-full',
)}
onClick={() => setIsPanelExpanded(!isPanelExpanded)}
>
<Icon type="chevron-down" className={isPanelExpanded ? 'rotate-90' : '-rotate-90'} />
</div>
</div> </div>
<div <div
className={classNames( className={classNames(
@@ -90,8 +72,8 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
'md:hover:[overflow-y:_overlay]', 'md:hover:[overflow-y:_overlay]',
)} )}
> >
<SmartViewsSection viewControllerManager={viewControllerManager} isCollapsed={!isPanelExpanded} /> <SmartViewsSection viewControllerManager={viewControllerManager} />
<TagsSection viewControllerManager={viewControllerManager} isCollapsed={!isPanelExpanded} /> <TagsSection viewControllerManager={viewControllerManager} />
</div> </div>
</ResponsivePaneContent> </ResponsivePaneContent>
{ref.current && ( {ref.current && (

View File

@@ -36,6 +36,7 @@ import {
import { reloadFont } from './FontFunctions' import { reloadFont } from './FontFunctions'
import { NoteViewProps } from './NoteViewProps' import { NoteViewProps } from './NoteViewProps'
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle' import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
import { classNames } from '@/Utils/ConcatenateClassNames'
const MINIMUM_STATUS_DURATION = 400 const MINIMUM_STATUS_DURATION = 400
const TEXTAREA_DEBOUNCE = 100 const TEXTAREA_DEBOUNCE = 100
@@ -911,7 +912,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
{this.note && ( {this.note && (
<div id="editor-title-bar" className="content-title-bar section-title-bar z-editor-title-bar w-full"> <div id="editor-title-bar" className="content-title-bar section-title-bar z-editor-title-bar w-full">
<div className="mb-2 flex flex-wrap items-center justify-between gap-2 md:mb-0 md:flex-nowrap md:gap-0"> <div className="mb-2 flex flex-wrap items-start justify-between gap-2 md:mb-0 md:flex-nowrap md:gap-0 xl:items-center">
<div className={(this.state.noteLocked ? 'locked' : '') + ' flex-grow'}> <div className={(this.state.noteLocked ? 'locked' : '') + ' flex-grow'}>
<div className="title overflow-auto"> <div className="title overflow-auto">
<input <input
@@ -929,9 +930,14 @@ class NoteView extends PureComponent<NoteViewProps, State> {
/> />
</div> </div>
</div> </div>
<div className="flex flex-col flex-wrap items-start gap-3 md:flex-row md:flex-nowrap md:items-center"> <div
className={classNames(
'flex flex-col flex-wrap items-start gap-3 md:flex-col-reverse md:items-end',
'xl:flex-row xl:flex-nowrap xl:items-center',
)}
>
{this.state.noteStatus?.message?.length && ( {this.state.noteStatus?.message?.length && (
<div id="save-status-container"> <div id="save-status-container" className={'xl:mr-5 xl:max-w-[16ch]'}>
<div id="save-status"> <div id="save-status">
<div <div
className={ className={

View File

@@ -5,10 +5,9 @@ import SmartViewsListItem from './SmartViewsListItem'
type Props = { type Props = {
viewControllerManager: ViewControllerManager viewControllerManager: ViewControllerManager
isCollapsed: boolean
} }
const SmartViewsList: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed }: Props) => { const SmartViewsList: FunctionComponent<Props> = ({ viewControllerManager }: Props) => {
const allViews = viewControllerManager.navigationController.smartViews const allViews = viewControllerManager.navigationController.smartViews
return ( return (
@@ -20,7 +19,6 @@ const SmartViewsList: FunctionComponent<Props> = ({ viewControllerManager, isCol
view={view} view={view}
tagsState={viewControllerManager.navigationController} tagsState={viewControllerManager.navigationController}
features={viewControllerManager.featuresController} features={viewControllerManager.featuresController}
isCollapsed={isCollapsed}
/> />
) )
})} })}

View File

@@ -21,7 +21,6 @@ type Props = {
view: SmartView view: SmartView
tagsState: NavigationController tagsState: NavigationController
features: FeaturesController features: FeaturesController
isCollapsed: boolean
} }
const PADDING_BASE_PX = 14 const PADDING_BASE_PX = 14
@@ -47,7 +46,7 @@ const smartViewIconType = (view: SmartView, isSelected: boolean): IconType => {
return 'hashtag' return 'hashtag'
} }
const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState, isCollapsed }) => { const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
const { toggleAppPane } = useResponsiveAppPane() const { toggleAppPane } = useResponsiveAppPane()
const [title, setTitle] = useState(view.title || '') const [title, setTitle] = useState(view.title || '')
@@ -113,31 +112,19 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState, isColla
return ( return (
<> <>
<div <div
className={classNames( className={classNames('tag', isSelected && 'selected', isFaded && 'opacity-50')}
'tag',
isSelected && 'selected',
isFaded && 'opacity-50',
isCollapsed && '!bg-transparent',
)}
onClick={selectCurrentTag} onClick={selectCurrentTag}
style={{ style={{
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`, paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
}} }}
> >
<div className="tag-info relative"> <div className="tag-info">
<div <div className={'tag-icon mr-2'}>
className={classNames(
isCollapsed
? `relative flex h-[40px] w-[40px] items-center justify-center
${isSelected ? 'transparent-info-color-background' : 'transparent-info-color-background-hover'}`
: 'tag-icon mr-2',
)}
>
<Icon type={iconType} className={isSelected ? 'text-info' : 'text-neutral'} /> <Icon type={iconType} className={isSelected ? 'text-info' : 'text-neutral'} />
</div> </div>
{isEditing ? ( {isEditing ? (
<input <input
className={classNames('title editing', isCollapsed ? 'hidden' : 'block')} className={'title editing'}
id={`react-tag-${view.uuid}`} id={`react-tag-${view.uuid}`}
onBlur={onBlur} onBlur={onBlur}
onInput={onInput} onInput={onInput}
@@ -147,19 +134,14 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState, isColla
ref={inputRef} ref={inputRef}
/> />
) : ( ) : (
<div <div className={'title overflow-hidden text-left'} id={`react-tag-${view.uuid}`}>
className={classNames('title overflow-hidden text-left', isCollapsed ? 'hidden' : 'block')}
id={`react-tag-${view.uuid}`}
>
{title} {title}
</div> </div>
)} )}
<div className={classNames('count', isCollapsed ? 'absolute top-0 right-0' : '')}> <div className={'count'}>{view.uuid === SystemViewId.AllNotes && tagsState.allNotesCount}</div>
{view.uuid === SystemViewId.AllNotes && tagsState.allNotesCount}
</div>
</div> </div>
{!isSystemView(view) && !isCollapsed && ( {!isSystemView(view) && (
<div className="meta"> <div className="meta">
{view.conflictOf && ( {view.conflictOf && (
<div className="danger text-[0.625rem] font-bold">Conflicted Copy {view.conflictOf}</div> <div className="danger text-[0.625rem] font-bold">Conflicted Copy {view.conflictOf}</div>

View File

@@ -5,13 +5,12 @@ import SmartViewsList from './SmartViewsList'
type Props = { type Props = {
viewControllerManager: ViewControllerManager viewControllerManager: ViewControllerManager
isCollapsed: boolean
} }
const SmartViewsSection: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed }) => { const SmartViewsSection: FunctionComponent<Props> = ({ viewControllerManager }) => {
return ( return (
<section> <section>
<SmartViewsList viewControllerManager={viewControllerManager} isCollapsed={isCollapsed} /> <SmartViewsList viewControllerManager={viewControllerManager} />
</section> </section>
) )
} }

View File

@@ -9,10 +9,9 @@ import { TagsListItem } from './TagsListItem'
type Props = { type Props = {
viewControllerManager: ViewControllerManager viewControllerManager: ViewControllerManager
isCollapsed: boolean
} }
const TagsList: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed }: Props) => { const TagsList: FunctionComponent<Props> = ({ viewControllerManager }: Props) => {
const tagsState = viewControllerManager.navigationController const tagsState = viewControllerManager.navigationController
const allTags = tagsState.allLocalRootTags const allTags = tagsState.allLocalRootTags
@@ -53,7 +52,6 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed
tagsState={tagsState} tagsState={tagsState}
features={viewControllerManager.featuresController} features={viewControllerManager.featuresController}
onContextMenu={onContextMenu} onContextMenu={onContextMenu}
isCollapsed={isCollapsed}
/> />
) )
})} })}

View File

@@ -22,7 +22,6 @@ import { useDrag, useDrop } from 'react-dnd'
import { DropItem, DropProps, ItemTypes } from './DragNDrop' import { DropItem, DropProps, ItemTypes } from './DragNDrop'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider' import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata' import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { classNames } from '@/Utils/ConcatenateClassNames'
type Props = { type Props = {
tag: SNTag tag: SNTag
@@ -30,306 +29,287 @@ type Props = {
features: FeaturesController features: FeaturesController
level: number level: number
onContextMenu: (tag: SNTag, posX: number, posY: number) => void onContextMenu: (tag: SNTag, posX: number, posY: number) => void
isCollapsed: boolean
} }
const PADDING_BASE_PX = 14 const PADDING_BASE_PX = 14
const PADDING_BASE_WHEN_COLLAPSED_PX = 6
const PADDING_PER_LEVEL_PX = 21 const PADDING_PER_LEVEL_PX = 21
const PADDING_PER_LEVEL_WHEN_COLLAPSED_PX = 10
export const TagsListItem: FunctionComponent<Props> = observer( export const TagsListItem: FunctionComponent<Props> = observer(({ tag, features, tagsState, level, onContextMenu }) => {
({ tag, features, tagsState, level, onContextMenu, isCollapsed }) => { const { toggleAppPane } = useResponsiveAppPane()
const { toggleAppPane } = useResponsiveAppPane()
const [title, setTitle] = useState(tag.title || '') const [title, setTitle] = useState(tag.title || '')
const [subtagTitle, setSubtagTitle] = useState('') const [subtagTitle, setSubtagTitle] = useState('')
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
const subtagInputRef = useRef<HTMLInputElement>(null) const subtagInputRef = useRef<HTMLInputElement>(null)
const menuButtonRef = useRef<HTMLAnchorElement>(null) const menuButtonRef = useRef<HTMLAnchorElement>(null)
const isSelected = tagsState.selected === tag const isSelected = tagsState.selected === tag
const isEditing = tagsState.editingTag === tag const isEditing = tagsState.editingTag === tag
const isAddingSubtag = tagsState.addingSubtagTo === tag const isAddingSubtag = tagsState.addingSubtagTo === tag
const noteCounts = computed(() => tagsState.getNotesCount(tag)) const noteCounts = computed(() => tagsState.getNotesCount(tag))
const childrenTags = computed(() => tagsState.getChildren(tag)).get() const childrenTags = computed(() => tagsState.getChildren(tag)).get()
const hasChildren = childrenTags.length > 0 const hasChildren = childrenTags.length > 0
const hasFolders = features.hasFolders const hasFolders = features.hasFolders
const hasAtLeastOneFolder = tagsState.hasAtLeastOneFolder const hasAtLeastOneFolder = tagsState.hasAtLeastOneFolder
const premiumModal = usePremiumModal() const premiumModal = usePremiumModal()
const [showChildren, setShowChildren] = useState(tag.expanded) const [showChildren, setShowChildren] = useState(tag.expanded)
const [hadChildren, setHadChildren] = useState(hasChildren) const [hadChildren, setHadChildren] = useState(hasChildren)
useEffect(() => { useEffect(() => {
if (!hadChildren && hasChildren) { if (!hadChildren && hasChildren) {
setShowChildren(true) setShowChildren(true)
} }
setHadChildren(hasChildren) setHadChildren(hasChildren)
}, [hadChildren, hasChildren]) }, [hadChildren, hasChildren])
useEffect(() => { useEffect(() => {
setTitle(tag.title || '') setTitle(tag.title || '')
}, [setTitle, tag]) }, [setTitle, tag])
const toggleChildren: MouseEventHandler = useCallback( const toggleChildren: MouseEventHandler = useCallback(
(e) => { (e) => {
e.stopPropagation() e.stopPropagation()
setShowChildren((x) => { setShowChildren((x) => {
tagsState.setExpanded(tag, !x) tagsState.setExpanded(tag, !x)
return !x return !x
}) })
}, },
[setShowChildren, tag, tagsState], [setShowChildren, tag, tagsState],
) )
const selectCurrentTag = useCallback(async () => { const selectCurrentTag = useCallback(async () => {
await tagsState.setSelectedTag(tag) await tagsState.setSelectedTag(tag)
toggleAppPane(AppPaneId.Items) toggleAppPane(AppPaneId.Items)
}, [tagsState, tag, toggleAppPane]) }, [tagsState, tag, toggleAppPane])
const onBlur = useCallback(() => { const onBlur = useCallback(() => {
tagsState.save(tag, title).catch(console.error) tagsState.save(tag, title).catch(console.error)
setTitle(tag.title) setTitle(tag.title)
}, [tagsState, tag, title, setTitle]) }, [tagsState, tag, title, setTitle])
const onInput: FormEventHandler = useCallback( const onInput: FormEventHandler = useCallback(
(e) => { (e) => {
const value = (e.target as HTMLInputElement).value
setTitle(value)
},
[setTitle],
)
const onKeyDown: KeyboardEventHandler = useCallback(
(e) => {
if (e.key === KeyboardKey.Enter) {
inputRef.current?.blur()
e.preventDefault()
}
},
[inputRef],
)
useEffect(() => {
if (isEditing) {
inputRef.current?.focus()
}
}, [inputRef, isEditing])
const onSubtagInput = useCallback((e) => {
const value = (e.target as HTMLInputElement).value const value = (e.target as HTMLInputElement).value
setSubtagTitle(value) setTitle(value)
}, []) },
[setTitle],
)
const onSubtagInputBlur = useCallback(() => { const onKeyDown: KeyboardEventHandler = useCallback(
tagsState.createSubtagAndAssignParent(tag, subtagTitle).catch(console.error) (e) => {
setSubtagTitle('') if (e.key === KeyboardKey.Enter) {
}, [subtagTitle, tag, tagsState]) inputRef.current?.blur()
e.preventDefault()
const onSubtagKeyDown: KeyboardEventHandler = useCallback(
(e) => {
if (e.key === KeyboardKey.Enter) {
e.preventDefault()
subtagInputRef.current?.blur()
}
},
[subtagInputRef],
)
useEffect(() => {
if (isAddingSubtag) {
subtagInputRef.current?.focus()
} }
}, [subtagInputRef, isAddingSubtag]) },
[inputRef],
)
const [, dragRef] = useDrag( useEffect(() => {
() => ({ if (isEditing) {
type: ItemTypes.TAG, inputRef.current?.focus()
item: { uuid: tag.uuid }, }
canDrag: () => { }, [inputRef, isEditing])
return true
}, const onSubtagInput = useCallback((e) => {
collect: (monitor) => ({ const value = (e.target as HTMLInputElement).value
isDragging: !!monitor.isDragging(), setSubtagTitle(value)
}), }, [])
const onSubtagInputBlur = useCallback(() => {
tagsState.createSubtagAndAssignParent(tag, subtagTitle).catch(console.error)
setSubtagTitle('')
}, [subtagTitle, tag, tagsState])
const onSubtagKeyDown: KeyboardEventHandler = useCallback(
(e) => {
if (e.key === KeyboardKey.Enter) {
e.preventDefault()
subtagInputRef.current?.blur()
}
},
[subtagInputRef],
)
useEffect(() => {
if (isAddingSubtag) {
subtagInputRef.current?.focus()
}
}, [subtagInputRef, isAddingSubtag])
const [, dragRef] = useDrag(
() => ({
type: ItemTypes.TAG,
item: { uuid: tag.uuid },
canDrag: () => {
return true
},
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}), }),
[tag], }),
) [tag],
)
const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>( const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>(
() => ({ () => ({
accept: ItemTypes.TAG, accept: ItemTypes.TAG,
canDrop: (item) => { canDrop: (item) => {
return tagsState.isValidTagParent(tag, item as SNTag) return tagsState.isValidTagParent(tag, item as SNTag)
}, },
drop: (item) => { drop: (item) => {
if (!hasFolders) { if (!hasFolders) {
premiumModal.activate(TAG_FOLDERS_FEATURE_NAME) premiumModal.activate(TAG_FOLDERS_FEATURE_NAME)
return
}
tagsState.assignParent(item.uuid, tag.uuid).catch(console.error)
},
collect: (monitor) => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
}),
[tag, tagsState, hasFolders, premiumModal],
)
const readyToDrop = isOver && canDrop
const toggleContextMenu: MouseEventHandler<HTMLAnchorElement> = useCallback(
(event) => {
event.preventDefault()
event.stopPropagation()
if (!menuButtonRef.current) {
return return
} }
tagsState.assignParent(item.uuid, tag.uuid).catch(console.error)
const contextMenuOpen = tagsState.contextMenuOpen
const menuButtonRect = menuButtonRef.current?.getBoundingClientRect()
if (contextMenuOpen) {
tagsState.setContextMenuOpen(false)
} else {
onContextMenu(tag, menuButtonRect.right, menuButtonRect.top)
}
}, },
[onContextMenu, tagsState, tag], collect: (monitor) => ({
) isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
}),
[tag, tagsState, hasFolders, premiumModal],
)
return ( const readyToDrop = isOver && canDrop
<>
<button const toggleContextMenu: MouseEventHandler<HTMLAnchorElement> = useCallback(
className={`tag focus:shadow-inner ${isSelected ? 'selected' : ''} ${readyToDrop ? 'is-drag-over' : ''}`} (event) => {
onClick={selectCurrentTag} event.preventDefault()
ref={dragRef} event.stopPropagation()
style={{
paddingLeft: `${ if (!menuButtonRef.current) {
isCollapsed return
? level * PADDING_PER_LEVEL_WHEN_COLLAPSED_PX + PADDING_BASE_WHEN_COLLAPSED_PX }
: level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX
}px`, const contextMenuOpen = tagsState.contextMenuOpen
}} const menuButtonRect = menuButtonRef.current?.getBoundingClientRect()
onContextMenu={(e) => {
e.preventDefault() if (contextMenuOpen) {
onContextMenu(tag, e.clientX, e.clientY) tagsState.setContextMenuOpen(false)
}} } else {
> onContextMenu(tag, menuButtonRect.right, menuButtonRect.top)
<div className="tag-info" title={title} ref={dropRef}> }
{hasAtLeastOneFolder && ( },
<div className="tag-fold-container"> [onContextMenu, tagsState, tag],
<a )
role="button"
className={`tag-fold focus:shadow-inner ${showChildren ? 'opened' : 'closed'} ${ return (
!hasChildren ? 'invisible' : '' <>
}`} <button
onClick={hasChildren ? toggleChildren : undefined} className={`tag focus:shadow-inner ${isSelected ? 'selected' : ''} ${readyToDrop ? 'is-drag-over' : ''}`}
> onClick={selectCurrentTag}
<Icon className={'text-neutral'} type={showChildren ? 'menu-arrow-down-alt' : 'menu-arrow-right'} /> ref={dragRef}
</a> style={{
</div> paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
)} }}
<div onContextMenu={(e) => {
className={classNames('tag-icon draggable mr-2', isCollapsed ? 'md-only:!hidden lg-only:!hidden' : '')} e.preventDefault()
ref={dragRef} onContextMenu(tag, e.clientX, e.clientY)
> }}
<Icon type="hashtag" className={`${isSelected ? 'text-info' : 'text-neutral'}`} /> >
</div> <div className="tag-info" title={title} ref={dropRef}>
{isEditing ? ( {hasAtLeastOneFolder && (
<input <div className="tag-fold-container">
className={classNames(
'title editing focus:shadow-none focus:outline-none',
isCollapsed ? 'md-only:!w-min lg-only:!w-min' : '',
)}
id={`react-tag-${tag.uuid}`}
onBlur={onBlur}
onInput={onInput}
value={title}
onKeyDown={onKeyDown}
spellCheck={false}
ref={inputRef}
/>
) : (
<div
className={classNames(
'title overflow-hidden text-left focus:shadow-none focus:outline-none',
isCollapsed ? 'md-only:!w-min lg-only:!w-min' : '',
)}
id={`react-tag-${tag.uuid}`}
>
{title}
</div>
)}
<div className="flex items-center">
<a <a
role="button" role="button"
className={`mr-2 cursor-pointer border-0 bg-transparent hover:bg-contrast focus:shadow-inner ${ className={`tag-fold focus:shadow-inner ${showChildren ? 'opened' : 'closed'} ${
isSelected ? 'visible' : 'invisible' !hasChildren ? 'invisible' : ''
}`} }`}
onClick={toggleContextMenu} onClick={hasChildren ? toggleChildren : undefined}
ref={menuButtonRef}
> >
<Icon type="more" className="text-neutral" /> <Icon className={'text-neutral'} type={showChildren ? 'menu-arrow-down-alt' : 'menu-arrow-right'} />
</a> </a>
<div className="count">{noteCounts.get()}</div>
</div> </div>
)}
<div className={'tag-icon draggable mr-2'} ref={dragRef}>
<Icon type="hashtag" className={`${isSelected ? 'text-info' : 'text-neutral'}`} />
</div> </div>
{isEditing ? (
<input
className={'title editing focus:shadow-none focus:outline-none'}
id={`react-tag-${tag.uuid}`}
onBlur={onBlur}
onInput={onInput}
value={title}
onKeyDown={onKeyDown}
spellCheck={false}
ref={inputRef}
/>
) : (
<div
className={'title overflow-hidden text-left focus:shadow-none focus:outline-none'}
id={`react-tag-${tag.uuid}`}
>
{title}
</div>
)}
<div className="flex items-center">
<a
role="button"
className={`mr-2 cursor-pointer border-0 bg-transparent hover:bg-contrast focus:shadow-inner ${
isSelected ? 'visible' : 'invisible'
}`}
onClick={toggleContextMenu}
ref={menuButtonRef}
>
<Icon type="more" className="text-neutral" />
</a>
<div className="count">{noteCounts.get()}</div>
</div>
</div>
<div className={`meta ${hasAtLeastOneFolder ? 'with-folders' : ''}`}> <div className={`meta ${hasAtLeastOneFolder ? 'with-folders' : ''}`}>
{tag.conflictOf && <div className="danger text-[0.625rem] font-bold">Conflicted Copy {tag.conflictOf}</div>} {tag.conflictOf && <div className="danger text-[0.625rem] font-bold">Conflicted Copy {tag.conflictOf}</div>}
</div> </div>
</button> </button>
{isAddingSubtag && ( {isAddingSubtag && (
<div <div
className="tag overflow-hidden" className="tag overflow-hidden"
style={{ style={{
paddingLeft: `${(level + 1) * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`, paddingLeft: `${(level + 1) * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
}} }}
> >
<div className="tag-info"> <div className="tag-info">
<div className="flex h-full min-w-[22px] items-center border-0 bg-transparent p-0" /> <div className="flex h-full min-w-[22px] items-center border-0 bg-transparent p-0" />
<div className="tag-icon mr-1"> <div className="tag-icon mr-1">
<Icon type="hashtag" className="mr-1 text-neutral" /> <Icon type="hashtag" className="mr-1 text-neutral" />
</div>
<input
className="title w-full focus:shadow-none focus:outline-none"
type="text"
ref={subtagInputRef}
onBlur={onSubtagInputBlur}
onKeyDown={onSubtagKeyDown}
value={subtagTitle}
onInput={onSubtagInput}
/>
</div> </div>
<input
className="title w-full focus:shadow-none focus:outline-none"
type="text"
ref={subtagInputRef}
onBlur={onSubtagInputBlur}
onKeyDown={onSubtagKeyDown}
value={subtagTitle}
onInput={onSubtagInput}
/>
</div> </div>
)} </div>
{showChildren && ( )}
<> {showChildren && (
{childrenTags.map((tag) => { <>
return ( {childrenTags.map((tag) => {
<TagsListItem return (
level={level + 1} <TagsListItem
key={tag.uuid} level={level + 1}
tag={tag} key={tag.uuid}
tagsState={tagsState} tag={tag}
features={features} tagsState={tagsState}
onContextMenu={onContextMenu} features={features}
isCollapsed={isCollapsed} onContextMenu={onContextMenu}
/> />
) )
})} })}
</> </>
)} )}
</> </>
) )
}, })
)
TagsListItem.displayName = 'TagsListItem' TagsListItem.displayName = 'TagsListItem'

View File

@@ -5,14 +5,12 @@ import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback, useEffect, useState } from 'react' import { FunctionComponent, useCallback, useEffect, useState } from 'react'
import TagsSectionAddButton from './TagsSectionAddButton' import TagsSectionAddButton from './TagsSectionAddButton'
import TagsSectionTitle from './TagsSectionTitle' import TagsSectionTitle from './TagsSectionTitle'
import { classNames } from '@/Utils/ConcatenateClassNames'
type Props = { type Props = {
viewControllerManager: ViewControllerManager viewControllerManager: ViewControllerManager
isCollapsed: boolean
} }
const TagsSection: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed }) => { const TagsSection: FunctionComponent<Props> = ({ viewControllerManager }) => {
const [hasMigration, setHasMigration] = useState<boolean>(false) const [hasMigration, setHasMigration] = useState<boolean>(false)
const checkIfMigrationNeeded = useCallback(() => { const checkIfMigrationNeeded = useCallback(() => {
@@ -55,7 +53,7 @@ const TagsSection: FunctionComponent<Props> = ({ viewControllerManager, isCollap
return ( return (
<section> <section>
<div className={classNames('section-title-bar', isCollapsed ? 'md-only:hidden lg-only:hidden' : '')}> <div className={'section-title-bar'}>
<div className="section-title-bar-header"> <div className="section-title-bar-header">
<TagsSectionTitle <TagsSectionTitle
features={viewControllerManager.featuresController} features={viewControllerManager.featuresController}
@@ -68,13 +66,7 @@ const TagsSection: FunctionComponent<Props> = ({ viewControllerManager, isCollap
/> />
</div> </div>
</div> </div>
<div <TagsList viewControllerManager={viewControllerManager} />
className={classNames(
'hidden',
isCollapsed ? 'mt-6 mb-7 border border-border md-only:block lg-only:block' : '',
)}
/>
<TagsList viewControllerManager={viewControllerManager} isCollapsed={isCollapsed} />
</section> </section>
) )
} }

View File

@@ -7,16 +7,15 @@ import { FunctionComponent } from 'react'
type Props = { type Props = {
tags: NavigationController tags: NavigationController
features: FeaturesController features: FeaturesController
className?: string
} }
const TagsSectionAddButton: FunctionComponent<Props> = ({ tags, className = '' }) => { const TagsSectionAddButton: FunctionComponent<Props> = ({ tags }) => {
return ( return (
<IconButton <IconButton
focusable={true} focusable={true}
icon="add" icon="add"
title="Create a new tag" title="Create a new tag"
className={`p-0 text-neutral ${className}`} className="p-0 text-neutral"
onClick={() => tags.createNewTemplate()} onClick={() => tags.createNewTemplate()}
/> />
) )

View File

@@ -9,10 +9,9 @@ type Props = {
features: FeaturesController features: FeaturesController
hasMigration: boolean hasMigration: boolean
onClickMigration: () => void onClickMigration: () => void
className?: string
} }
const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, onClickMigration, className = '' }) => { const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, onClickMigration }) => {
const entitledToFolders = features.hasFolders const entitledToFolders = features.hasFolders
const modal = usePremiumModal() const modal = usePremiumModal()
@@ -23,7 +22,7 @@ const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, on
if (entitledToFolders) { if (entitledToFolders) {
return ( return (
<> <>
<div className={`title text-sm ${className}`}> <div className="title text-sm">
<span className="font-bold">Folders</span> <span className="font-bold">Folders</span>
{hasMigration && ( {hasMigration && (
<label className="ml-1 cursor-pointer font-bold text-info" onClick={onClickMigration}> <label className="ml-1 cursor-pointer font-bold text-info" onClick={onClickMigration}>
@@ -37,7 +36,7 @@ const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, on
return ( return (
<> <>
<div className={`title text-sm ${className}`}> <div className="title text-sm">
<span className="font-bold">Tags</span> <span className="font-bold">Tags</span>
<Tooltip label={TAG_FOLDERS_FEATURE_TOOLTIP}> <Tooltip label={TAG_FOLDERS_FEATURE_TOOLTIP}>
<label className="ml-1 cursor-pointer font-bold text-passive-2" onClick={showPremiumAlert}> <label className="ml-1 cursor-pointer font-bold text-passive-2" onClick={showPremiumAlert}>

View File

@@ -67,11 +67,6 @@ $heading-height: 75px;
position: relative; position: relative;
min-width: 16ch; min-width: 16ch;
overflow: visible; overflow: visible;
margin-right: 20px;
@media screen and (min-width: 768px) {
max-width: 16ch;
}
} }
#save-status { #save-status {

View File

@@ -132,21 +132,6 @@
@include DimmedBackground(var(--sn-stylekit-info-color), 0.08); @include DimmedBackground(var(--sn-stylekit-info-color), 0.08);
} }
.transparent-info-color-background {
&::after {
@include DimmedBackground(var(--sn-stylekit-info-color), 0.12);
border-radius: 100px;
}
}
.transparent-info-color-background-hover {
&:hover {
&::after {
@extend .transparent-info-color-background;
}
}
}
svg.sk-circular-progress { svg.sk-circular-progress {
$pi: 3.14159265358979; $pi: 3.14159265358979;