News
Anywhere Aloha Film: The Drive with Drew Brees
Anywhere Aloha Film: The Drive with Drew Brees
Episode Two of our Aloha Moment series features Drew Brees, another extraordinary individual who turned fate into purpose. Inspired by the unbreakable spirit of New Orleans after Hurricane Katrina, Drew embraced a pivotal moment in his career to contribute to the city's recovery and renewal.
"It was a calling that went well beyond being the quarterback of a team. It was about rebuilding one of America's greatest cities.” - Drew Brees
Shop The Story
{"id":7409768104071,
"handle":"laeahi-li-ili-mens-leather-sneakers-stone",
"title":"Lae‘ahi Lī ‘Ili - Stone",
"available":true,
"tags":["arch:standard","best:special_occasion","cat:shoes","collection:leather-sneakers","color:grey","drop-in heel®","fit:standard","free shipping","gender:men","lad ahi li ili","lae ahi li lli","lae'ahi li'ili","lae'ahi li'lli","laeahi","Laeahi Ili","Laeahi li il","Laeahi Li Ili","laeahiliili","laeai","lae‘ahi collection","lae‘ahi li ili","landleather","lea ahi li ili","leaahi lilli","leather","leather runners","leather shoe","leather sneaker","leather:material","material:leather","men","mens","RGroup_10455","shoes","size:true","sneakers","Special Occasion","split_color","style:10455","water poof","waterproof","waterproof leather"],
"type":"Men’s Waterproof Leather Sneakers",
"options_with_values": [{"name":"Color","position":1,"values":["Stone"]},{"name":"Size","position":2,"values":["7","8","8.5","9","9.5","10","10.5","11","11.5","12","13","14","15"]}],
"price":18000,
"price_min":18000,
"price_max":18000,
"compare_at_price":null,"featured_image": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_001_M_LaeahiLiIli_StoneStone_f51b50cc-6ed0-4ab8-9df1-c9e5e9a01b61.png?v=1762447329",
"featured_image_aspect_ratio": 1.0,
"featured_image_width": 2000,
"featured_image_height": 2000,
"featured_image_id": 34414970175623,
"featured_image_alt": "Stone",
"hover_image": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_101_M_Lae_ahiLiIli_Stone_bd54d5f1-9407-43fb-8367-5148f66cffd9.jpg?v=1744996363",
"hover_image_alt": "Stone",
"images": [{
"id": 34414970175623,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_001_M_LaeahiLiIli_StoneStone_f51b50cc-6ed0-4ab8-9df1-c9e5e9a01b61.png?v=1762447329",
"alt": "Stone",
"width": 2000,
"height": 2000
},{
"id": 34414970208391,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_101_M_Lae_ahiLiIli_Stone_bd54d5f1-9407-43fb-8367-5148f66cffd9.jpg?v=1744996363",
"alt": "Stone",
"width": 2000,
"height": 2000
},{
"id": 34414970241159,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_201_c7bd0de7-47c9-4df9-a32f-e2f3b8368a17.jpg?v=1744996363",
"alt": "Stone",
"width": 2000,
"height": 2000
},{
"id": 34414970273927,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_101_283d74ab-88bf-4934-9618-363b09429469.jpg?v=1744996363",
"alt": "Stone",
"width": 2000,
"height": 2000
},{
"id": 34414970306695,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_002_M_LaeahiLiIli_StoneStone.png?v=1744996363",
"alt": "Stone",
"width": 2000,
"height": 2000
},{
"id": 34414970339463,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_102_M_Lae_ahiLiIli_Stone_43081c6b-a4e6-43e4-8f55-dc6de8c71013.jpg?v=1744996363",
"alt": "Stone",
"width": 2000,
"height": 2000
},{
"id": 34414970372231,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_004_M_LaeahiLiIli_StoneStone.png?v=1744996363",
"alt": "Stone",
"width": 2000,
"height": 2000
},{
"id": 34414970404999,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_102_33242678-a540-4d35-89bc-d9dfa9be06f6.jpg?v=1744996363",
"alt": "Stone",
"width": 2000,
"height": 2000
},{
"id": 34414970437767,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_4Q4Q_005_M_LaeahiLiIli_StoneStone.png?v=1744996363",
"alt": "Stone",
"width": 2000,
"height": 2000
}],
"plp_image_order_positions": "",
"sku": "10455-4Q4Q-7"
}
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
{"id":5156404396167,
"handle":"ohana-mens-beach-sandals-dark-java-ray",
"title":"‘Ohana - Dark Java \/ Ray",
"available":true,
"tags":["'oahni","2024-gift-guide","arch:high","beach sandals","Beach\/Boat","beachbum","best sellers","best selling","best:beach_boat","best:everday_wear","best:post_workout","cat:sandals","collection:tide-tracker","collection:wfh","color:brown","color:Dark Java \/ Ray","dk java","enhanced traction outsole","Everyday Wear","EXP_REC_MEN","extra-colors","Father’s Day Gifts","fit:wide","flip flops","flipfops","gender:men","gifts","hana","labor day","likes:beach","material:synthetic","men","mens","mens flipflops","men’s","o hana","obama","Oh","oha","ohan","ohana men s sandals","ohanaa","ohanaflip flops","ohanna","ohano","ohanu","ohona","Post-Workout","prime","RGroup_10110","size:true","SP-19-COLOR","SP19-Style","split_color","style:10110","synthetic","tempstandard","vacation:tropical","vegan","water reistant","water-friendly","wide width","widewidh","YGroup_10110","‘ohana"],
"type":"Men’s Water-Friendly Beach Sandals",
"options_with_values": [{"name":"Color","position":1,"values":["Dark Java \/ Ray"]},{"name":"Size","position":2,"values":["7","8","9","10","11","12","13","14","15","16","17","18"]}],
"price":10000,
"price_min":10000,
"price_max":10000,
"compare_at_price":null,"featured_image": "\/\/olukai.ca\/cdn\/shop\/files\/10110_4827_001_M_Ohana_DarkJava_f6321f63-1b0b-446f-a607-5f8e3d241990.png?v=1762441465",
"featured_image_aspect_ratio": 1.0,
"featured_image_width": 2000,
"featured_image_height": 2000,
"featured_image_id": 34400638435463,
"featured_image_alt": "Dark Java \/ Ray",
"hover_image": "\/\/olukai.ca\/cdn\/shop\/files\/Ohana-DarkJava-galleryimage-01.jpg?v=1746734340",
"hover_image_alt": "Dark Java \/ Ray",
"images": [{
"id": 34400638435463,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10110_4827_001_M_Ohana_DarkJava_f6321f63-1b0b-446f-a607-5f8e3d241990.png?v=1762441465",
"alt": "Dark Java \/ Ray",
"width": 2000,
"height": 2000
},{
"id": 34587117027463,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/Ohana-DarkJava-galleryimage-01.jpg?v=1746734340",
"alt": "Dark Java \/ Ray",
"width": 2000,
"height": 2000
},{
"id": 34587117060231,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10110_4827_201.jpg?v=1746734340",
"alt": "Dark Java \/ Ray",
"width": 2000,
"height": 2000
},{
"id": 34587117092999,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10110_4827_101_M_OHANA_DarkJavaRay.jpg?v=1746734340",
"alt": "Dark Java \/ Ray",
"width": 2000,
"height": 2000
},{
"id": 34587117125767,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10110_4827_103_bdd18907-7a8d-42b4-bc9b-bf1bea50e003.jpg?v=1746734340",
"alt": "Dark Java \/ Ray",
"width": 2000,
"height": 2000
},{
"id": 34400638664839,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10110_4827_102_M_OHANA_DarkJavaRay_1ca47e3c-3fa6-4062-9e87-8ec31f3772c9.jpg?v=1746734340",
"alt": "Dark Java \/ Ray",
"width": 2000,
"height": 2000
},{
"id": 34400638697607,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10110_4827_101_6dd8d973-9fda-457b-a6b8-aaa9a390a1f3.jpg?v=1746734340",
"alt": "Dark Java \/ Ray",
"width": 2000,
"height": 2000
},{
"id": 34400638730375,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10110_4827_203_3eaf126b-c4dc-4d8f-ac5b-2f56b10bd4de.jpg?v=1746734340",
"alt": "Dark Java \/ Ray",
"width": 2000,
"height": 2000
},{
"id": 34400638763143,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10110_4827_005_M_Ohana_DarkJava.png?v=1746734340",
"alt": "Dark Java \/ Ray",
"width": 2000,
"height": 2000
}],
"plp_image_order_positions": "",
"sku": "10110-4827-7"
}
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
Explore Similar Stories
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>