Last active
December 3, 2025 16:40
-
-
Save bisonbrah/f9d24ff92753a9140bd0d68917bbce1d to your computer and use it in GitHub Desktop.
Block example w/ component
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Blocks; | |
| use App\Fields\Partials\PaddingOptions; | |
| use App\Traits\PaddingHelpers; | |
| use Log1x\AcfComposer\Block; | |
| use Log1x\AcfComposer\Builder; | |
| class CardsGrid extends Block | |
| { | |
| use PaddingHelpers; | |
| /** | |
| * The block name. | |
| * | |
| * @var string | |
| */ | |
| public $name = 'Cards Grid'; | |
| /** | |
| * The block description. | |
| * | |
| * @var string | |
| */ | |
| public $description = 'A beautiful Cards Grid block.'; | |
| /** | |
| * The block category. | |
| * | |
| * @var string | |
| */ | |
| public $category = 'theme'; | |
| /** | |
| * The block icon. | |
| * | |
| * @var string|array | |
| */ | |
| public $icon = 'editor-ul'; | |
| /** | |
| * The block keywords. | |
| * | |
| * @var array | |
| */ | |
| public $keywords = []; | |
| /** | |
| * The block post type allow list. | |
| * | |
| * @var array | |
| */ | |
| public $post_types = ['page']; | |
| /** | |
| * The parent block type allow list. | |
| * | |
| * @var array | |
| */ | |
| public $parent = []; | |
| /** | |
| * The ancestor block type allow list. | |
| * | |
| * @var array | |
| */ | |
| public $ancestor = []; | |
| /** | |
| * The default block mode. | |
| * | |
| * @var string | |
| */ | |
| public $mode = 'preview'; | |
| /** | |
| * The default block alignment. | |
| * | |
| * @var string | |
| */ | |
| public $align = ''; | |
| /** | |
| * The default block text alignment. | |
| * | |
| * @var string | |
| */ | |
| public $align_text = ''; | |
| /** | |
| * The default block content alignment. | |
| * | |
| * @var string | |
| */ | |
| public $align_content = ''; | |
| /** | |
| * The default block spacing. | |
| * | |
| * @var array | |
| */ | |
| public $spacing = []; | |
| /** | |
| * The supported block features. | |
| * | |
| * @var array | |
| */ | |
| public $supports = [ | |
| 'align' => true, | |
| 'align_text' => false, | |
| 'align_content' => false, | |
| 'full_height' => false, | |
| 'anchor' => false, | |
| 'mode' => true, | |
| 'multiple' => true, | |
| 'jsx' => true, | |
| 'color' => [ | |
| 'background' => false, | |
| 'text' => false, | |
| 'gradients' => false, | |
| ], | |
| 'spacing' => [ | |
| 'padding' => false, | |
| 'margin' => false, | |
| ], | |
| ]; | |
| /** | |
| * The block styles. | |
| * | |
| * @var array | |
| */ | |
| public $styles = []; | |
| /** | |
| * The block preview example data. | |
| * | |
| * @var array | |
| */ | |
| public $example = []; | |
| /** | |
| * The block template. | |
| * | |
| * @var array | |
| */ | |
| public $template = []; | |
| /** | |
| * Data to be passed to the block before rendering. | |
| */ | |
| public function with(): array | |
| { | |
| return [ | |
| 'featuredCards' => $this->featuredCards(), | |
| 'paddingClasses' => $this->paddingClasses(), | |
| ]; | |
| } | |
| /** | |
| * The block field group. | |
| */ | |
| public function fields(): array | |
| { | |
| $fields = Builder::make('cards_grid') | |
| ->addPartial(PaddingOptions::class); | |
| $fields | |
| ->addAccordion('cards_grid', [ | |
| 'label' => 'Block - Cards Grid', | |
| ]) | |
| ->addRepeater('featured_cards', [ | |
| 'label' => 'Featured Cards', | |
| 'collapsed' => 'title', | |
| ]) | |
| ->addText('title', [ | |
| 'label' => 'Title', | |
| ]) | |
| ->addWysiwyg('content', [ | |
| 'label' => 'Content', | |
| 'media_upload' => 0, | |
| ]) | |
| ->addImage('featured_image', [ | |
| 'label' => 'Featured Image', | |
| 'return_format' => 'ID', | |
| ]) | |
| // TODO: Convert this into Trait | |
| ->addSelect('ascent_color', [ | |
| 'label' => 'Ascent Color Field', | |
| 'instructions' => 'Select an ascent color.', | |
| 'choices' => [ | |
| 'border-lightBlue' => 'lightBlue', | |
| 'border-blue' => 'blue', | |
| 'border-darkBlue' => 'darkBlue', | |
| 'border-teal' => 'teal', | |
| 'border-orange' => 'orange', | |
| 'border-orangeYellow' => 'orangeYellow', | |
| 'border-green' => 'green', | |
| 'border-purple' => 'purple', | |
| 'border-red' => 'red', | |
| 'border-pink' => 'pink', | |
| ], | |
| 'allow_null' => 1, | |
| 'return_format' => 'value', | |
| 'placeholder' => 'Select an option', | |
| 'wrapper' => ['width' => '33%',], | |
| ]) | |
| ->endRepeater(); | |
| return $fields->build(); | |
| } | |
| /** | |
| * Retrieve the featured_cards field. | |
| * | |
| * @return array|null | |
| */ | |
| public function featuredCards(): ?array | |
| { | |
| $cards = get_field('featured_cards'); | |
| if (empty($cards) || !is_array($cards)) { | |
| return null; | |
| } | |
| $count = count($cards); | |
| foreach ($cards as $i => &$card) { | |
| $card['centered'] = ($count % 2 === 1) && ($i === $count - 1); | |
| } | |
| return $cards; | |
| } | |
| /** | |
| * Retrieve the ascent color class. | |
| * | |
| * @return string|null | |
| */ | |
| public function ascentColor(): ?string | |
| { | |
| return get_field('ascent_color') ?: null; | |
| } | |
| /** | |
| * Assets enqueued when rendering the block. | |
| */ | |
| public function assets(array $block): void | |
| { | |
| // | |
| } | |
| } | |
| <x-block-wrapper :block="$block" additional_classes="not-prose {{ $paddingClasses }}"> | |
| <div class="container container-md max-w-container"> | |
| <div class="xl:w-4/5 grid grid-cols-1 md:grid-cols-2 md:gap-8 lg:gap-16 xl:gap-28 items-start mx-auto justify-items-center"> | |
| @if($featuredCards) | |
| @foreach($featuredCards as $card) | |
| <x-card | |
| :title="$card['title']" | |
| :content="$card['content']" | |
| :borderColor="$card['ascent_color']" | |
| :featuredImage="$card['featured_image']" | |
| :centered="$card['centered'] ?? false" | |
| /> | |
| @endforeach | |
| @endif | |
| </div> | |
| </div> | |
| </x-block-wrapper> | |
| <?php | |
| namespace App\View\Components; | |
| use Closure; | |
| use Illuminate\Contracts\View\View; | |
| use Illuminate\View\Component; | |
| class Card extends Component | |
| { | |
| public string $title; | |
| public string $content; | |
| public string $borderColor; | |
| public int $featuredImage; | |
| public bool $centered; | |
| /** | |
| * Create a new component instance. | |
| */ | |
| public function __construct(string $title, string $content, string $borderColor, int $featuredImage, bool $centered = false) | |
| { | |
| $this->title = $title; | |
| $this->content = $content; | |
| $this->borderColor = $borderColor; | |
| $this->featuredImage = $featuredImage; | |
| $this->centered = $centered; | |
| } | |
| /** | |
| * Get the view / contents that represent the component. | |
| */ | |
| public function render(): View|Closure|string | |
| { | |
| return view('components.card'); | |
| } | |
| } | |
| <div {{ $attributes->merge([ | |
| 'class' => 'not-prose relative text-center' . (!empty($centered) ? ' col-span-1 md:col-span-2 mx-auto max-w-md w-full md:max-w-[350px] lg:min-w-[465px] xl:min-w-[505px]' : '') | |
| ]) }}> | |
| <div class="hidden lg:block absolute inset-0 -z-10 border-4 lg:h-[45px] xl:h-[75px] {{ $borderColor }} lg:-top-6 xl:-top-10 lg:-left-4 xl:-left-8 lg:-right-4 xl:-right-8"></div> | |
| <div class="relative inline-block"> | |
| {!! wp_get_attachment_image($featuredImage, 'card', false, [ | |
| 'class' => 'w-full h-auto object-cover shadow-lg my-0', | |
| 'alt' => get_post_meta($featuredImage, '_wp_attachment_image_alt', true), | |
| ]) !!} | |
| </div> | |
| <div class="mt-6 max-w-prose mx-auto"> | |
| @if($title) | |
| <h3 class="font-bold text-darkBlue uppercase text-3xl mb-2">{{ $title }}</h3> | |
| @endif | |
| @if($content) | |
| <div class="text-xl text-darkBlue font-thin [&_p]:leading-normal [&_a]:text-teal [&_a]:underline [&_a:hover]:text-darkBlue [&_a:hover]:no-underline [&_strong]:font-bold"> | |
| {!! $content !!} | |
| </div> | |
| @endif | |
| </div> | |
| </div> | |
| <?php | |
| namespace App\Fields\Partials; | |
| use Log1x\AcfComposer\Builder; | |
| use Log1x\AcfComposer\Partial; | |
| class PaddingOptions extends Partial | |
| { | |
| /** | |
| * The partial field group. | |
| */ | |
| public function fields(): Builder | |
| { | |
| $fields = Builder::make('padding_options'); | |
| $fields | |
| ->addAccordion('padding_options', [ | |
| 'label' => 'Partial - Padding Options', | |
| ]) | |
| ->addSelect('padding_top', [ | |
| 'label' => 'Padding Top', | |
| 'choices' => [ | |
| '3em' => 'Default', | |
| '1.5em' => 'Half', | |
| '6em' => 'Double', | |
| 'none' => 'None' | |
| ], | |
| 'default_value' => ['Default'], | |
| 'wrapper' => ['width' => '33%',], | |
| ]) | |
| ->addSelect('padding_bottom', [ | |
| 'label' => 'Padding Bottom', | |
| 'choices' => [ | |
| '3em' => 'Default', | |
| '1.5em' => 'Half', | |
| '6em' => 'Double', | |
| 'none' => 'None' | |
| ], | |
| 'default_value' => ['Default'], | |
| 'wrapper' => ['width' => '33%',], | |
| ]); | |
| return $fields; | |
| } | |
| } | |
| <?php | |
| namespace App\Traits; | |
| /** | |
| * Provides helper methods to manage and retrieve padding values and their corresponding | |
| * Tailwind CSS classes for top and bottom padding configuration. Used within other ACF | |
| * blocks. | |
| */ | |
| trait PaddingHelpers | |
| { | |
| /** | |
| * Return the padding_top field value. | |
| * | |
| * @return string|null | |
| */ | |
| protected function paddingTop(): ?string | |
| { | |
| return get_field('padding_top'); | |
| } | |
| /** | |
| * Return the padding_bottom field value. | |
| * | |
| * @return string|null | |
| */ | |
| protected function paddingBottom(): ?string | |
| { | |
| return get_field('padding_bottom'); | |
| } | |
| /** | |
| * Determines and returns the Tailwind classes for the top and bottom padding | |
| * based on the configured container padding values. | |
| * | |
| * @return string The combined classes representing the top and bottom padding. | |
| */ | |
| protected function paddingClasses(): string | |
| { | |
| $top = match ($this->paddingTop() ?? '') { | |
| '1.5em' => 'pt-[1.5em] md:pt-[1.5em]', | |
| '6em' => 'pt-[1.5em] md:pt-[3em] lg:pt-[6em]', | |
| 'none' => 'pt-0', | |
| default => 'pt-[1.5em] md:pt-[3em]', | |
| }; | |
| $bottom = match ($this->paddingBottom() ?? '') { | |
| '1.5em' => 'pb-[1.5em] md:pb-[1.5em]', | |
| '6em' => 'pb-[1.5em] md:pb-[3em] lg:pb-[6em]', | |
| 'none' => 'pb-0', | |
| default => 'pb-[1.5em] md:pb-[3em]', | |
| }; | |
| return "{$top} {$bottom}"; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment