general products page and small rewrite of search and category controllers etc

This commit is contained in:
elvira
2026-09-03 21:32:40 +03:00
parent b2207a622c
commit 1f0612861b
23 changed files with 564 additions and 108 deletions
@@ -0,0 +1,24 @@
import { Controller } from '@hotwired/stimulus'
// Turbo doesn't scroll for <turbo-frame> navigations, so after the frame swaps
// its contents (a sort, filter, or pagination link) this brings the top of the
// frame back into view — otherwise clicking pagination at the bottom of the
// list leaves you stranded down there. The frame's own scroll-margin-top keeps
// it clear of the sticky header.
//
// `turbo:frame-render` fires only on a content swap, not on the initial page
// render, and the frame element itself persists across swaps — so the listener
// is bound once in connect().
export default class extends Controller {
connect() {
this.element.addEventListener('turbo:frame-render', this.#toTop)
}
disconnect() {
this.element.removeEventListener('turbo:frame-render', this.#toTop)
}
#toTop = () => {
this.element.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
}
+4
View File
@@ -8,6 +8,8 @@ import AutoSubmitController from './auto-submit-controller'
import BackToTopController from './back-to-top-controller'
import CarouselController from './carousel-controller'
import DropdownController from './dropdown-controller'
import FrameScrollController from './frame-scroll-controller'
import NavSearchController from './nav-search-controller'
import ProductFormController from './product-form-controller'
import ProductGalleryController from './product-gallery-controller'
import QuantityController from './quantity-controller'
@@ -21,6 +23,8 @@ export function registerControllers(application) {
application.register('back-to-top', BackToTopController)
application.register('carousel', CarouselController)
application.register('dropdown', DropdownController)
application.register('frame-scroll', FrameScrollController)
application.register('nav-search', NavSearchController)
application.register('product-form', ProductFormController)
application.register('product-gallery', ProductGalleryController)
application.register('quantity', QuantityController)
@@ -0,0 +1,28 @@
import { Controller } from '@hotwired/stimulus'
// The search overlay is a [popover] that must sit exactly over the header. The
// header has no fixed height below `lg`, so on open we copy its current height
// onto --search-overlay-h and move focus into the field. Escape / click-away
// close come from the Popover API; the fade is CSS (#search-overlay).
export default class extends Controller {
static targets = ['input']
connect() {
this.element.addEventListener('toggle', this.#onToggle)
}
disconnect() {
this.element.removeEventListener('toggle', this.#onToggle)
}
#onToggle = (event) => {
if (event.newState !== 'open') return
const header = this.element.closest('header')
if (header) {
this.element.style.setProperty('--search-overlay-h', `${header.offsetHeight}px`)
}
requestAnimationFrame(() => this.inputTarget.focus())
}
}