mirror of
https://github.com/zen-browser/www.git
synced 2025-07-08 09:20:00 +02:00
refactor(ModsList): cache DOM elements for improved performance and readability
This commit is contained in:
parent
d7c6204b75
commit
aa7b0f608d
2 changed files with 164 additions and 75 deletions
|
@ -175,17 +175,58 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
this.lastFilteredMods = null
|
this.lastFilteredMods = null
|
||||||
this.lastRenderState = null
|
this.lastRenderState = null
|
||||||
|
|
||||||
|
// Cache DOM elements for better performance and intellisense
|
||||||
|
this.elements = this.cacheElements()
|
||||||
|
|
||||||
this.initializeFromURL()
|
this.initializeFromURL()
|
||||||
this.bindEvents()
|
this.bindEvents()
|
||||||
this.renderMods()
|
this.renderMods()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheElements() {
|
||||||
|
const searchInput = document.getElementById('search')
|
||||||
|
const limitSelect = document.getElementById('limit')
|
||||||
|
const createdSortButton = document.getElementById('created-sort')
|
||||||
|
const updatedSortButton = document.getElementById('updated-sort')
|
||||||
|
const modsGrid = document.getElementById('mods-grid')
|
||||||
|
const noResults = document.getElementById('no-results')
|
||||||
|
const pagination = document.getElementById('pagination')
|
||||||
|
|
||||||
|
// Sort icon elements
|
||||||
|
const createdSortDefault = document.getElementById('created-sort-default')
|
||||||
|
const createdSortAsc = document.getElementById('created-sort-asc')
|
||||||
|
const createdSortDesc = document.getElementById('created-sort-desc')
|
||||||
|
const updatedSortDefault = document.getElementById('updated-sort-default')
|
||||||
|
const updatedSortAsc = document.getElementById('updated-sort-asc')
|
||||||
|
const updatedSortDesc = document.getElementById('updated-sort-desc')
|
||||||
|
|
||||||
|
return {
|
||||||
|
searchInput: searchInput instanceof HTMLInputElement ? searchInput : null,
|
||||||
|
limitSelect: limitSelect instanceof HTMLSelectElement ? limitSelect : null,
|
||||||
|
createdSortButton:
|
||||||
|
createdSortButton instanceof HTMLButtonElement ? createdSortButton : null,
|
||||||
|
updatedSortButton:
|
||||||
|
updatedSortButton instanceof HTMLButtonElement ? updatedSortButton : null,
|
||||||
|
modsGrid: modsGrid instanceof HTMLDivElement ? modsGrid : null,
|
||||||
|
noResults: noResults instanceof HTMLDivElement ? noResults : null,
|
||||||
|
pagination: pagination instanceof HTMLDivElement ? pagination : null,
|
||||||
|
sortIcons: {
|
||||||
|
createdDefault: createdSortDefault instanceof HTMLSpanElement ? createdSortDefault : null,
|
||||||
|
createdAsc: createdSortAsc instanceof HTMLSpanElement ? createdSortAsc : null,
|
||||||
|
createdDesc: createdSortDesc instanceof HTMLSpanElement ? createdSortDesc : null,
|
||||||
|
updatedDefault: updatedSortDefault instanceof HTMLSpanElement ? updatedSortDefault : null,
|
||||||
|
updatedAsc: updatedSortAsc instanceof HTMLSpanElement ? updatedSortAsc : null,
|
||||||
|
updatedDesc: updatedSortDesc instanceof HTMLSpanElement ? updatedSortDesc : null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getLocalePath(path) {
|
getLocalePath(path) {
|
||||||
if (this.locale && this.locale !== 'en' && !path.startsWith(`/${this.locale}`)) {
|
if (this.locale && this.locale !== 'en' && !path.startsWith(`/${this.locale}`)) {
|
||||||
return `/${this.locale}${path.startsWith('/') ? '' : '/'}${path}`
|
return `/${this.locale}${path.startsWith('/') ? '' : '/'}${path}`
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
} // Validation helpers - similar to Zod
|
}
|
||||||
validateSortOrder(value, defaultValue = 'default') {
|
validateSortOrder(value, defaultValue = 'default') {
|
||||||
const validSortOrders = ['default', 'asc', 'desc']
|
const validSortOrders = ['default', 'asc', 'desc']
|
||||||
return validSortOrders.includes(value) ? value : defaultValue
|
return validSortOrders.includes(value) ? value : defaultValue
|
||||||
|
@ -204,7 +245,6 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
initializeFromURL() {
|
initializeFromURL() {
|
||||||
const params = new URLSearchParams(window.location.search)
|
const params = new URLSearchParams(window.location.search)
|
||||||
|
|
||||||
// Validate and sanitize URL parameters with fallbacks
|
|
||||||
const rawCreatedSort = params.get('created')
|
const rawCreatedSort = params.get('created')
|
||||||
const rawUpdatedSort = params.get('updated')
|
const rawUpdatedSort = params.get('updated')
|
||||||
const rawPage = params.get('page')
|
const rawPage = params.get('page')
|
||||||
|
@ -218,13 +258,18 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
limit: this.validateLimit(rawLimit, 12),
|
limit: this.validateLimit(rawLimit, 12),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set form values
|
// Set form values using cached elements
|
||||||
document.getElementById('search').value = this.state.search
|
if (this.elements.searchInput) {
|
||||||
document.getElementById('limit').value = this.state.limit.toString()
|
this.elements.searchInput.value = this.state.search
|
||||||
|
}
|
||||||
|
if (this.elements.limitSelect) {
|
||||||
|
this.elements.limitSelect.value = this.state.limit.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
// Search input with debouncing
|
// Search input with debouncing
|
||||||
document.getElementById('search').addEventListener('input', e => {
|
if (this.elements.searchInput) {
|
||||||
|
this.elements.searchInput.addEventListener('input', e => {
|
||||||
// Clear existing timeout
|
// Clear existing timeout
|
||||||
if (this.searchTimeout) {
|
if (this.searchTimeout) {
|
||||||
clearTimeout(this.searchTimeout)
|
clearTimeout(this.searchTimeout)
|
||||||
|
@ -232,23 +277,36 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
|
|
||||||
// Set new timeout for debounced search
|
// Set new timeout for debounced search
|
||||||
this.searchTimeout = setTimeout(() => {
|
this.searchTimeout = setTimeout(() => {
|
||||||
|
if (e.target instanceof HTMLInputElement) {
|
||||||
this.setState({ search: e.target.value, page: 1 })
|
this.setState({ search: e.target.value, page: 1 })
|
||||||
|
}
|
||||||
}, 300) // 300ms debounce
|
}, 300) // 300ms debounce
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Sort buttons
|
// Sort buttons
|
||||||
document.getElementById('created-sort').addEventListener('click', () => {
|
if (this.elements.createdSortButton) {
|
||||||
|
this.elements.createdSortButton.addEventListener('click', () => {
|
||||||
this.toggleCreatedSort()
|
this.toggleCreatedSort()
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('updated-sort').addEventListener('click', () => {
|
if (this.elements.updatedSortButton) {
|
||||||
|
this.elements.updatedSortButton.addEventListener('click', () => {
|
||||||
this.toggleUpdatedSort()
|
this.toggleUpdatedSort()
|
||||||
}) // Limit select
|
})
|
||||||
document.getElementById('limit').addEventListener('change', e => {
|
}
|
||||||
|
|
||||||
|
// Limit select
|
||||||
|
if (this.elements.limitSelect) {
|
||||||
|
this.elements.limitSelect.addEventListener('change', e => {
|
||||||
|
if (e.target instanceof HTMLSelectElement) {
|
||||||
const newLimit = this.validateLimit(e.target.value, this.state.limit)
|
const newLimit = this.validateLimit(e.target.value, this.state.limit)
|
||||||
this.setState({ limit: newLimit, page: 1 })
|
this.setState({ limit: newLimit, page: 1 })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setState(newState) {
|
setState(newState) {
|
||||||
// Prevent multiple renders
|
// Prevent multiple renders
|
||||||
if (this.isRendering) return
|
if (this.isRendering) return
|
||||||
|
@ -311,27 +369,45 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
this.setState({ updatedSort: newSort, createdSort: 'default', page: 1 })
|
this.setState({ updatedSort: newSort, createdSort: 'default', page: 1 })
|
||||||
}
|
}
|
||||||
updateSortIcons() {
|
updateSortIcons() {
|
||||||
// Update created sort icons
|
// Update created sort icons using cached elements
|
||||||
document
|
if (this.elements.sortIcons.createdDefault) {
|
||||||
.getElementById('created-sort-default')
|
this.elements.sortIcons.createdDefault.classList.toggle(
|
||||||
.classList.toggle('hidden', this.state.createdSort !== 'default')
|
'hidden',
|
||||||
document
|
this.state.createdSort !== 'default'
|
||||||
.getElementById('created-sort-asc')
|
)
|
||||||
.classList.toggle('hidden', this.state.createdSort !== 'asc')
|
}
|
||||||
document
|
if (this.elements.sortIcons.createdAsc) {
|
||||||
.getElementById('created-sort-desc')
|
this.elements.sortIcons.createdAsc.classList.toggle(
|
||||||
.classList.toggle('hidden', this.state.createdSort !== 'desc')
|
'hidden',
|
||||||
|
this.state.createdSort !== 'asc'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (this.elements.sortIcons.createdDesc) {
|
||||||
|
this.elements.sortIcons.createdDesc.classList.toggle(
|
||||||
|
'hidden',
|
||||||
|
this.state.createdSort !== 'desc'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Update updated sort icons
|
// Update updated sort icons using cached elements
|
||||||
document
|
if (this.elements.sortIcons.updatedDefault) {
|
||||||
.getElementById('updated-sort-default')
|
this.elements.sortIcons.updatedDefault.classList.toggle(
|
||||||
.classList.toggle('hidden', this.state.updatedSort !== 'default')
|
'hidden',
|
||||||
document
|
this.state.updatedSort !== 'default'
|
||||||
.getElementById('updated-sort-asc')
|
)
|
||||||
.classList.toggle('hidden', this.state.updatedSort !== 'asc')
|
}
|
||||||
document
|
if (this.elements.sortIcons.updatedAsc) {
|
||||||
.getElementById('updated-sort-desc')
|
this.elements.sortIcons.updatedAsc.classList.toggle(
|
||||||
.classList.toggle('hidden', this.state.updatedSort !== 'desc')
|
'hidden',
|
||||||
|
this.state.updatedSort !== 'asc'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (this.elements.sortIcons.updatedDesc) {
|
||||||
|
this.elements.sortIcons.updatedDesc.classList.toggle(
|
||||||
|
'hidden',
|
||||||
|
this.state.updatedSort !== 'desc'
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateURL(isSearchOrFilter = false) {
|
updateURL(isSearchOrFilter = false) {
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
|
@ -399,12 +475,13 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
const endIndex = startIndex + this.state.limit
|
const endIndex = startIndex + this.state.limit
|
||||||
const paginatedMods = filteredMods.slice(startIndex, endIndex)
|
const paginatedMods = filteredMods.slice(startIndex, endIndex)
|
||||||
|
|
||||||
const modsGrid = document.getElementById('mods-grid')
|
|
||||||
const noResults = document.getElementById('no-results')
|
|
||||||
|
|
||||||
if (paginatedMods.length > 0) {
|
if (paginatedMods.length > 0) {
|
||||||
noResults.classList.add('hidden')
|
if (this.elements.noResults) {
|
||||||
modsGrid.classList.remove('hidden')
|
this.elements.noResults.classList.add('hidden')
|
||||||
|
}
|
||||||
|
if (this.elements.modsGrid) {
|
||||||
|
this.elements.modsGrid.classList.remove('hidden')
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we're in the default state
|
// Check if we're in the default state
|
||||||
const isDefaultState =
|
const isDefaultState =
|
||||||
|
@ -452,15 +529,21 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear and append fragment (single reflow)
|
// Clear and append fragment (single reflow)
|
||||||
modsGrid.innerHTML = ''
|
if (this.elements.modsGrid) {
|
||||||
modsGrid.appendChild(fragment)
|
this.elements.modsGrid.innerHTML = ''
|
||||||
|
this.elements.modsGrid.appendChild(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
// Track that we've modified the content
|
// Track that we've modified the content
|
||||||
this.hasBeenModified = !isDefaultState
|
this.hasBeenModified = !isDefaultState
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
modsGrid.classList.add('hidden')
|
if (this.elements.modsGrid) {
|
||||||
noResults.classList.replace('hidden', 'flex')
|
this.elements.modsGrid.classList.add('hidden')
|
||||||
|
}
|
||||||
|
if (this.elements.noResults) {
|
||||||
|
this.elements.noResults.classList.replace('hidden', 'flex')
|
||||||
|
}
|
||||||
this.hasBeenModified = true
|
this.hasBeenModified = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,11 +588,13 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPagination(totalPages, totalItems) {
|
renderPagination(totalPages, totalItems) {
|
||||||
const pagination = document.getElementById('pagination')
|
const paginationElement = this.elements.pagination
|
||||||
|
|
||||||
|
if (!paginationElement) return
|
||||||
|
|
||||||
if (totalPages <= 1) {
|
if (totalPages <= 1) {
|
||||||
pagination.innerHTML = ''
|
paginationElement.innerHTML = ''
|
||||||
pagination.classList.add('hidden')
|
paginationElement.classList.add('hidden')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,7 +624,7 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
.replace('{totalPages}', totalPages.toString())
|
.replace('{totalPages}', totalPages.toString())
|
||||||
.replace('{totalItems}', totalItems.toString())
|
.replace('{totalItems}', totalItems.toString())
|
||||||
|
|
||||||
pagination.innerHTML = `
|
paginationElement.innerHTML = `
|
||||||
${prevButton}
|
${prevButton}
|
||||||
<form class="flex items-center gap-2" id="page-form">
|
<form class="flex items-center gap-2" id="page-form">
|
||||||
<input
|
<input
|
||||||
|
@ -552,11 +637,15 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
<span class="text-sm">${paginationText.replace('{input}', '')}</span>
|
<span class="text-sm">${paginationText.replace('{input}', '')}</span>
|
||||||
</form>
|
</form>
|
||||||
${nextButton}
|
${nextButton}
|
||||||
` // Bind pagination events
|
`
|
||||||
pagination.classList.replace('hidden', 'flex')
|
|
||||||
|
// Bind pagination events
|
||||||
|
paginationElement.classList.replace('hidden', 'flex')
|
||||||
|
|
||||||
const pageForm = document.getElementById('page-form')
|
const pageForm = document.getElementById('page-form')
|
||||||
const pageInput = document.getElementById('page-input')
|
const pageInput = document.getElementById('page-input')
|
||||||
|
|
||||||
|
if (pageForm && pageInput instanceof HTMLInputElement) {
|
||||||
pageForm.addEventListener('submit', e => {
|
pageForm.addEventListener('submit', e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const inputValue = pageInput.value
|
const inputValue = pageInput.value
|
||||||
|
@ -570,10 +659,11 @@ const totalPages = Math.ceil(allMods.length / defaultLimit)
|
||||||
pageInput.value = this.state.page.toString()
|
pageInput.value = this.state.page.toString()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Bind navigation links
|
// Bind navigation links
|
||||||
pagination.addEventListener('click', e => {
|
paginationElement.addEventListener('click', e => {
|
||||||
if (e.target.tagName === 'A' && e.target.dataset.page) {
|
if (e.target instanceof HTMLAnchorElement && e.target.dataset.page) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.navigatePage(parseInt(e.target.dataset.page, 10))
|
this.navigatePage(parseInt(e.target.dataset.page, 10))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { getLocale, getPath, getUI } from '~/utils/i18n'
|
||||||
export { getStaticPaths } from '~/utils/i18n'
|
export { getStaticPaths } from '~/utils/i18n'
|
||||||
|
|
||||||
const locale = getLocale(Astro)
|
const locale = getLocale(Astro)
|
||||||
console.log(Astro.currentLocale)
|
|
||||||
const getLocalePath = getPath(locale)
|
const getLocalePath = getPath(locale)
|
||||||
const {
|
const {
|
||||||
routes: { notFound },
|
routes: { notFound },
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue