generated from boboko/starter
stock check, variants in cart, variant buttons in product page
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
:price="$product['price'] ?? null"
|
||||
:image="$product['image'] ?? null"
|
||||
:href="$product['href'] ?? '#'"
|
||||
:variant-id="$product['variantId'] ?? null"
|
||||
/>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
'href' => null,
|
||||
'type' => 'button',
|
||||
'size' => 'lg',
|
||||
'variant' => 'primary',
|
||||
'position' => 'relative',
|
||||
])
|
||||
|
||||
@@ -15,6 +16,16 @@
|
||||
default => 'py-5 px-[46px] text-[19px]',
|
||||
};
|
||||
|
||||
// 'primary' is the CTA look (offset-shadow via .btn-primary's ::before/
|
||||
// ::after, italic, uppercase). 'secondary' is a plain bordered toggle —
|
||||
// no shadow layers, fills solid on hover/aria-pressed=true instead, used
|
||||
// for option pickers (see x-ui.option-buttons) and anywhere else a
|
||||
// secondary/toggle action shouldn't compete visually with the CTA.
|
||||
$variantClasses = match($variant) {
|
||||
'secondary' => 'font-semibold hover:bg-black hover:text-neutral-200 aria-pressed:bg-black aria-pressed:text-neutral-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-black',
|
||||
default => 'btn-primary font-bold italic uppercase',
|
||||
};
|
||||
|
||||
// $position defaults to 'relative' (needed so the ::before/::after layers
|
||||
// in .btn-primary position against the button itself), but callers that
|
||||
// need to place the button absolutely (e.g. a hover-reveal CTA over a
|
||||
@@ -22,7 +33,7 @@
|
||||
// "absolute" via the class prop — Tailwind's generated stylesheet always
|
||||
// orders the "relative" utility after "absolute", so on a class clash
|
||||
// "relative" silently wins and the button never actually gets positioned.
|
||||
$class = 'btn-primary '.$position.' isolate inline-flex items-center justify-center border border-black font-display font-bold italic text-black cursor-pointer no-underline uppercase ' . $sizeClasses;
|
||||
$class = trim($position.' isolate inline-flex items-center justify-center border border-black text-black cursor-pointer no-underline '.$variantClasses.' '.$sizeClasses);
|
||||
|
||||
$attrs = $href
|
||||
? $attributes->merge(['href' => $href, 'class' => $class])
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
{{-- $variants: array of Modules\Core\Catalog\Services\ProductIndexer's mapVariant()
|
||||
shape (id, options: [{option, value, meta}], ...) — plain arrays, not Eloquent
|
||||
models, since this is fed from Modules\Core\Catalog\Services\ProductService.
|
||||
Use this instead of <x-ui.color-swatch> for options that aren't a color
|
||||
(no meta.hex), where a swatch dot has nothing meaningful to show. --}}
|
||||
@props(['variants', 'option' => null])
|
||||
|
||||
<div {{ $attributes }}>
|
||||
@if($option)
|
||||
<p class="font-bold mb-3 text-sm uppercase tracking-wide">
|
||||
{{ $option }}
|
||||
</p>
|
||||
@endif
|
||||
|
||||
<div
|
||||
class="flex flex-wrap gap-2"
|
||||
role="group"
|
||||
aria-label="{{ $option ?? 'Option' }}"
|
||||
>
|
||||
@foreach($variants as $variant)
|
||||
@php
|
||||
$value = $variant['options'][0] ?? null;
|
||||
$label = $value['value'] ?? '';
|
||||
@endphp
|
||||
<x-ui.button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
data-product-form-target="swatch"
|
||||
data-action="click->product-form#selectVariant"
|
||||
data-variant-id="{{ $variant['id'] }}"
|
||||
aria-label="{{ $label }}"
|
||||
aria-pressed="false"
|
||||
>{{ $label }}</x-ui.button>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,8 +1,9 @@
|
||||
@props([
|
||||
'name' => '',
|
||||
'price' => null,
|
||||
'image' => null,
|
||||
'href' => '#',
|
||||
'name' => '',
|
||||
'price' => null,
|
||||
'image' => null,
|
||||
'href' => '#',
|
||||
'variantId' => null,
|
||||
])
|
||||
|
||||
{{-- data-turbo-frame="_top" on the links: this card renders inside the
|
||||
@@ -27,9 +28,20 @@ class="w-full h-auto block"
|
||||
@endif
|
||||
</a>
|
||||
|
||||
<x-ui.button size="md" position="absolute" class="opacity-0 group-hover:opacity-100 transition-opacity duration-100">
|
||||
{{ __('storefront.product.add_to_cart') }}
|
||||
</x-ui.button>
|
||||
@if ($variantId)
|
||||
{{-- has-[...] forces the button visible while an add-to-cart error
|
||||
is showing, so it isn't only readable on hover — a shopper who
|
||||
already moved off the card (mouse or the click itself) must
|
||||
still see why nothing happened. --}}
|
||||
<x-checkout::add-to-cart
|
||||
:purchasable="$variantId"
|
||||
class="absolute opacity-0 group-hover:opacity-100 has-[.bbk-add-to-cart-error:not([hidden])]:opacity-100 transition-opacity duration-100"
|
||||
>
|
||||
<x-ui.button type="submit" size="md">
|
||||
{{ __('storefront.product.add_to_cart') }}
|
||||
</x-ui.button>
|
||||
</x-checkout::add-to-cart>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="flex items-baseline justify-between gap-4">
|
||||
|
||||
Reference in New Issue
Block a user