Skip to content

Commit

Permalink
feat: ability to cancel multiple selection from UI (#1045)
Browse files Browse the repository at this point in the history
  • Loading branch information
moughxyz authored May 23, 2022
1 parent acdf442 commit 8877c42
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const FileContextMenu: FunctionComponent<Props> = observer(({ appState })
const [closeOnBlur] = useCloseOnBlur(contextMenuRef, (open: boolean) => setShowFileContextMenu(open))
useCloseOnClickOutside(contextMenuRef, () => appState.files.setShowFileContextMenu(false))

const selectedFile = Object.values(selectedFiles)[0]
const selectedFile = selectedFiles[0]
if (!showFileContextMenu || !selectedFile) {
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { observer } from 'mobx-react-lite'
import { NotesOptionsPanel } from '@/Components/NotesOptions/NotesOptionsPanel'
import { WebApplication } from '@/UIModels/Application'
import { PinNoteButton } from '@/Components/PinNoteButton/PinNoteButton'
import { Button } from '../Button/Button'
import { useCallback } from 'preact/hooks'

type Props = {
application: WebApplication
Expand All @@ -13,6 +15,10 @@ type Props = {
export const MultipleSelectedNotes = observer(({ application, appState }: Props) => {
const count = appState.notes.selectedNotesCount

const cancelMultipleSelection = useCallback(() => {
appState.selectedItems.cancelMultipleSelection()
}, [appState])

return (
<div className="flex flex-col h-full items-center">
<div className="flex items-center justify-between p-4 w-full">
Expand All @@ -28,6 +34,9 @@ export const MultipleSelectedNotes = observer(({ application, appState }: Props)
<IlNotesIcon className="block" />
<h2 className="text-lg m-0 text-center mt-4">{count} selected notes</h2>
<p className="text-sm mt-2 text-center max-w-60">Actions will be performed on all selected notes.</p>
<Button className="mt-2.5" onClick={cancelMultipleSelection}>
Cancel multiple selection
</Button>
</div>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export const NotesOptions = observer(({ application, appState, closeOnBlur }: No
return notesMatchingAttribute.length > notesNotMatchingAttribute.length
}

const notes = Object.values(appState.notes.selectedNotes)
const notes = appState.notes.selectedNotes
const hidePreviews = toggleOn((note) => note.hidePreview)
const locked = toggleOn((note) => note.locked)
const protect = toggleOn((note) => note.protected)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const PinNoteButton: FunctionComponent<Props> = observer(
return null
}

const notes = Object.values(appState.notes.selectedNotes)
const notes = appState.notes.selectedNotes
const pinned = notes.some((note) => note.pinned)

const togglePinned = useCallback(async () => {
Expand Down
5 changes: 5 additions & 0 deletions app/assets/javascripts/Services/IOService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,14 @@ export class IOService {
if (!modifier) {
return
}

this.activeModifiers.delete(modifier)
}

public cancelAllKeyboardModifiers = (): void => {
this.activeModifiers.clear()
}

public handleComponentKeyDown = (modifier: KeyboardModifier | undefined): void => {
this.addActiveModifier(modifier)
}
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/UIModels/AppState/FilesState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class FilesState extends AbstractState {
)
}

get selectedFiles() {
get selectedFiles(): FileItem[] {
return this.appState.selectedItems.getSelectedItems<FileItem>(ContentType.File)
}

Expand Down
12 changes: 6 additions & 6 deletions app/assets/javascripts/UIModels/AppState/NotesState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ export class NotesState extends AbstractState {
application.streamItems<SNNote>(ContentType.Note, ({ changed, inserted, removed }) => {
runInAction(() => {
for (const removedNote of removed) {
delete this.selectedNotes[removedNote.uuid]
this.appState.selectedItems.deselectItem(removedNote)
}

for (const note of [...changed, ...inserted]) {
if (this.selectedNotes[note.uuid]) {
this.selectedNotes[note.uuid] = note
if (this.appState.selectedItems.isItemSelected(note)) {
this.appState.selectedItems.updateReferenceOfSelectedItem(note)
}
}
})
Expand All @@ -80,14 +80,14 @@ export class NotesState extends AbstractState {

for (const selectedId of selectedUuids) {
if (!activeNoteUuids.includes(selectedId)) {
delete this.selectedNotes[selectedId]
this.appState.selectedItems.deselectItem({ uuid: selectedId })
}
}
}),
)
}

get selectedNotes() {
public get selectedNotes(): SNNote[] {
return this.appState.selectedItems.getSelectedItems<SNNote>(ContentType.Note)
}

Expand Down Expand Up @@ -262,7 +262,7 @@ export class NotesState extends AbstractState {
if (permanently) {
for (const note of this.getSelectedNotesList()) {
await this.application.mutator.deleteItem(note)
delete this.selectedNotes[note.uuid]
this.appState.selectedItems.deselectItem(note)
}
} else {
await this.changeSelectedNotes((mutator) => {
Expand Down
56 changes: 47 additions & 9 deletions app/assets/javascripts/UIModels/AppState/SelectedItemsState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,32 @@ export class SelectedItemsState extends AbstractState {
return Object.keys(this.selectedItems).length
}

getSelectedItems = <T extends ListableContentItem>(contentType: ContentType) => {
const filteredEntries = Object.entries(this.appState.selectedItems.selectedItems).filter(
([_, item]) => item.content_type === contentType,
) as [UuidString, T][]
return Object.fromEntries<T>(filteredEntries)
getSelectedItems = <T extends ListableContentItem = ListableContentItem>(contentType?: ContentType): T[] => {
return Object.values(this.selectedItems).filter((item) => {
return !contentType ? true : item.content_type === contentType
}) as T[]
}

setSelectedItems = (selectedItems: SelectedItems) => {
this.selectedItems = selectedItems
}

public deselectItem = (item: { uuid: ListableContentItem['uuid'] }): void => {
delete this.selectedItems[item.uuid]

if (item.uuid === this.lastSelectedItem?.uuid) {
this.lastSelectedItem = undefined
}
}

public isItemSelected = (item: ListableContentItem): boolean => {
return this.selectedItems[item.uuid] != undefined
}

public updateReferenceOfSelectedItem = (item: ListableContentItem): void => {
this.selectedItems[item.uuid] = item
}

private selectItemsRange = async (selectedItem: ListableContentItem): Promise<void> => {
const items = this.appState.contentListView.renderedItems

Expand All @@ -88,6 +103,32 @@ export class SelectedItemsState extends AbstractState {
}
}

cancelMultipleSelection = () => {
this.io.cancelAllKeyboardModifiers()

const firstSelectedItem = this.getSelectedItems()[0]

if (firstSelectedItem) {
this.replaceSelection(firstSelectedItem)
} else {
this.deselectAll()
}
}

private replaceSelection = (item: ListableContentItem): void => {
this.setSelectedItems({
[item.uuid]: item,
})

this.lastSelectedItem = item
}

private deselectAll = (): void => {
this.setSelectedItems({})

this.lastSelectedItem = undefined
}

selectItem = async (
uuid: UuidString,
userTriggered?: boolean,
Expand Down Expand Up @@ -119,10 +160,7 @@ export class SelectedItemsState extends AbstractState {
} else {
const shouldSelectNote = hasMoreThanOneSelected || !this.selectedItems[uuid]
if (shouldSelectNote && isAuthorizedForAccess) {
this.setSelectedItems({
[item.uuid]: item,
})
this.lastSelectedItem = item
this.replaceSelection(item)
}
}

Expand Down
6 changes: 3 additions & 3 deletions app/assets/stylesheets/_theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
--text-selection-background-color: var(--sn-stylekit-info-color);

--note-preview-progress-color: var(--sn-stylekit-info-color);
--note-preview-progress-background-color: var(--sn-stylekit-contrast-background-color);
--note-preview-progress-background-color: var(--sn-stylekit-grey-4-opacity-variant);

--note-preview-selected-progress-color: var(--sn-stylekit-secondary-background-color);
--note-preview-selected-progress-background-color: var(--sn-stylekit-secondary-foreground-color);
--note-preview-selected-progress-background-color:var(--sn-stylekit-grey-4-opacity-variant);

--items-column-background-color: var(--sn-stylekit-background-color);
--items-column-items-background-color: var(--sn-stylekit-background-color);
--items-column-border-left-color: var(--sn-stylekit-border-color);
--items-column-border-right-color: var(--sn-stylekit-border-color);
--items-column-search-background-color: var(--sn-stylekit-contrast-background-color);
--item-cell-selected-background-color: var(--sn-stylekit-grey-5);
--item-cell-selected-background-color: var(--sn-stylekit-contrast-background-color);
--item-cell-selected-border-left-color: var(--sn-stylekit-info-color);

--navigation-column-background-color: var(--sn-stylekit-secondary-background-color);
Expand Down

0 comments on commit 8877c42

Please sign in to comment.