fix: unable to add children to favorited tag; remove expand-on-click; render arrow before title (#1931)

This commit is contained in:
Mo
2022-11-03 09:38:31 -05:00
committed by GitHub
parent 63c10c703f
commit 5bb4435c5b
8 changed files with 96 additions and 134 deletions

View File

@@ -50,7 +50,7 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
}, [setTitle, view]) }, [setTitle, view])
const selectCurrentTag = useCallback(async () => { const selectCurrentTag = useCallback(async () => {
await tagsState.setSelectedTag(view, { await tagsState.setSelectedTag(view, 'views', {
userTriggered: true, userTriggered: true,
}) })
toggleAppPane(AppPaneId.Items) toggleAppPane(AppPaneId.Items)
@@ -86,7 +86,7 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
}, [inputRef, isEditing]) }, [inputRef, isEditing])
const onClickRename = useCallback(() => { const onClickRename = useCallback(() => {
tagsState.editingTag = view tagsState.setEditingTag(view)
}, [tagsState, view]) }, [tagsState, view])
const onClickSave = useCallback(() => { const onClickSave = useCallback(() => {

View File

@@ -40,8 +40,7 @@ const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag
const onClickRename = useCallback(() => { const onClickRename = useCallback(() => {
navigationController.setContextMenuOpen(false) navigationController.setContextMenuOpen(false)
navigationController.editingFrom = navigationController.contextMenuOpenFrom navigationController.setEditingTag(selectedTag)
navigationController.editingTag = selectedTag
}, [navigationController, selectedTag]) }, [navigationController, selectedTag])
const onClickDelete = useCallback(() => { const onClickDelete = useCallback(() => {

View File

@@ -1 +1 @@
export type TagListSectionType = 'all' | 'favorites' export type TagListSectionType = 'views' | 'all' | 'favorites'

View File

@@ -25,19 +25,18 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, type }: Pro
x: posX, x: posX,
y: posY, y: posY,
}) })
viewControllerManager.navigationController.setContextMenuOpenFrom(type)
viewControllerManager.navigationController.reloadContextMenuLayout() viewControllerManager.navigationController.reloadContextMenuLayout()
viewControllerManager.navigationController.setContextMenuOpen(true) viewControllerManager.navigationController.setContextMenuOpen(true)
}, },
[viewControllerManager, type], [viewControllerManager],
) )
const onContextMenu = useCallback( const onContextMenu = useCallback(
(tag: SNTag, posX: number, posY: number) => { (tag: SNTag, posX: number, posY: number) => {
void viewControllerManager.navigationController.setSelectedTag(tag) void viewControllerManager.navigationController.setSelectedTag(tag, type)
openTagContextMenu(posX, posY) openTagContextMenu(posX, posY)
}, },
[viewControllerManager, openTagContextMenu], [viewControllerManager, openTagContextMenu, type],
) )
return ( return (

View File

@@ -47,24 +47,21 @@ export const TagsListItem: FunctionComponent<Props> = observer(
({ tag, type, features, navigationController: navigationController, level, onContextMenu, linkingController }) => { ({ tag, type, features, navigationController: navigationController, level, onContextMenu, linkingController }) => {
const { toggleAppPane } = useResponsiveAppPane() const { toggleAppPane } = useResponsiveAppPane()
const isFavorite = type === 'favorites'
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 = navigationController.selected === tag const isSelected = navigationController.selected === tag && navigationController.selectedLocation === type
const isEditing = navigationController.editingTag === tag && navigationController.editingFrom === type const isEditing = navigationController.editingTag === tag && navigationController.selectedLocation === type
const isAddingSubtag = navigationController.addingSubtagTo === tag const isAddingSubtag = navigationController.addingSubtagTo === tag && navigationController.selectedLocation === type
const noteCounts = computed(() => navigationController.getNotesCount(tag)) const noteCounts = computed(() => navigationController.getNotesCount(tag))
const childrenTags = computed(() => navigationController.getChildren(tag)).get() const childrenTags = computed(() => navigationController.getChildren(tag)).get()
const hasChildren = childrenTags.length > 0 const hasChildren = childrenTags.length > 0
const hasFolders = features.hasFolders const hasFolders = features.hasFolders
const hasAtLeastOneFolder = navigationController.hasAtLeastOneFolder
const premiumModal = usePremiumModal() const premiumModal = usePremiumModal()
@@ -93,12 +90,11 @@ export const TagsListItem: FunctionComponent<Props> = observer(
) )
const selectCurrentTag = useCallback(async () => { const selectCurrentTag = useCallback(async () => {
await navigationController.setSelectedTag(tag, { await navigationController.setSelectedTag(tag, type, {
userTriggered: true, userTriggered: true,
}) })
toggleChildren()
toggleAppPane(AppPaneId.Items) toggleAppPane(AppPaneId.Items)
}, [navigationController, tag, toggleAppPane, toggleChildren]) }, [navigationController, tag, type, toggleAppPane])
const onBlur = useCallback(() => { const onBlur = useCallback(() => {
navigationController.save(tag, title).catch(console.error) navigationController.save(tag, title).catch(console.error)
@@ -257,23 +253,14 @@ export const TagsListItem: FunctionComponent<Props> = observer(
}} }}
> >
<div className="tag-info" title={title} ref={dropRef}> <div className="tag-info" title={title} ref={dropRef}>
{hasAtLeastOneFolder && !isFavorite && ( <div onClick={selectCurrentTag} className={'tag-icon draggable mr-2'} ref={dragRef}>
<div className="tag-fold-container"> <Icon
<a type={tag.iconString as IconType}
role="button" className={`cursor-pointer ${isSelected ? 'text-info' : 'text-neutral'}`}
className={`tag-fold focus:shadow-inner ${showChildren ? 'opened' : 'closed'} ${ />
!hasChildren ? 'invisible' : ''
}`}
onClick={hasChildren ? toggleChildren : undefined}
>
<Icon className={'text-neutral'} type={showChildren ? 'menu-arrow-down-alt' : 'menu-arrow-right'} />
</a>
</div>
)}
<div className={'tag-icon draggable mr-2'} ref={dragRef}>
<Icon type={tag.iconString as IconType} className={`${isSelected ? 'text-info' : 'text-neutral'}`} />
</div> </div>
{isEditing ? (
{isEditing && (
<input <input
className={ className={
'title editing text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item' 'title editing text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item'
@@ -286,34 +273,60 @@ export const TagsListItem: FunctionComponent<Props> = observer(
spellCheck={false} spellCheck={false}
ref={inputRef} ref={inputRef}
/> />
) : (
<div
className={
'title overflow-hidden text-left text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item'
}
id={`react-tag-${tag.uuid}-${type}`}
>
{title}
</div>
)} )}
{!isEditing && (
<>
<div
className={
'title overflow-hidden text-left text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item'
}
id={`react-tag-${tag.uuid}-${type}`}
>
{title}
</div>
</>
)}
<div className="flex items-center"> <div className="flex items-center">
<a {isSelected && (
role="button" <a
className={`mr-2 cursor-pointer border-0 bg-transparent hover:bg-contrast focus:shadow-inner ${ role="button"
isSelected ? 'visible' : 'invisible' className={'mr-2 cursor-pointer border-0 bg-transparent hover:bg-contrast focus:shadow-inner'}
onClick={toggleContextMenu}
ref={menuButtonRef}
>
<Icon type="more" className="text-neutral" />
</a>
)}
{hasChildren && (
<a
role="button"
className={`focus:shadow-inner ${showChildren ? 'cursor-n-resize' : 'cursor-s-resize'} ${
showChildren ? 'opened' : 'closed'
} `}
onClick={toggleChildren}
>
<Icon
className={'text-neutral'}
size="large"
type={showChildren ? 'menu-arrow-down-alt' : 'menu-arrow-right'}
/>
</a>
)}
<div
onClick={hasChildren ? toggleChildren : undefined}
className={`count text-base lg:text-sm ${
hasChildren ? (showChildren ? 'cursor-n-resize' : 'cursor-s-resize') : ''
}`} }`}
onClick={toggleContextMenu}
ref={menuButtonRef}
> >
<Icon type="more" className="text-neutral" /> {noteCounts.get()}
</a> </div>
<div className="count text-base lg:text-sm">{noteCounts.get()}</div>
</div> </div>
</div> </div>
<div className={`meta ${hasAtLeastOneFolder ? 'with-folders' : ''}`}> {tag.conflictOf && <div className="-mt-1 text-[0.625rem] font-bold text-danger">Conflicted Copy</div>}
{tag.conflictOf && <div className="-mt-1 text-[0.625rem] font-bold text-danger">Conflicted Copy</div>}
</div>
</div> </div>
{isAddingSubtag && ( {isAddingSubtag && (
<div <div

View File

@@ -247,7 +247,7 @@ export class LinkingController extends AbstractViewController {
this.setIsLinkingPanelOpen(false) this.setIsLinkingPanelOpen(false)
if (item instanceof SNTag) { if (item instanceof SNTag) {
await this.navigationController.setSelectedTag(item) await this.navigationController.setSelectedTag(item, 'all')
return AppPaneId.Items return AppPaneId.Items
} else if (item instanceof SNNote) { } else if (item instanceof SNNote) {
await this.navigationController.selectHomeNavigationView() await this.navigationController.selectHomeNavigationView()

View File

@@ -2,9 +2,7 @@ import { confirmDialog, NavigationControllerPersistableValue } from '@standardno
import { STRING_DELETE_TAG } from '@/Constants/Strings' import { STRING_DELETE_TAG } from '@/Constants/Strings'
import { MAX_MENU_SIZE_MULTIPLIER, MENU_MARGIN_FROM_APP_BORDER, SMART_TAGS_FEATURE_NAME } from '@/Constants/Constants' import { MAX_MENU_SIZE_MULTIPLIER, MENU_MARGIN_FROM_APP_BORDER, SMART_TAGS_FEATURE_NAME } from '@/Constants/Constants'
import { import {
ComponentAction,
ContentType, ContentType,
MessageData,
SmartView, SmartView,
SNTag, SNTag,
TagMutator, TagMutator,
@@ -37,14 +35,13 @@ export class NavigationController
starredTags: SNTag[] = [] starredTags: SNTag[] = []
allNotesCount_ = 0 allNotesCount_ = 0
selectedUuid: AnyTag['uuid'] | undefined = undefined selectedUuid: AnyTag['uuid'] | undefined = undefined
selected_: AnyTag | undefined selected_: AnyTag | undefined = undefined
previouslySelected_: AnyTag | undefined selectedLocation: TagListSectionType | undefined = undefined
editing_: SNTag | SmartView | undefined previouslySelected_: AnyTag | undefined = undefined
editingFrom?: TagListSectionType editing_: SNTag | SmartView | undefined = undefined
addingSubtagTo: SNTag | undefined addingSubtagTo: SNTag | undefined = undefined
contextMenuOpen = false contextMenuOpen = false
contextMenuOpenFrom?: TagListSectionType
contextMenuPosition: { top?: number; left: number; bottom?: number } = { contextMenuPosition: { top?: number; left: number; bottom?: number } = {
top: 0, top: 0,
left: 0, left: 0,
@@ -58,24 +55,18 @@ export class NavigationController
super(application, eventBus) super(application, eventBus)
this.tagsCountsState = new TagsCountsState(this.application) this.tagsCountsState = new TagsCountsState(this.application)
this.selected_ = undefined
this.previouslySelected_ = undefined
this.editing_ = undefined
this.addingSubtagTo = undefined
this.smartViews = this.application.items.getSmartViews() this.smartViews = this.application.items.getSmartViews()
makeObservable(this, { makeObservable(this, {
tags: observable, tags: observable,
starredTags: observable, starredTags: observable,
smartViews: observable.ref, smartViews: observable.ref,
hasAtLeastOneFolder: computed,
allNotesCount_: observable, allNotesCount_: observable,
allNotesCount: computed, allNotesCount: computed,
setAllNotesCount: action, setAllNotesCount: action,
selected_: observable, selected_: observable,
selectedLocation: observable,
previouslySelected_: observable.ref, previouslySelected_: observable.ref,
previouslySelected: computed, previouslySelected: computed,
editing_: observable.ref, editing_: observable.ref,
@@ -166,14 +157,14 @@ export class NavigationController
) )
} }
findAndSetTag = (uuid: UuidString) => { private findAndSetTag = (uuid: UuidString) => {
const tagToSelect = [...this.tags, ...this.smartViews].find((tag) => tag.uuid === uuid) const tagToSelect = [...this.tags, ...this.smartViews].find((tag) => tag.uuid === uuid)
if (tagToSelect) { if (tagToSelect) {
void this.setSelectedTag(tagToSelect) void this.setSelectedTag(tagToSelect, isTag(tagToSelect) ? (tagToSelect.starred ? 'favorites' : 'all') : 'views')
} }
} }
selectHydratedTagOrDefault = () => { private selectHydratedTagOrDefault = () => {
if (this.selectedUuid && !this.selected_) { if (this.selectedUuid && !this.selected_) {
this.findAndSetTag(this.selectedUuid) this.findAndSetTag(this.selectedUuid)
} }
@@ -237,7 +228,7 @@ export class NavigationController
this.application.sync.sync().catch(console.error) this.application.sync.sync().catch(console.error)
runInAction(() => { runInAction(() => {
void this.setSelectedTag(createdTag as SNTag) void this.setSelectedTag(createdTag as SNTag, 'all')
}) })
this.setAddingSubtagTo(undefined) this.setAddingSubtagTo(undefined)
@@ -276,10 +267,6 @@ export class NavigationController
this.addingSubtagTo = tag this.addingSubtagTo = tag
} }
setContextMenuOpenFrom(section: TagListSectionType): void {
this.contextMenuOpenFrom = section
}
setContextMenuOpen(open: boolean): void { setContextMenuOpen(open: boolean): void {
this.contextMenuOpen = open this.contextMenuOpen = open
} }
@@ -436,7 +423,11 @@ export class NavigationController
}) })
} }
public async setSelectedTag(tag: AnyTag | undefined, { userTriggered } = { userTriggered: false }) { public async setSelectedTag(
tag: AnyTag | undefined,
location: TagListSectionType,
{ userTriggered } = { userTriggered: false },
) {
if (tag && tag.conflictOf) { if (tag && tag.conflictOf) {
this.application.mutator this.application.mutator
.changeAndSaveItem(tag, (mutator) => { .changeAndSaveItem(tag, (mutator) => {
@@ -445,7 +436,7 @@ export class NavigationController
.catch(console.error) .catch(console.error)
} }
const selectionHasNotChanged = this.selected_?.uuid === tag?.uuid const selectionHasNotChanged = this.selected_?.uuid === tag?.uuid && location === this.selectedLocation
if (selectionHasNotChanged) { if (selectionHasNotChanged) {
return return
@@ -455,6 +446,7 @@ export class NavigationController
await runInAction(async () => { await runInAction(async () => {
this.setSelectedTagInstance(tag) this.setSelectedTagInstance(tag)
this.selectedLocation = location
if (tag && this.application.items.isTemplateItem(tag)) { if (tag && this.application.items.isTemplateItem(tag)) {
return return
@@ -471,11 +463,11 @@ export class NavigationController
} }
public async selectHomeNavigationView(): Promise<void> { public async selectHomeNavigationView(): Promise<void> {
await this.setSelectedTag(this.homeNavigationView) await this.setSelectedTag(this.homeNavigationView, 'views')
} }
public async selectFilesView() { public async selectFilesView() {
await this.setSelectedTag(this.filesNavigationView) await this.setSelectedTag(this.filesNavigationView, 'views')
} }
get homeNavigationView(): SmartView { get homeNavigationView(): SmartView {
@@ -525,9 +517,11 @@ export class NavigationController
return this.editing_ return this.editing_
} }
public set editingTag(editingTag: SNTag | SmartView | undefined) { public setEditingTag(editingTag: SNTag | SmartView | undefined) {
this.editing_ = editingTag runInAction(() => {
void this.setSelectedTag(editingTag) this.editing_ = editingTag
void this.setSelectedTag(editingTag, this.selectedLocation || 'all')
})
} }
public createNewTemplate() { public createNewTemplate() {
@@ -540,16 +534,15 @@ export class NavigationController
const newTag = this.application.mutator.createTemplateItem(ContentType.Tag) as SNTag const newTag = this.application.mutator.createTemplateItem(ContentType.Tag) as SNTag
runInAction(() => { runInAction(() => {
this.selectedLocation = 'all'
this.editing_ = newTag this.editing_ = newTag
this.editingFrom = 'all'
}) })
} }
public undoCreateNewTag() { public undoCreateNewTag() {
this.editingFrom = undefined
this.editing_ = undefined this.editing_ = undefined
const previousTag = this.previouslySelected_ || this.smartViews[0] const previousTag = this.previouslySelected_ || this.smartViews[0]
void this.setSelectedTag(previousTag) void this.setSelectedTag(previousTag, this.selectedLocation || 'views')
} }
public async remove(tag: SNTag | SmartView, userTriggered: boolean) { public async remove(tag: SNTag | SmartView, userTriggered: boolean) {
@@ -562,7 +555,7 @@ export class NavigationController
} }
if (shouldDelete) { if (shouldDelete) {
this.application.mutator.deleteItem(tag).catch(console.error) this.application.mutator.deleteItem(tag).catch(console.error)
await this.setSelectedTag(this.smartViews[0]) await this.setSelectedTag(this.smartViews[0], 'views')
} }
} }
@@ -575,7 +568,6 @@ export class NavigationController
const hasDuplicatedTitle = siblings.some((other) => other.title.toLowerCase() === newTitle.toLowerCase()) const hasDuplicatedTitle = siblings.some((other) => other.title.toLowerCase() === newTitle.toLowerCase())
runInAction(() => { runInAction(() => {
this.editingFrom = undefined
this.editing_ = undefined this.editing_ = undefined
}) })
@@ -607,7 +599,7 @@ export class NavigationController
const insertedTag = await this.application.mutator.createTagOrSmartView(newTitle) const insertedTag = await this.application.mutator.createTagOrSmartView(newTitle)
this.application.sync.sync().catch(console.error) this.application.sync.sync().catch(console.error)
runInAction(() => { runInAction(() => {
void this.setSelectedTag(insertedTag as SNTag) void this.setSelectedTag(insertedTag as SNTag, this.selectedLocation || 'views')
}) })
} else { } else {
await this.application.mutator.changeAndSaveItem<TagMutator>(tag, (mutator) => { await this.application.mutator.changeAndSaveItem<TagMutator>(tag, (mutator) => {
@@ -615,31 +607,6 @@ export class NavigationController
}) })
} }
} }
public onFoldersComponentMessage(action: ComponentAction, data: MessageData): void {
if (action === ComponentAction.SelectItem) {
const item = data.item
if (!item) {
return
}
if (item.content_type === ContentType.Tag || item.content_type === ContentType.SmartView) {
const matchingTag = this.application.items.findItem(item.uuid)
if (matchingTag) {
void this.setSelectedTag(matchingTag as AnyTag)
return
}
}
} else if (action === ComponentAction.ClearSelection) {
void this.setSelectedTag(this.smartViews[0])
}
}
public get hasAtLeastOneFolder(): boolean {
return this.tags.some((tag) => !!this.application.items.getTagParent(tag))
}
} }
class TagsCountsState { class TagsCountsState {

View File

@@ -88,18 +88,9 @@ $content-horizontal-padding: 16px;
display: flex; display: flex;
align-items: center; align-items: center;
height: 100%; height: 100%;
&.draggable {
cursor: move;
}
&.propose-folders {
cursor: help;
}
} }
> .title { > .title {
width: 80%;
background-color: transparent; background-color: transparent;
font-weight: 600; font-weight: 600;
color: var(--navigation-item-text-color); color: var(--navigation-item-text-color);
@@ -107,7 +98,6 @@ $content-horizontal-padding: 16px;
border: none; border: none;
cursor: pointer; cursor: pointer;
text-overflow: ellipsis; text-overflow: ellipsis;
width: 75%;
flex-grow: 1; flex-grow: 1;
// Required for Safari to avoid highlighting when dragging panel resizers // Required for Safari to avoid highlighting when dragging panel resizers
@@ -144,12 +134,6 @@ $content-horizontal-padding: 16px;
} }
.meta { .meta {
padding-left: 3px;
&.with-folders {
padding-left: 25px;
}
> .menu { > .menu {
font-size: 11px; font-size: 11px;
@@ -160,8 +144,8 @@ $content-horizontal-padding: 16px;
opacity: 0.5; opacity: 0.5;
font-weight: bold; font-weight: bold;
clear: both; clear: both;
margin-top: 2px;
margin-bottom: 2px; margin-bottom: 2px;
padding-bottom: 5px;
&:hover { &:hover {
opacity: 1; opacity: 1;