Search views (#367)
Add support for switching between viewing newly added items and recently used items in search for weapons and summons
This commit is contained in:
parent
8877f3cfeb
commit
62b957034f
5 changed files with 116 additions and 40 deletions
|
|
@ -16,9 +16,6 @@
|
||||||
@include breakpoint(phone) {
|
@include breakpoint(phone) {
|
||||||
place-items: flex-end;
|
place-items: flex-end;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
|
||||||
&.filter {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialogContent {
|
.dialogContent {
|
||||||
|
|
@ -72,7 +69,7 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 430px;
|
height: 60vh;
|
||||||
max-height: none;
|
max-height: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
|
@ -93,7 +90,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.editParty {
|
&.editParty {
|
||||||
min-height: 80vh;
|
// min-height: 80vh;
|
||||||
|
height: 60vh;
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -103,6 +101,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
|
||||||
&.scrollable {
|
&.scrollable {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.header {
|
.header {
|
||||||
align-items: inherit;
|
align-items: inherit;
|
||||||
|
border-bottom: 1px solid var(--button-contained-bg);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: $unit;
|
gap: $unit;
|
||||||
|
|
@ -65,23 +66,59 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.results {
|
.results {
|
||||||
|
flex-grow: 1;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 ($unit * 1.5);
|
padding: $unit-2x ($unit * 1.5) 0;
|
||||||
padding-bottom: $unit * 1.5;
|
padding-bottom: $unit * 1.5;
|
||||||
|
|
||||||
// Infinite scroll
|
// Infinite scroll
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: 500px;
|
|
||||||
|
|
||||||
@include breakpoint(phone) {
|
@include breakpoint(phone) {
|
||||||
max-height: initial;
|
max-height: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
h5.total {
|
.totalRow {
|
||||||
font-size: $font-regular;
|
font-size: $font-small;
|
||||||
font-weight: $normal;
|
display: flex;
|
||||||
color: var(--text-tertiary);
|
justify-content: space-between;
|
||||||
padding: $unit-half ($unit * 1.5);
|
padding-bottom: $unit-half;
|
||||||
|
|
||||||
|
h5.total {
|
||||||
|
font-weight: $bold;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
padding: $unit-half ($unit * 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewSwitcher {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
gap: $unit-fourth;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
margin-right: $unit-half;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
background: none;
|
||||||
|
border-radius: $item-corner-small;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
display: inline-block;
|
||||||
|
font-size: $font-small;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--button-contained-bg);
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: var(--button-contained-bg);
|
||||||
|
font-weight: $bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import type { SearchableObject, SearchableObjectArray } from '~types'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
import CrossIcon from '~public/icons/Cross.svg'
|
import CrossIcon from '~public/icons/Cross.svg'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
interface Props extends DialogProps {
|
interface Props extends DialogProps {
|
||||||
send: (object: SearchableObject, position: number) => any
|
send: (object: SearchableObject, position: number) => any
|
||||||
|
|
@ -57,8 +58,20 @@ const SearchModal = (props: Props) => {
|
||||||
// Pagination states
|
// Pagination states
|
||||||
const [recordCount, setRecordCount] = useState(0)
|
const [recordCount, setRecordCount] = useState(0)
|
||||||
const [currentPage, setCurrentPage] = useState(1)
|
const [currentPage, setCurrentPage] = useState(1)
|
||||||
|
const [currentView, setCurrentView] = useState(0)
|
||||||
const [totalPages, setTotalPages] = useState(1)
|
const [totalPages, setTotalPages] = useState(1)
|
||||||
|
|
||||||
|
// Classes
|
||||||
|
const newestViewClasses = classNames({
|
||||||
|
[styles.link]: true,
|
||||||
|
[styles.active]: currentView === 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
const recentViewClasses = classNames({
|
||||||
|
[styles.link]: true,
|
||||||
|
[styles.active]: currentView === 1,
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (searchInput.current) searchInput.current.focus()
|
if (searchInput.current) searchInput.current.focus()
|
||||||
}, [searchInput])
|
}, [searchInput])
|
||||||
|
|
@ -119,6 +132,7 @@ const SearchModal = (props: Props) => {
|
||||||
const cookieObj: SearchableObjectArray = cookie
|
const cookieObj: SearchableObjectArray = cookie
|
||||||
? JSON.parse(cookie as string)
|
? JSON.parse(cookie as string)
|
||||||
: []
|
: []
|
||||||
|
|
||||||
let recents: SearchableObjectArray = []
|
let recents: SearchableObjectArray = []
|
||||||
|
|
||||||
if (props.object === 'weapons') {
|
if (props.object === 'weapons') {
|
||||||
|
|
@ -187,31 +201,6 @@ const SearchModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
}, [open, currentPage])
|
}, [open, currentPage])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Filters changed
|
|
||||||
const key = `recent_${props.object}`
|
|
||||||
const cookie = getCookie(key)
|
|
||||||
const cookieObj: Weapon[] | Summon[] | Character[] = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: []
|
|
||||||
|
|
||||||
if (open) {
|
|
||||||
if (
|
|
||||||
firstLoad &&
|
|
||||||
cookieObj &&
|
|
||||||
cookieObj.length > 0 &&
|
|
||||||
!extraPositions().includes(props.fromPosition)
|
|
||||||
) {
|
|
||||||
setResults(cookieObj)
|
|
||||||
setRecordCount(cookieObj.length)
|
|
||||||
setFirstLoad(false)
|
|
||||||
} else {
|
|
||||||
setCurrentPage(1)
|
|
||||||
fetchResults({ replace: true })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [filters])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Query changed
|
// Query changed
|
||||||
if (open && query.length != 1) {
|
if (open && query.length != 1) {
|
||||||
|
|
@ -231,6 +220,33 @@ const SearchModal = (props: Props) => {
|
||||||
setCurrentPage(currentPage + 1)
|
setCurrentPage(currentPage + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showNewest() {
|
||||||
|
if (currentView !== 0) {
|
||||||
|
setCurrentView(0)
|
||||||
|
|
||||||
|
setCurrentPage(1)
|
||||||
|
fetchResults({ replace: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showRecent() {
|
||||||
|
if (currentView !== 1) {
|
||||||
|
setCurrentView(1)
|
||||||
|
|
||||||
|
// Fetch recently used items
|
||||||
|
const key = `recent_${props.object}`
|
||||||
|
const cookie = getCookie(key)
|
||||||
|
const cookieObj: Weapon[] | Summon[] | Character[] = cookie
|
||||||
|
? JSON.parse(cookie as string)
|
||||||
|
: []
|
||||||
|
|
||||||
|
// Set the view
|
||||||
|
setResults(cookieObj)
|
||||||
|
setRecordCount(cookieObj.length)
|
||||||
|
setFirstLoad(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderResults() {
|
function renderResults() {
|
||||||
let jsx
|
let jsx
|
||||||
|
|
||||||
|
|
@ -437,9 +453,22 @@ const SearchModal = (props: Props) => {
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className={styles.results} ref={scrollContainer}>
|
<div className={styles.results} ref={scrollContainer}>
|
||||||
<h5 className={styles.total}>
|
<div className={styles.totalRow}>
|
||||||
{t('search.result_count', { record_count: recordCount })}
|
<h5 className={styles.total}>
|
||||||
</h5>
|
{t('search.result_count', { record_count: recordCount })}
|
||||||
|
</h5>
|
||||||
|
{(props.object === 'weapons' || props.object === 'summons') && (
|
||||||
|
<div className={styles.viewSwitcher}>
|
||||||
|
<span>{t('search.labels.view')}</span>
|
||||||
|
<button className={newestViewClasses} onClick={showNewest}>
|
||||||
|
{t('search.labels.newest')}
|
||||||
|
</button>
|
||||||
|
<button className={recentViewClasses} onClick={showRecent}>
|
||||||
|
{t('search.labels.recently_used')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{open ? renderResults() : ''}
|
{open ? renderResults() : ''}
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -512,6 +512,11 @@
|
||||||
"end_results": "No more results",
|
"end_results": "No more results",
|
||||||
"type": "Keep typing..."
|
"type": "Keep typing..."
|
||||||
},
|
},
|
||||||
|
"labels": {
|
||||||
|
"view": "View:",
|
||||||
|
"newest": "Newly added",
|
||||||
|
"recently_used": "Recently used"
|
||||||
|
},
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"weapon": "Search for a weapon...",
|
"weapon": "Search for a weapon...",
|
||||||
"summon": "Search for a summon...",
|
"summon": "Search for a summon...",
|
||||||
|
|
|
||||||
|
|
@ -510,6 +510,11 @@
|
||||||
"end_results": "検索結果これ以上ありません",
|
"end_results": "検索結果これ以上ありません",
|
||||||
"type": "もっと入力してください"
|
"type": "もっと入力してください"
|
||||||
},
|
},
|
||||||
|
"labels": {
|
||||||
|
"view": "表示:",
|
||||||
|
"newest": "最新発表",
|
||||||
|
"recently_used": "最近使用"
|
||||||
|
},
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"weapon": "武器を検索...",
|
"weapon": "武器を検索...",
|
||||||
"summon": "召喚石を検索...",
|
"summon": "召喚石を検索...",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue