Show sort direction on sort menu

This commit is contained in:
Chen-I Lim 2020-10-12 09:37:53 -07:00
parent 87413c3ad9
commit b5053c5292
5 changed files with 76 additions and 61 deletions

View file

@ -4,7 +4,6 @@ import { Block } from "../block"
import { BlockIcons } from "../blockIcons"
import { IPropertyOption } from "../board"
import { BoardTree } from "../boardTree"
import { ISortOption } from "../boardView"
import { CardFilter } from "../cardFilter"
import { Constants } from "../constants"
import { Menu } from "../menu"
@ -96,7 +95,7 @@ class BoardComponent extends React.Component<Props, State> {
Group by <span style={groupByStyle} id="groupByLabel">{boardTree.groupByProperty?.name}</span>
</div>
<div className={hasFilter ? "octo-button active" : "octo-button"} onClick={(e) => { this.filterClicked(e) }}>Filter</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { this.sortClicked(e) }}>Sort</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, mutator, boardTree) }}>Sort</div>
{this.state.isSearching
? <Editable
ref={this.searchFieldRef}
@ -275,32 +274,6 @@ class BoardComponent extends React.Component<Props, State> {
pageController.showFilter(e.target as HTMLElement)
}
private async sortClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { activeView } = boardTree
const { sortOptions } = activeView
const sortOption = sortOptions.length > 0 ? sortOptions[0] : undefined
const propertyTemplates = boardTree.board.cardProperties
Menu.shared.options = propertyTemplates.map((o) => { return { id: o.id, name: o.name } })
Menu.shared.onMenuClicked = async (propertyId: string) => {
let newSortOptions: ISortOption[] = []
if (sortOption && sortOption.propertyId === propertyId) {
// Already sorting by name, so reverse it
newSortOptions = [
{ propertyId, reversed: !sortOption.reversed }
]
} else {
newSortOptions = [
{ propertyId, reversed: false }
]
}
await mutator.changeViewSortOptions(activeView, newSortOptions)
}
Menu.shared.showAtElement(e.target as HTMLElement)
}
private async optionsClicked(e: React.MouseEvent) {
const { boardTree } = this.props

View file

@ -4,7 +4,6 @@ import { Block } from "../block"
import { BlockIcons } from "../blockIcons"
import { IPropertyTemplate } from "../board"
import { BoardTree } from "../boardTree"
import { ISortOption } from "../boardView"
import { CsvExporter } from "../csvExporter"
import { Menu } from "../menu"
import { Mutator } from "../mutator"
@ -90,7 +89,7 @@ class TableComponent extends React.Component<Props, State> {
<div className="octo-spacer"></div>
<div className="octo-button" onClick={(e) => { this.propertiesClicked(e) }}>Properties</div>
<div className={ hasFilter ? "octo-button active" : "octo-button"} onClick={(e) => { this.filterClicked(e) }}>Filter</div>
<div className={ hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { this.sortClicked(e) }}>Sort</div>
<div className={ hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, mutator, boardTree) }}>Sort</div>
{this.state.isSearching
? <Editable
ref={this.searchFieldRef}
@ -248,32 +247,6 @@ class TableComponent extends React.Component<Props, State> {
pageController.showFilter(e.target as HTMLElement)
}
private async sortClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { activeView } = boardTree
const { sortOptions } = activeView
const sortOption = sortOptions.length > 0 ? sortOptions[0] : undefined
const propertyTemplates = boardTree.board.cardProperties
Menu.shared.options = propertyTemplates.map((o) => { return { id: o.id, name: o.name } })
Menu.shared.onMenuClicked = async (propertyId: string) => {
let newSortOptions: ISortOption[] = []
if (sortOption && sortOption.propertyId === propertyId) {
// Already sorting by name, so reverse it
newSortOptions = [
{ propertyId, reversed: !sortOption.reversed }
]
} else {
newSortOptions = [
{ propertyId, reversed: false }
]
}
await mutator.changeViewSortOptions(activeView, newSortOptions)
}
Menu.shared.showAtElement(e.target as HTMLElement)
}
private async optionsClicked(e: React.MouseEvent) {
const { boardTree } = this.props

View file

@ -4,6 +4,7 @@ type MenuOption = {
id: string,
name: string,
isOn?: boolean,
icon?: "checked" | "sortUp" | "sortDown" | undefined,
type?: "separator" | "color" | "submenu" | "switch" | undefined
}
@ -54,6 +55,20 @@ class Menu {
this.showSubMenu(rect.right - bodyRect.left, rect.top - bodyRect.top, option.id)
}
} else {
if (option.icon) {
let iconName: string
switch (option.icon) {
case "checked": { iconName = "imageMenuCheck"; break }
case "sortUp": { iconName = "imageMenuSortUp"; break }
case "sortDown": { iconName = "imageMenuSortDown"; break }
default: { Utils.assertFailure(`Unsupported menu icon: ${option.icon}`) }
}
if (iconName) {
optionElement.appendChild(Utils.htmlToElement(`<div class="${iconName}" style="float: right;"></div>`))
}
}
optionElement.onmouseenter = () => {
this.hideSubMenu()
}

View file

@ -1,7 +1,7 @@
import React from "react"
import { IPropertyTemplate } from "./board"
import { BoardTree } from "./boardTree"
import { BoardView } from "./boardView"
import { BoardView, ISortOption } from "./boardView"
import { Editable } from "./components/editable"
import { Menu, MenuOption } from "./menu"
import { Mutator } from "./mutator"
@ -143,7 +143,7 @@ class OctoUtils {
showMenu(e.target as HTMLElement)
}
} : undefined}
onFocus={mutator ? () => { Menu.shared.hide() } : undefined }
onFocus={mutator ? () => { Menu.shared.hide() } : undefined}
>
{finalDisplayValue}
</div>
@ -176,7 +176,7 @@ class OctoUtils {
if (index === 0) {
return block.order / 2
}
const previousBlock = blocks[index-1]
const previousBlock = blocks[index - 1]
return (block.order + previousBlock.order) / 2
}
@ -185,9 +185,40 @@ class OctoUtils {
if (index === blocks.length - 1) {
return block.order + 1000
}
const nextBlock = blocks[index+1]
const nextBlock = blocks[index + 1]
return (block.order + nextBlock.order) / 2
}
static showSortMenu(e: React.MouseEvent, mutator: Mutator, boardTree: BoardTree) {
const { activeView } = boardTree
const { sortOptions } = activeView
const sortOption = sortOptions.length > 0 ? sortOptions[0] : undefined
const propertyTemplates = boardTree.board.cardProperties
Menu.shared.options = propertyTemplates.map((o) => {
return {
id: o.id,
name: o.name,
icon: (sortOption.propertyId === o.id) ? sortOption.reversed ? "sortUp" : "sortDown" : undefined
}
})
Menu.shared.onMenuClicked = async (propertyId: string) => {
let newSortOptions: ISortOption[] = []
if (sortOption && sortOption.propertyId === propertyId) {
// Already sorting by name, so reverse it
newSortOptions = [
{ propertyId, reversed: !sortOption.reversed }
]
} else {
newSortOptions = [
{ propertyId, reversed: false }
]
}
await mutator.changeViewSortOptions(activeView, newSortOptions)
}
Menu.shared.showAtElement(e.target as HTMLElement)
}
}
export { OctoUtils }

View file

@ -6,7 +6,7 @@
}
.imageAdd {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" style="stroke:black;stroke-width:4;" stroke-opacity="50%"><polyline points="30,50 70,50" /><polyline points="50,30 50,70" /></svg>');
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" style="stroke:black;stroke-width:4;" fill="none" stroke-opacity="50%"><polyline points="30,50 70,50" /><polyline points="50,30 50,70" /></svg>');
background-size: 100% 100%;
min-width: 24px;
min-height: 24px;
@ -19,9 +19,32 @@
min-height: 24px;
}
/*-- Menu images --*/
.imageSubmenuTriangle {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><polygon points="50,35 75,50 50,65" style="fill:black;stroke:none;" fill-opacity="70%" /></svg>');
background-size: 100% 100%;
min-width: 24px;
min-height: 24px;
}
.imageMenuCheck {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" style="stroke:black;stroke-width:8;" fill="none" stroke-opacity="50%"><polyline points="20,60 40,80 80,40" /></svg>');
background-size: 100% 100%;
min-width: 24px;
min-height: 24px;
}
.imageMenuSortUp {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" style="stroke:black;stroke-width:8;" fill="none" stroke-opacity="50%"><polyline points="50,20 50,80" /><polyline points="30,40 50,20 70,40" /></svg>');
background-size: 100% 100%;
min-width: 24px;
min-height: 24px;
}
.imageMenuSortDown {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" style="stroke:black;stroke-width:8;" fill="none" stroke-opacity="50%"><polyline points="50,20 50,80" /><polyline points="30,60 50,80 70,60" /></svg>');
background-size: 100% 100%;
min-width: 24px;
min-height: 24px;
}