# Dynamic Filterable Gallery Widget

> Filterable image gallery sourced from post taxonomy terms. Two template skins (cards, hoverer). Extends Lite's Filterable_Gallery widget concepts via Pro's `Filterable_Gallery_Extender` trait.

**Class file:** `includes/Elements/Dynamic_Filterable_Gallery.php` (2,420 lines)
**Slug:** `dynamic-filter-gallery` (widget id `eael-dynamic-filterable-gallery`)
**Public docs:** <https://essential-addons.com/elementor/docs/dynamic-gallery/>
**Pro-shared:** Pro-only. Tightly coupled to Pro's `Filterable_Gallery_Extender` trait + Lite's Filterable_Gallery CSS.

## Overview

Pulls images from posts of a chosen post type and renders them as a filterable grid. Filter buttons are taxonomy terms; clicking filters the visible items. Two layout templates: `cards` (image + caption card) and `hoverer` (image-only with hover-state overlay). Built on top of Lite's `filterable-gallery.min.css` foundation — Pro adds dynamic-source behaviour and per-template styling.

## Pro vs Lite

Pro-only widget. Pro's `Filterable_Gallery_Extender` trait also extends **Lite's** static Filterable_Gallery widget with similar features. This widget is the "dynamic source" sibling.

## File Map

| File | Role |
| --- | --- |
| `includes/Elements/Dynamic_Filterable_Gallery.php` | Widget class (2,420 lines) |
| `includes/Template/Dynamic-Filterable-Gallery/cards.php` | Cards template |
| `includes/Template/Dynamic-Filterable-Gallery/hoverer.php` | Hoverer template |
| `includes/Traits/Filterable_Gallery_Extender.php` | Pro trait — also injects features into Lite's Filterable_Gallery widget |
| `src/css/view/dynamic-filter-gallery.scss` → `assets/front-end/css/view/dynamic-filter-gallery.min.css` | Pro-specific styling |
| `EAEL_PLUGIN_PATH/assets/front-end/css/view/filterable-gallery.min.css` | Lite base styling (shared) |
| `EAEL_PLUGIN_PATH/assets/front-end/css/view/load-more.min.css` | Lite load-more CSS |
| `src/js/view/dynamic-filter-gallery.js` → `assets/front-end/js/view/dynamic-filter-gallery.min.js` | Filter + load-more init |
| `config.php` entry `'dynamic-filter-gallery'` | Mixed Lite + Pro CSS, Pro JS |

## Architecture

- **Composes Lite's `Helper` trait** — line 16: `use Helper;` from `Essential_Addons_Elementor\Traits\Helper`. Standard Lite-helper composition pattern. See [`_patterns.md § Helper imports`](_patterns.md#helper-imports).
- **Imports both Helper classes** — `HelperClass` (Lite) and `HelperClassPro` (Pro) — line 13-15. The widget uses Lite query helpers AND Pro presentation helpers.
- **Template dispatch via `eael_fg_grid_style`** — control default is `'hoverer'`; options include `'eael-hoverer'` and `'eael-cards'`. Note the **double-naming inconsistency**: control accepts both legacy slugs (`eael-cards`) and short slugs (`cards`). Render path branches on either form. See condition at line 225: `'eael_fg_grid_style' => [ 'eael-cards', 'cards' ]`.
- **`Filterable_Gallery_Extender` trait** — composed into `Bootstrap`, not into this widget. The trait targets **Lite's** Filterable_Gallery widget, adding Pro layouts/styles there. The widget here is a parallel "dynamic-source" widget; the Extender doesn't directly affect it.
- **Layout values commented out at line 100+** — older controls (`eael_dynamic_template_Layout`) are commented. Refactor leftovers.
- **Lite owns filtering JS and AJAX** — the filter + load-more logic ships from Lite's filterable-gallery handler. Pro contributes the dynamic-source backend and the dynamic-filter-gallery CSS.

## Render Output

```html
<div class="eael-filter-gallery-wrapper">
  <div class="eael-filter-gallery-control">
    <ul>
      <li class="active" data-filter="*">All</li>
      <li data-filter=".{term-slug}">{Term Name}</li>
      ...
    </ul>
  </div>
  <div class="eael-filter-gallery-container grid eael-{template}">
    <!-- One item per post; templates differ -->
    <div class="eael-filterable-gallery-item-wrap {term-slug-list}">
      <div class="dynamic-gallery-item-inner">
        <div class="dynamic-gallery-thumbnail">
          <img src="{thumbnail}" />
          [?] <div class="overlay">{title} / {excerpt} / link button (hoverer template)</div>
        </div>
        [?] <div class="card-body">{title} / {excerpt} (cards template)</div>
      </div>
    </div>
  </div>
  [?] <button class="eael-load-more-button">Load More</button>
</div>
```

## Controls Reference

| Control id | Tab → Section | Type | Purpose |
| --- | --- | --- | --- |
| `eael_fg_grid_style` | Content → Layout | SELECT | `cards` / `hoverer` (template selector) |
| Source post type + taxonomy | Content → Query | various | Standard Lite query controls |
| Default filter | Content → Filter | SELECT | Initial active term |
| Filter button styling | Style → Filter | various | Active / hover / inactive button styling |
| Card height / hoverer height | Style → Gallery | SLIDER | Per-template item height |
| Pagination (load-more) | Content → Pagination | SWITCHER | Enable AJAX load-more |

## Conditional Dependencies

```text
eael_fg_grid_style = 'eael-cards' | 'cards'
  └── shows card-specific styling
  └── activates cards.php template

eael_fg_grid_style = 'hoverer' | 'eael-hoverer'
  └── shows hoverer-specific styling
  └── activates hoverer.php template

load_more = 'yes'
  └── shows load-more pagination
```

## JavaScript Lifecycle

`src/js/view/dynamic-filter-gallery.js` + Lite's `filterable-gallery.min.js` (loaded via Lite CSS path's sibling script if present, OR inline). Isotope-based filtering — items animate in/out of the visible grid.

Filter click handler:

```js
$( '.eael-filter-gallery-control li' ).on( 'click', function() {
    var filter = $( this ).data( 'filter' );
    $grid.isotope( { filter: filter } );
} );
```

## Hooks & Filters

Standard widget render. Filter / load-more goes through Lite's AJAX handlers.

## Common Issues

| Symptom | Likely cause | Diagnose | Fix |
| --- | --- | --- | --- |
| Items don't filter | Isotope not loaded | Check console for `isotope is not a function` | Verify Lite is active; Isotope ships via Lite's `lib-view/` |
| Filter buttons missing | Source post type has no taxonomy / no terms attached | Inspect WP_Query output | Assign terms to posts |
| Cards template renders as hoverer | `grid_style` accepts both `cards` and `eael-cards` — mismatched conditions in CSS vs PHP | Inspect computed class on `.eael-filter-gallery-container` | Verify the template-selected class matches CSS rules; older user content uses `eael-cards` prefix, newer uses bare `cards` |
| Load-more breaks filter state | After load-more, new items aren't in the Isotope instance | Inspect grid after load-more | Re-run `$grid.isotope( 'appended', $newItems )` after AJAX append (Lite handler responsibility) |
| Filter button labels wrong | Term names changed but cached | Check WP cache | Clear term cache; reload editor |

## Known Limitations

- **Double-naming inconsistency (`cards` vs `eael-cards`)** — saved pages from different Pro versions may use either form; render path tolerates both, but maintenance burden
- **Comment-out artefacts** at line 88-93 and 105 — old template-layout controls left as comments
- **Lite-coupled filtering / load-more** — Lite owns the JS; Pro updates require Lite-side changes for filter behaviour
- **Filter relies on Isotope** — not lazy-loaded; ships on every page with this widget
- **No per-template hook** for third-party templates — adding a 3rd template requires editing `register_controls` and adding a PHP file
- **`_Extender` trait shares the namespace** but doesn't extend THIS widget — naming is misleading. See `Filterable_Gallery_Extender` for the actual Lite-widget extension

## Cross-References

- Sibling: [`post-block.md`](post-block.md) — also uses Lite's load-more CSS
- Architecture: [`docs/architecture/pro-lite-bridge.md`](../architecture/pro-lite-bridge.md) — Filterable_Gallery_Extender pattern
- Lite widget: `Filterable_Gallery` (Lite-side static-source counterpart, extended by Pro)
- Shared patterns: [`_patterns.md`](_patterns.md)
