Fixed
- CI PHPStan (quality) — Playground pages use
Auth::check();urlForSlug()no longer null-coalesced; PhoneField validation messages / allow-types / state cast narrowed for level 8; Whisper install progress callback and FlexFieldAssets Livewire/env checks typed cleanly.
Fixed
- ItemCardStack / form layout CSS uninstall — central
FlexFieldAssets::resolveAssetConsumerLivewireKey()+consumerAttributesForComponent()guarantee every emit batch has a CRG consumer ({livewireId}.{component}/page.{component}when FilamentgetLivewireKey()is null).emit-assetsdefense-in-depth never ships consumer-less markers;ItemCardStack/ItemCardGroupdefault keys useisInheritable: falseso CRG ownership does not double child field paths. Consumer-less batches previously loaded then got uninstalled (~150ms), so production forms looked broken while playground bundles stayed protected. - UserColumn / table lazy CSS flash — table columns emit Livewire-scoped CRG consumers from the first cell (
{livewireId}.{component}) with the full root URL set;load-stylesheetmatches that ownership model;queued-stylesheetsstays a safety net withtable-columns.{root}. Sharedtable-columnsids previously collapsed deps and uninstalled CSS after ~150ms. - Schema conditions playground FOUC — hub now bundles
select-fieldCSS (with Flex Text Input); core anti-FOUC paints Select / Flex Text Input chrome before lazy sheets so border color no longer jumps on reload. - TranslatableFields / SegmentTabs
helperText()— schema layout now supports field-style helper copy below the tab panels (previously threwBadMethodCallException).
Changed
- Whisper runtime opt-in (#64) — ~40 MB
@xenova/transformersONNX/WASM is removed from the Composer package and no longer auto-copied topublic/bypublishStaticAssetsIfStale(). Install withphp artisan fff:whisper:install(SHA-256 pinned viaWhisperRuntimeManifest); inspect withfff:whisper:status; CI gate withfff:whisper:install --check.AudioField::transcription()soft-disables the Transcribe UI until the runtime is present (player always works). Source maps are not installed. HTTP auto-publish of package static assets is limited tolocal/testing(staging no longer re-copies on every request). Upgrade: apps that use transcription should runfff:whisper:installonce per environment after updating to 3.1.18+. - PhoneField — docs lock
nationalafter normalize as libphonenumber NATIONAL formatting; schema defaults/configurator triple-sync forbrowser_locale_*andlocale(parity with CountryField); additive playground demos (fixedLineOnly,exceptCountries,searchable(false),readOnly); PHP example national placeholders when no custom placeholder; soft clientisPossiblePhoneNumberhint from existinglibphonenumber-js/min(CSS/aria only, non-blocking). - Phone / Country / Select overlays — exclusive open is claimed before async country load;
forceHideForeignSelectMenusalso closes teleported phone/country menus (AlpinecloseTeleportedMenuImmediate) so only one dropdown stays open across phone ↔ select; playground Non-searchable uses the full country list (searchable(false)only). - PhoneField —
allowTypes()/strictTypes(),validateForRegion(),nationalDigitsOnly()(default NATIONAL stays BC),includeFormats(['international','rfc3966']),includeMetadata(['carrier','geo','timezones','type'])via PHP libphonenumber mappers (zero phone-lib JS cost); schema + Flex Forms validator parity. - Playground catalog — new
/admin/flex-fields-playground/indexhomepage lists every hub by category with GitHubart/*.webppreviews (Gravity icon fallback when art is missing); cluster entry redirects here.
Fixed
- CI PHPStan (quality PHP 8.3) — drop redundant
is_object()/ nullsafe calls that level 8 rejects after typedSpatieMediaRef::media(): objectand non-nullablegetLivewire().
Fixed
- CI quality gates —
VoiceNoteSpatieRecorderFieldmaps to sharedvoice-note-recorder-fieldassets inaudit:components(false FAIL); removed orphan CSS entry.scoped-build.test. - CI Pest flake (PHP 8.5) — Select playground render timing budget raised for CI runners (~1s wall-clock was racey under load).
Fixed
- CI PHPStan (quality PHP 8.3) — typed Media Ingress / registry helpers and refreshed
phpstan-baseline.neonso Release 3.1.13’s PHPStan job no longer fails after the Media Capture OS surface landed.
Changed
- CI / Pest speed — Select playground stress suites (
PlaygroundSelectVariantAtomicsTest~8k Livewire cases, plus cascade / Livewire / config matrices) are excluded from default Pest and CI. Maintainer one-shot only:FFF_SELECT_ATOMICS_FULL=1 composer test:select-atomics. Defaultqualityjobs should finish in minutes. - Bundle budgets — slightly raised
icon-picker-field.css/flex-fields-select-menucaps to match current dist after Select sheet / menu work.
Fixed
- FlexTextInput
->copyable()— copy action lives in the external action group (outside Filament’s.fi-input-wrp), so the stock Alpine handler threwCannot read properties of null (reading 'querySelector'). Native action-group button copies via the field shell and swaps to a check icon for ~2.5s (no Filament tooltip).
Fixed
- CI on PHP 8.3 —
composer.lockis generated withconfig.platform.php = 8.3.33so locked deps (e.g.symfony/clock) stay installable on the minimum supported PHP. The previous lock pulled Symfony 8.1 (php >=8.4.1) on a PHP 8.5 maintainer machine and broke all PHP 8.3 Actions jobs. - Faster Filament watch — Dependabot Composer + scheduled CI run daily (~06:00 UTC) so new Filament 5.x tags are usually caught within ~24h (npm/Actions Dependabot stay weekly).
Fixed
- CI reproducibility —
composer.lockis tracked in git (removed from.gitignore; stillexport-ignorefor Composer dist) so Actions no longer floating-resolves Filament (root cause of failed Release 3.1.10 / Release 3.1.9 Pest jobs).
Tests
- World-class CI: PHP 8.3/8.4/8.5 quality matrix, Filament 5.7.8 × ^5.8 compat matrix, non-blocking
5.x-devcanary,composer audit, Actions[email protected]/[email protected], and SUPPORT.md compatibility policy. - npm:
@tiptap/core/@tiptap/extension-youtube3.31.x,@gravity-ui/icons2.22.0,@playwright/test^1.62.1.
Fixed
- TranslatableFields + Filament ≥ 5.8 —
CustomizesTranslatableComponents::schema()now acceptsFilament\Schemas\Schema|array|Closure, matchingComponent::schema()after filamentphp/filament#19867 (fixes fatal signature incompatibility reported in #61). - TranslatableFields child-schema cache hygiene —
schema(),locales(),localesLabels(),spatieTranslatable(),localeFieldUsing(), andstorageAttributeUsing()now callclearCachedChildSchemas()so Filament 5.8+ cached / deferred hierarchies rebuild when templates or locales change (we intentionally bypassparent::schema()).
Tests
- Package test suite locked/run against filament/schemas v5.8.1; added
TranslatableFieldsFilament58SmokeTestcovering LSP signature,Schema/ Closure-Schematemplates, repeatedgetChildSchema()resolution, cache invalidation, and JSON hydration viafillStateWithNull(). - Select HTML smokes accept Filament 5.8 / Laravel 13.31+
Js::from()encoding (JSON.parse('{\u0022…\u0022:…}')) viatests/Support/AlpineJsHtml.php. - Enterprise Filament guardrails —
FilamentOverrideCompatibilityTest+FilamentOverrideSignatureGuardassert critical overrides stay LSP-compatible; CI jobfilament-compatmatrix; Dependabot groupsfilament/*; Composer scriptcomposer test:filament-compat.
Added
- Unified media uploads — one shared upload path for files, images, voice notes, signatures, and rich-editor attachments. You can store on a normal disk (including S3) or with Spatie Media Library, with virus scanning, private download links, and clearer multi-tenant disk settings. See Media Ingress and the migration guide.
- Spatie Media Library as a first-class choice — pick Spatie per field (including voice notes), keep full image conversions on your model, and use Form Builder
storage_driver(diskorspatie). Signatures can optionally copy into Spatie without changing how the signature is stored. - Media documentation — step-by-step examples for disk vs Spatie, S3, conversions, Form Builder, voice notes, signatures, and the rich editor.
Fixed
- Mobile select sheets (iOS Safari) — bottom sheets stay flush with
viewport-fit=coverandenv(safe-area-inset-bottom)(inner padding only). Keyboard focus uses Apple’s Visual Viewport API so the sheet docks above the keyboard without a gap, async option loads refit height correctly, and page scroll stays locked while the sheet is open. - Mobile sheet search — search inputs use 16px text so Safari does not zoom the page on focus.
- Sheet drag-to-dismiss — dismiss drag tracks the finger smoothly (no height thrash / jitter while pulling down).
- Desktop dropdown glass — Select / User Select (and other teleported menus) keep backdrop blur on desktop panels; opaque no-blur styling stays limited to mobile sheets.
- Icon picker opening — no more blank white flash when opening the picker; loading placeholders match real icon size and layout, and the panel height stays steady.
- Large select lists — long option lists (including playground demos with thousands of items) scroll through the full set instead of stopping early.
- Select “create option” — when creating new values is off, unknown options are rejected; when it is on, free-text values are allowed as intended.
- Invalid native select combinations — native select can no longer be combined with search, multiple, or HTML options (clear error at setup).
- Select, Tags, and Icon picker search — safer rate limiting and more reliable server validation for tag limits and suggestion-only mode.
- Currency field — unknown currency codes are rejected instead of being quietly remapped.
- Select dropdowns — menus close and clean up reliably when the page updates or the field is removed.
- Smoother large menus — Select, Icon picker, phone/country/currency lists, dual listbox, todo list, and timezone menus stay responsive with big option sets.
Changed
- Timezone and currency pickers — large built-in catalogs load more efficiently; small custom lists behave as before.
- User select in Studio / Form Builder — clearer option handling when a model source and static options are both present.
- Select playground — notes on configurations that are not allowed (for example native + searchable).
Docs
- Clearer guides for Select (create option, search limits, pagination), User Select, Timezone, Currency, and Trusted Proxies for search.
- Expanded Media Ingress / file upload docs for disk, Spatie, S3, and conversions.
Fixed
- FormBuilder / FlexTextInput — skip
verificationStatusColor()when the config value is null (avoids TypeError on formula/conditions builds). - FormBuilder sizes — when a definition omits
size, configurators honorfilament-flex-fields.ui.*_sizeinstead of the hardcoded default-configmd. - FlexTextarea toolbar_select — empty default
toolbar_selects: []no longer blocks the legacy singulartoolbar_selectconfig. - Hold Confirm action view — stylesheet include tolerates missing
$getLivewireKeyoutside a Livewire field context. - Credit Card PCI tests / defaults — unit coverage aligned with
never_store_pan(last4 dehydrate + tokenization required). - Mapbox geocoding rate-limit tests — set
filament-flex-fields.geocoding.rate_limit_per_minute(controller key), not only the legacy mapbox key. - Playground registry count — navigation tests expect 64 components.
- CI / asset assertion drift — barcode
openScanner($event), rich-editor stylesheet include, select CSS, UserSelectoverlay-runtime, emit-assets batch markers, Select playground render budget, and bundle budgets for select/icon-picker/select-menu.
Security
- GitHub Actions pinned to SHA —
checkout,setup-php, andsetup-nodeuse full commit SHAs (Plumb / supply-chain hardening). - Dependabot covers GitHub Actions —
.github/dependabot.ymlnow includes thegithub-actionsecosystem withcooldown.default-daysonly (semver-*-daysis invalid for Actions and fails GitHub’s config check).
Changed
- npm —
@internationalized/date^3.12.4,libphonenumber-js^1.13.12.
Fixed
- SelectField headless → Livewire sync — first pick on an empty single select (
state === null) no longer skips wire write, so fill/required validation receives the value (UI no longer shows a label while the server still sees empty). - SelectField SSR/default hydration — when Alpine still has empty wire state but
initialStateis set, init pushes the selection into Livewire once (programmatic write, not a user mutation). - SelectField dynamic options soft-fail —
getOptionsForJsreturningnullor throwing no longer applies[]and prunes a valid selection. - SelectField remote prune — option-list prune uses set equality and marks writes as programmatic so empty Livewire echoes do not wipe a restored selection.
Fixed
- SelectField clear icon — reset Filament panel
mask-image/background-coloron.fi-select-input-value-remove-btnso Gravitycircle-xmarkis not clipped to a plain × (same outline pair ascircle-chevron-down).
Fixed
- Focus outline playground — hub CSS bundle now includes Flex Text Input, Select, Textarea, Phone, and Credit Card styles (was empty), so fields no longer flash Filament’s black
fi-input-wrpring on reload. Core anti-FOUC also covers phone wrappers and Select.
Fixed
- VideoField playground / AirPlay+Cast icons — default cast/AirPlay icons no longer use Remix (
ri-cast-line/ri-airplay-line), which threwSvgNotFoundwhen that icon set is not installed. Defaults are Gravity UI (gravityui-tv,gravityui-antenna-signal), already required with the package. - ImageChoiceCards mobile lag — playground body-type silhouettes are published WebP assets instead of megabyte-scale base64 data-URIs; selection paints from native
:checkedbefore Alpinex-loadhydrates; overlay blur / imagewill-changeno longer run on coarse pointers. Remote option images warm earlier on scroll (Safari lazy) and fade in once loaded. - ChoiceCards / ChoiceCheckboxCards — selected options no longer flash on reload (SSR
is-selected+ no transition before hydrate); clicking the checkmark square toggles correctly (click.prevent+ controlled state, indicatorpointer-events: none). - FlexChecklist / FlexRadiolist — clicking the checkbox/radio control no longer double-toggles (nested label
click.stopremoved; row handler owns selection). - Playground code snippets — auto-generated hub snippets are real Field usage PHP (fluent
::make()from live demo components), notFlexFieldsPlaygroundRegistryboilerplate. Layout hubs (e.g. Segment Tabs) exportSegmentTabs/ tabs schema instead of a nested input; Segment Tabs ships a hand-written usage block.
Fixed
- Mobile overlay sheets (PhoneField, SelectField, UserSelect, and other searchable pickers) — first open no longer paints the bottom drawer under the dimmer: backdrop uses a fixed
z-indexbelow the sheet and is inserted before the panel in the DOM. - Sheet drag-to-dismiss — pulling the handle down animates with
translateY(matching CSStransform: … !importanton.is-open) instead of shrinking height, so the close gesture no longer jumps or skips the exit animation.
New
- AudioField client-side Transcribe — optional Whisper speech-to-text in the browser (
->transcription()), inspired by Xenova/whisper-web: model / language / task settings, multilingual + quantized toggles, and a session transcript panel (UI-only; not written to field state). Playground hub demos full and English-only presets.
Improved
- VideoField player rebuild — playback speed menu, quality options, and hover/scrub stop-frame thumbnails on the progress bar (cached preview frames), alongside the refreshed marketing art and control dock.
- SelectField marketing art — mobile bottom-sheet screenshot (
art/drawer-mobile.webp) in README Screenshots and the SelectField Help Center page.
Changed
- JS deps —
@tiptap/core+@tiptap/extension-youtube3.31.0;esbuild0.28.2;libphonenumber-js1.13.11;@internationalized/date3.12.3.
Fixed
- SelectField / UserSelect mobile sheets — smoother bottom-sheet open on phones: enter animation stays transform-only, Livewire option fetch and focus wait until after the sheet finishes opening, and the UserSelect mixin loads with the combobox factory so option paint no longer races a missing
renderUserOptionHtml. - SelectField / UserSelect resize while open — desktop→mobile→desktop no longer leaves the dropdown pinned at
left: 0(detached over the sidebar); live breakpoint flips the overlay between sheet and panel, re-anchors under the trigger, and syncs body scroll lock / sheet backdrop with the drawer. - SelectField init with dynamic search —
serverSideFilterFnstub keeps engine create safe while the Livewire mixin loads in the background (no Alpine init serialization across the playground). - Asset injector stale registry — rebuilding JS refreshes
asset-registry.jsonwhenalpine-manifest.jsonis newer, so the injector no longer modulepreloads orphaned select-field chunk hashes (duplicate loads / main-thread jank on the playground). - Asset injector URL identity — cache-bust
?v=is ignored when deduping modulepreloads/stylesheets, so the same chunk is not injected twice under two versions. - UserSelect loading skeleton — single-container shimmer instead of per-row gradient pulses, so the sheet feels lighter while options load.
- Multi UserSelect trigger — long selected-name lists truncate with an ellipsis inside the bordered control; the chevron stays in place instead of painting past the field edge.
- IconPickerField trigger — selected icon and icon name share the same vertical center in the closed trigger.
- IconPickerField mobile sheet — no more cell/icon size jumps while the bottom sheet opens: sheet keeps full viewport width (no 22rem pin), grid geometry waits until enter settles, and locked virtual strides ignore tiny width thrash from sheet height fits.
- IconPickerField loading skeleton — rebuilt: phase-driven bones (full-cell pulse), fetch no longer waits on grid geometry, icon track is not gated on
resultsGeometryReady(that deadlock left the panel empty forever). - IconPickerField resize / breakpoint — open panel flips panel↔sheet on window resize: width pins run after mode chrome, stuck sheet-enter no longer blocks flips, and the icon grid remasures (the old resize wrapper never rebound
window). - Overlay breakpoint — sheet vs panel follows viewport width only (
≤768); coarse pointer alone no longer locks sheet on wide screens, so mobile→desktop restore works. - CI Pest bootstrap —
phpunit.xmlno longer hard-codes monorepo../../vendor/autoload.php;tests/bootstrap.phploads packagevendor/(GitHub Actions) or the app vendor when developing inside a monorepo.
Flex Fields 3.1 ships two major interactive fields, closes the admin-to-runtime loop from 3.0, refreshes Phone/Video marketing art, speeds up heavy fields, and polishes the playground and release process.
New
- BubbleChoiceField — pannable bubble multi-select with center magnification, fringe shrink on pan, optional icons/images, min/max selection, and scalloped selection morph (radial geometry + SVG stroke). Studio/
FieldType::BubbleChoice, playground hub, and Help Center page. - TodoListField — enterprise animated checklist: check/strike motion, spark lines, celebrations (fireworks and custom hooks), sub-stacks, undo toast, create/edit/delete, reorder, search, virtualize, and infinite scroll. Studio/
FieldType::TodoList, playground hub, and Help Center page. - Sections end-to-end — layout sections defined in field groups render in Filament forms via
flexFieldFormLayout(). Import, export, publish, and rollback preserve sections and fieldsection_idassignments. - Section visibility at runtime — section
visibleWhenrules apply in forms, table column order follows section sort with section-prefixed labels, and infolist entries group by section. - Visibility builder v2 — field pickers for required/disabled rules and model-attribute sources in the field-group editor; section visibility rules in admin.
- Formula field in admin — save calculated expressions with cycle detection before publish.
- CSV value exchange — export a values CSV template and validate imports from the field-group editor.
- Playground categories — Guides, Navigation, Buttons, Pickers, Date and Time, Colors, Controls, Collections, Text Input, Data Display, Media, and Location group the hubs; related-hub cross-links between composition demos; SelectField hub demos
allowCreateOption()(smart suggest create row). - Package CI workflow — Pest, PHPStan, component audit, and bundle budgets in GitHub Actions.
- Help Center pages — field intelligence, schema conditions, and admin columns documentation; BubbleChoice and TodoList docs with screenshots.
Improved
- PhoneField & VideoField — updated marketing screenshots (
art/phone-field.webp,art/videofield.webp); README and docs point at the new WebP art (legacysc-23/sc-15refreshed in parallel). - README & art — BubbleChoice and TodoList featured first; package art screenshots prefer WebP; component count updated to 72 (60 form + 9 layout + 3 table).
- SelectField and UserSelect — lazy Alpine mount markers; select-field.css gzip budget met (≤12 KB); critical preload of overlay-runtime and teleported-menu when picker CSS is queued; explicit JS semantic chunks for theme-utils, combobox-engine, and search-normalize; playground CSS loads shared base + slim per-hub bundles (no duplicated toolbar chrome).
- Asset preloads — transitive barcode chunk preloading; background preloads respect save-data and reduced-motion preferences.
- Encrypted fields — masked display in table and infolist builders; persist/decrypt round-trip on models using
HasFlexFields. - Schema export — full JSON file download instead of truncated notifications.
- Flex Field Studio — registry publish version/state on group cards; entity-discovery empty-state hint and config cookbook.
- Blueprint packs — CRM and support starters ship with layout sections.
- Quality gates —
audit:componentsin release checklist; plugin navigation readsschema.navigation_*config keys; PHPStan burn-down on RichEditor, FileUpload, Slug, and Select paths.
Fixed
- Schema import validation — rejects unknown section IDs and circular formula dependencies.
- Schema export stream — download callback echoes JSON payload correctly.
- Playground docs — corrected hold-confirm, voice-note, and compliance-pack links; added focus-outline documentation; removed stale NEW badges on v3 hubs.
- Docs art paths — Help Center field pages use
/art/*.webpinstead of outdated PNGs.
Flex Fields 3.0 is a major release: manage custom fields from the admin panel, a faster form experience, and enterprise-ready uploads and signatures.
New
- Field groups in Filament — create and edit dynamic field sets for your models without hand-editing JSON. Pick the target model, field type, labels, visibility rules, and per-type options from visual controls. Optional database storage and version history when migrations are enabled (Flex Field Groups).
- Complete per-type settings — every supported field type exposes its options in the field-group editor (toggles, selects, option lists, matrix rows and columns). No raw config JSON for everyday setup.
- Calculated fields — formulas that update other fields automatically (totals, margins, progress, currency-aware math). Invalid or circular formulas are blocked before save.
- Conditional fields — show, hide, or require fields based on values elsewhere in the form.
- Ready-made schema blueprints — starter packs for CRM, HR, booking, inventory, support, and onboarding.
- Image choice cards — users pick from image cards (single or multiple choice) with layout, sizing, and selection limits.
- Redesigned SelectField — built for large lists and daily use: instant search, inline typing in the trigger, grouped options, disabled choices, paginated remote results, custom option layouts, recent and suggested values, inline create-new-option, and @-mentions for linked records. Multi-select chips wrap cleanly on small screens.
- Mobile-friendly pickers — country, phone, timezone, currency, address, tags, and similar fields open as bottom sheets on phones and tablets, with consistent keyboard support.
- Schedule and date/time — weekly schedules with overnight slots, copy-to-weekdays, overlap validation, locale-aware calendars, and blocked dates.
- Secure media and signatures — optional virus scanning and quarantine for uploads; Spatie Media Library integration; voice-note and barcode capture metadata; credit-card fields that never store full card numbers when tokenization is required; signature fields with legal timestamp seals, audit metadata, ink validation, and optional download (SVG or WebP).
- Translatable fields — improved locale tabs for many languages, correct text direction per locale on the input, and smoother RTL behaviour.
- Rich admin columns — table columns for progress, status chips, signature previews, and map pins, sized to match your panel density.
- Upgrade guide — current update workflow for teams moving from Flex Fields 2.x.
Improved
- CurrencyField — clearer amount display, stable caret while typing, and more natural decimal entry.
- NumberStepper and PriceRangeField — snappier number animations; price range sliders and inputs respect your chosen corner radius and large size tokens.
- Formula-driven forms — calculated values refresh reliably across related fields in the same form.
- Global density — compact, comfortable, and spacious panel density scales control heights consistently across fields.
- Fresh installs — v3 behaviour (modern select engine, formulas, enterprise media defaults) is enabled out of the box; run
php artisan fff:v3:upgradewhen upgrading an existing app.
Fixed
- SignatureField — legal signature packs save correctly with timestamp metadata; validation requires visible ink before accepting a seal.
- SelectField — large option lists load quickly; clearing a selection no longer restores the old value; inline search and RTL layouts behave predictably on first load and after Livewire updates.
- Date and time fields — no flash of empty segments while the form loads; timezone labels appear immediately.
- Translatable and slug fields — long locale tab bars scroll smoothly without breaking layout; fused title/slug locale tabs align correctly.
- Text inputs — caret stays at the end of prefilled values after autofocus.
- PriceRangeField — focus ring and sizing match other large inputs; histogram and inputs follow your rounding settings.
- Segment controls — overflow tabs position correctly on first paint and inside modals.
Added
- COMMERCIAL.md — public commercial plans (Single Product $169, Unlimited $299, Custom kit/OEM from $1,500), decision diagram, OEM vs in-product comparison, starter/boilerplate clarification, and Lemon Squeezy checkout.- NumberStepper / FlexSlider / PriceRangeField
locale()— opt-in locale-aware display (pt_BR→1.234,50). Defaultnullkeeps historical plain.decimals with no grouping (PHP + JS). (#47)
Changed
- LICENSE §5 — points to
COMMERCIAL.mdfor published plans/pricing; clarifies custom OEM / redistribution instruments. - README — license footer links to
LICENSEandCOMMERCIAL.mdwithout embedding prices. - Docs index — Links section includes License and Commercial plans.
- JS deps —
number-flow0.6.2;@tiptap/core+@tiptap/extension-youtube3.28.0;tailwindcss+@tailwindcss/cli4.3.3 (#28–#31, #27).
Fixed
- Client-side option search — timezone, currency, dual-listbox, and tags suggestion filters now use the same accent-insensitive normalizer as the country picker (
sao paulomatchesSão Paulo). Tag duplicate detection is unchanged. (#46) - SelectField multiple chips — selected badges wrap and the trigger grows in height (instead of a single clipped row). Chip spacing/contrast polished; after ~6 rows the chip area scrolls via
--fff-select-multiple-max-h. Chips scale with fieldsize()(sm/md/lg). (#45) - SelectField grid trigger / checkmark — grid trigger uses the same tighter start padding as multi chips; portaled grid selected-check badge keeps a solid circular background.
- SelectField
keepSelectedOptionsInDropdown()— checklist-style multi-select: selected options stay in the dropdown with checkmarks (menu stays open). Default multi-select still removes chosen values from the list (chip/tag pick-from-remaining). Playground adds Multiple states (checklist) next to the genres wrap demo. - SelectField selection UX JS — check enter/exit + checklist keep-selected extracted to a dedicated hot-path module (
select-field-selection-ux.js): template-cloned SVG, cached reduced-motion query, cancelable RAF/transition tokens, and nogetComputedStylespam on every dropdown sync (token cache + one-shot glass chrome). - SelectField / teleported menu runtime — close↔reopen race guard, real patch teardown (portal restore, overlay unbind, grid resize, inline-search click, document-click restore), theme-fingerprint token/glass caches, freeze Filament scroll/resize while closing, rAF-coalesced menu reposition on scroll,
applyTeleportedMenuThemeskip-if-unchanged, scrollbar-edge inset only when list overflows, estimated height when panel measures0on first paint. - Teleported searchable menus (phone / country / timezone / currency) — scrolling inside the menu no longer re-runs the open reveal animation (
revealonly on open); capture-phase scroll events from inside the menu are ignored so the panel does not flicker/jump.
Tests
- SelectField CSS coverage asserting multi-select badge wrap (
flex-wrap) and auto-height trigger (no fixedmax-heightlock to--fff-select-min-h). - SelectField dropdown shell tokens (fixed
1.5remmenu radius,calc(.25rem * 1.5)padding, list-box option sizing, near-opaque light menu bg).
Fixed
- ProgressBar (segmented titles) —
overflow-wrapon.fff-progress-bar__segment-title/ label columns changed fromanywheretobreak-word, so narrow multi-segment trackers no longer split words mid-token (e.g.Anagrafic a) or squeeze neighbouring columns via min-content. (#44)
Changed
- LICENSE v1.1 — clarified Permitted Free Use for private app repositories and
filament:assetsoutput (§1.5(iii), §2.2), application-level CSS/theme overrides (§2.3), local vendor patches kept only for your own install (§2.4), and audit confirmation by email (§10.8). Redistribution / publishing of modified copies remains restricted (§3.1 / §3.2). (#44) - README — added a short “What this license means for you” table for faster adoption review.
Tests
- ProgressBar unit coverage asserting segmented title CSS uses
break-word(notanywhere).
Fixed
- TitleSlugField + Spatie Sluggable — live slug preview no longer freezes on the first character while typing the title. Previously, each keystroke re-hydrated the current form
slugonto the Spatie model;GenerateSlugActionthen treated it as a manual customization (hasCustomSlugBeenUsed) and kept that first letter. Preview generation now excludes the slug attribute from form hydration so Spatie regenerates from the title source on every update. (#43)
Changed
- Docs (SlugField & TitleSlugField) — finished English pass on remaining Polish copy in
docs/slugfield-and-titleslugfield.md(layout, install, config, Spatie cookbook sections).
Tests
- Enterprise regression matrix for GitHub #43: GenreResource
name→slug+spatieModellive typing, multi-word titles, Polish diacritics, Playground without Spatie, renamedfieldSlugworkaround, incorrectgenerateSlugsFrom('slug')workaround, sibling Spatie sources, Regenerate path,preventOverwrite/skipGenerateWhenwith stale form slug ignored.
Fixed
- Dark mode CSS (
core.css/ component bundles) —dark:*utilities were compiling to Tailwind’s default@media (prefers-color-scheme: dark)variant instead of Filament’s class-based strategy. That leaked styles such asdark:text-whiteinto light-mode Filament panels whenever the OS preferred dark. All Tailwind entry points (core.css,playground.css,utilities-baseline.css) now declare@variant dark (&:where(.dark, .dark *));, matching Filament. (#41) - NumberStepper +
->live(debounce: …)— Filament’s$entangle()binding silently drops debounce and becomes live-immediate, so every +/- click fired a Livewire request and lagged the page. When a debounce is configured, the stepper now entangles non-live and commits via a debounced$wire.set(..., true)in Alpine so rapid clicks collapse into one request. (#40) - VideoField (Safari) — the dock’s masked
backdrop-filterblur no longer cold-starts on hover (opacity-only fade + GPU layer promotion;visibilitytoggle removed). MP4 sources without a poster also paint a first frame underpreload="metadata"instead of staying black until play. - IconPickerField — clearing the selection then reopening the panel no longer opens-and-immediately-closes. Deferred
$entangle(null)used to ride along with the first search request, remorph the shell, and fliplazy-alpine-mountfrom eager → lazy mid-open.wire:ignorenow wraps the mount shell so Alpine survives that round-trip. (#36) - CoverCard height — short
aspect-ratiovalues no longer clip footer actions underoverflow: hidden. Ratio is applied on a::beforegrid sizer; the content layer can grow the shared row taller than the preferred ratio. (#42) - CoverCard —
extraAttributes(['style' => …])now merges with the card’s own aspect-ratio CSS variable /max-widthinto a singlestyleattribute. Previously a secondstyle=""was emitted (invalid HTML), so browsers dropped either the custom style or the aspect ratio.
Tests
- Regression coverage for class-based dark CSS compilation, NumberStepper live-debounce binding HTML/Alpine commit path, IconPicker mount-shell stability after clear, CoverCard style merge + min-content height, and related VideoField assets.
Fixed
- Icon Picker Field — fixed a
__PHP_Incomplete_Classerror occurring upon cache unserialization by refactoringIconCatalogResolverto cache native arrays instead of class objects, bypassing Laravel’s strict cache object security restrictions.
Fixed
- Progress Circle — fixed SVG
stroke-dasharrayrendering issue in Firefox by converting unitlesscalc()ratios to lengths (* 1px). - Credit Card — fixed 3D flip animation in Firefox where the front face would bleed through the back of the card due to an
overflow: hidden+backface-visibilityrendering bug, by applying explicit Z-translation to faces. - Rating Column — fixed issue where SVG stars were stripped by Filament’s
HtmlSanitizerwhen->html()was used;RatingColumnnow renders usingHtmlStringto bypass sanitization correctly.
Fixed
- ItemCard CSS bundle — base
ItemCard/ItemCardGroup/ItemCardStackstyles (and productionfff-form-layouthelpers) lived in the lazyswitchstylesheet, so pages that render ItemCards without a Switch looked unstyled. Those rules now ship inflex-fields-item-card.css. - Asset injector (SPA navigation) — lazy CSS/JS could fail to re-apply after Livewire/Filament SPA navigation (
wire:navigate, resource sub-navigation).dedupeDocumentAssets()no longer treats the first injected<link>as a duplicate, loaded-asset caches are verified against the live DOM, andlivewire:navigatedresyncs from the document before loading only missing component assets. - Asset injector (boot / navigate race) — Livewire fires
livewire:navigatedshortly after first paint; ifboot()had already consumeddata-fff-asset-batchmarkers, page retain could stay empty and a later modal close would uninstall still-needed page CSS. The injector now restores page retain when navigation finds no remaining batch markers. - Asset injector (server emit protection) — ownership uninstall only removes injector-created
<link>tags. Server-emitteddata-fff-stylesheet/data-fff-alpine-chunklinks are never torn down by modal close or retain races. - Asset injector (double boot) — the injector script is registered
loadedOnRequestand rendered once viaSCRIPTS_AFTER, with a runtime singleton guard so event listeners are not duplicated.
Changed
- Asset injector (page / modal ownership) — CSS and Alpine chunks are retained per page vs per open modal. Modals load only missing assets (no duplicate
<link>tags). Closing a modal uninstalls assets that nothing still retains, while assets still required by the page stay loaded. - Asset injector (nested modals) — stacked Filament action modals use a LIFO ownership stack so a parent modal’s assets survive while a child modal is open and are cleaned up only when that parent actually closes.
- Package Health — Dependabot now sets an explicit
cooldown(≥3 days) for Composer and npm version updates (Plumb package health requirement).
Tests
- Expanded asset-injector JavaScript coverage for ownership, nested modals, SPA navigation, chunk lifecycle, and race conditions.
Fixed
- Package Health — removed
composer.lockfrom git tracking and added.gitattributesexport-ignoreto improve Packagist distribution archive health. - Package Health — added
SECURITY.mdpolicy. - Package Health — configured
.github/dependabot.ymlfor Composer and NPM to ensure dependencies are monitored.
Changed
- Documentation — updated all documentation links in the README to point to absolute Mintlify URLs (
https://flex-fields.bjanczak.com/docs/...).
Fixed
- Lazy asset emission (
emit-assets) — fixed a regression where queued component stylesheets (e.g.UserColumn,RatingColumn,IconColumn) were not applied on full Filament page loads. The partial previously used@push('styles')for non-Livewire requests, but Filament renders@stack('styles')before theSTYLES_AFTERhook, so pushed links never reached the document head and CSS relied solely on the JS asset injector. - Lazy asset emission (
emit-assets) — queued lazy stylesheets and Alpine chunk preloads are now always emitted as inline<link>tags (withdata-fff-stylesheet/data-fff-alpine-chunk) while preserving the existingdata-fff-asset-batchpayload forflex-field-asset-injector.js(Livewire morph, SPA navigation, modals). Per-component lazy loading, dependency resolution, deduplication, andmarkStylesheetsEmitted()behaviour are unchanged.
Fixed
- FlexRichEditor — fixed a critical bug in Native Filament’s
rich-editor.jswhere custom JS extensions (like the YouTube embed) triggered aTypeError: Y[F.name] is not a functionduring Livewire event dispatch due to missing references in the minified loop.runEditorCommandsis now safely overridden directly inflex-rich-editor.js. - FlexRichEditor — added
try/catchblock aroundsetEditorSelectionto preventRangeErrorfrom halting command execution when the Livewire server state syncs and trims the document, shifting the cursor out of bounds before inserting elements. - FlexRichEditor — fixed a race condition during initialization where Native Filament’s formatting buttons (Bold, Italic, etc.) failed to execute because the
$getEditormethod proxy was not correctly mapped to the Alpine component instance.
Added
CalculatorField— numeric input with a shared iOS-style calculator panel (one panel per page, per-field session memory).- Input: direct typing or Insert from the keypad; built on FlexTextInput styling (
primary,secondary,flat,softvariants;sm/md/lg). - Numeric API:
minValue(),maxValue(),step(),integer(),decimalPlaces(),maxLength(),roundingMode()(round,ceil,floor,truncate— defaulttruncate). - Panel: floating glass panel on desktop (draggable); bottom sheet on mobile; iOS keypad layout (
AC,±,%, operators); live preview without field rounding until Insert. - JS core:
calculator-engine,calculator-coordinator,calculator-panel-behavior,numeric-field-normalizer. - Playground slug
calculator-field; full docs indocs/calculator-field.md.
- Input: direct typing or Insert from the keypad; built on FlexTextInput styling (
- Tests —
CalculatorFieldTest,CalculatorPanelMountTest,CalculatorFieldRenderTest, JS unit tests (calculator-engine,calculator-coordinator,calculator-keypad-icons,numeric-field-normalizer), E2Eplayground-calculator-field.spec.mjs.
Changed
- README — CalculatorField gallery entry (
art/sc-34.png); component count 70; Number & range table updated. - Docs —
docs/calculator-field.mdregistered indocs.jsonanddocs/index.md.
Added
NpsField— enterprise survey scale input for NPS (0–10), CSAT, Likert, and custom option sets.- Variants:
pills(sliding segment control),segments(full-width bordered bar),emojis(icon or image rings with labels). - API:
options(),variant(),colorCoded(),colors(),textColors(),icons(),emojiImages(),disabledOptions(),minLabel(),maxLabel(),size(),rounding(). - State: defaults to
null(empty initial selection on all variants); optional fields deselect when the active option is clicked again. - Assets: bundled mood emoji webp (
resources/dist/assets/nps-field/emojis/) published topublic/filament-flex-fields-assets/viaphp artisan filament:assets. - Playground slug
nps-field; full docs indocs/nps-field.md.
- Variants:
CREDITS.md— third-party attributions (Microsoft Fluent UI Emoji, Gravity UI Icons); linked from README.FlexFieldStaticAssetsTest— coverage for static media publishing andFlexFieldAssets::assetUrl().FlexFieldAssets::publishStaticAssetsIfStale()— incremental sync of bundled media; sharedpublishStalePackageAssets()helper for Filament + static assets.
Changed
- Static asset pipeline — media under
resources/dist/assets/copies file-by-file only when the package source is newer (instead of full directory copy on every boot). - README — NpsField gallery entry (
art/sc-33.png); component count 69;CREDITS.mdlink in license footer. - Component documentation — expanded reference pages under
docs/for all form fields, columns, and layout components.
Fixed
- NpsField (pills) — sliding indicator no longer animates from
(0, 0)on first selection from an empty state. - NpsField (segments) — improved dark mode contrast for borders and unselected labels.
Added
FlexMatrixTable— new advanced grid component based onMatrixChoiceField. Allows defining arbitrary Filament components in theschema()instead of just radio/checkbox fields. You can now injectSelectField,FlexTextInput,SwitchField, etc., directly into matrix rows.- Matrix table column widths — added
columnWidths([])method to dynamically control CSS grid template columns for matrix-based fields, allowing elements like inline switches to perfectly fitmax-contentand align to the right. - Matrix table playground — added
FlexMatrixTablePlaygroundwithflex_matrix_table__basicandflex_matrix_table__specsexamples, demonstrating dynamic suffix injection, component size overrides, and column width control.
Fixed
- SelectField — updated the
getOptionLabelsmethod to maintain forward compatibility with Filament 5.x beta rendering optimizations while remaining completely backward compatible with stable releases, resolving PHPStan signature mismatch errors (#9). - PhoneField — changed national phone number formatting to properly use
libphonenumber’s native formatter (PhoneNumberFormat::NATIONAL) instead of raw string casting, preserving leading zeros (e.g. for Italian numbers) and correct local spacing (#9).
Fixed
- BarcodeScannerField — removed
isMobileCameraDevice()check to allow supported mobile browsers (like Chrome on Android) to use the nativeBarcodeDetectorAPI instead of falling back to the ZXing engine (#7). - Asset Build Script — fixed an issue in
build-component-js.mjswhere the JS build script incorrectly deleted transitive chunk dependencies as “orphans”, which caused the ZXing fallback engine to fail with a 404 error when attempting to load its dynamic chunks (#7).
Fixed
- FlexFileUpload / FlexImageUpload — fixed an issue where
optimizeImagesToWebp()was disabled by default when called without arguments (#6). - FlexFileUpload / FlexImageUpload — added a developer warning and updated documentation to clarify that
pruneOrphanedOnSave()should never be used with a staticdirectory()to prevent accidental data loss in shared folders (#6).
Fixed
- FlexFileUpload / FlexImageUpload — fixed an issue where uploaded files were left in the Livewire temporary directory and not moved to the final disk because Filament’s
saveUploadedFilesandhydrateFileslifecycle hooks were accidentally overwritten by the package’s internal file storage hooks (#5).
Fixed
- AddressAutocompleteField — fixed hardcoded Mapbox language to properly fallback to
FLEX_FIELDS_MAPBOX_LANGUAGE(#4). - SegmentControl — server-rendered
data-segment-selected/aria-checkedfrom$getState()before Alpinex-loadhydrates; CSS pre-hydration pill on the selected segment (:not(.is-hydrated)) so the white indicator is visible duringx-loadinstead of appearing ~0.3s later. - UserSelect / SelectField —
lazy-alpine-mounteagerwhen SSR trigger is shown;modulepreloadforselect-fieldAlpine chunk; SSR layer usesdisplay: noneonce replaced (wasvisibility: hidden, caused double height in grid). - IconPickerField — SSR SVG preview + label before
x-load;lazy-alpine-mounteagerwhen a value is selected;modulepreloadforicon-picker-fieldchunk;is-trigger-hydratedhandoff (replacesx-showon preview).
Changed
- FlexFileUpload / FlexImageUpload upload source tabs — default segment style is now
default(filled gray track) instead ofghost+primaryaccent; useuploadSourceTabsVariant('ghost')anduploadSourceTabsColor('primary')for the previous look. - FlexFileUpload dropzone icons — empty-state SVG icons use
--fff-flex-file-upload-accent(--primary-500) via CSS masks instead of hardcoded indigo#6366f1. - FlexFileUpload compact file list — remove button defaults to the right; with uploaded files, compact layout switches to stacked flow (gap below dropzone + between file cards) instead of broken absolute positioning.
- VoiceNoteRecorderField — red microphone record button no longer uses box-shadow.
- Conditional critical preload —
criticalPreloadStylesheets()emitsteleported-menuonly whenFlexFieldStylesheetQueue::hasQueuedTeleportedMenu(); empty queue on non-playground pages skips HEAD preloads. - Hold confirm assets — Alpine
modulepreloadmoved to per-actionhold-confirm.blade.php(@push); removed globalhold-confirm-action-preloadpartial. - Lazy Alpine mount — reusable
<x-filament-flex-fields::lazy-alpine-mount>withx-intersectfor heavy fields (date/time, file upload, select/user-select, barcode scanner, icon picker). - flex-date-time-field — calendar grid extracted to
calendar-panel.jswith dynamicimport()on first calendar open. - select-field.js — trigger-label helpers and patch boot logic split into
components/select-field/modules. - UserSelect — thin Filament class (46 lines) + traits
ConfiguresUserSelectColumns,ConfiguresUserSelectModel,InteractsWithUserSelectPresentation,HasUserSelectCollaborators; services inUserSelect/*. - flex-file-upload
pond-bridge— split intopond-component-resolver,pond-upload-state,pond-server-sync,pond-file-status(entry 21 lines). - CSS entries — all 54 lazy bundles use
utilities-baseline.css+base.css(was Tailwind×54 duplicate imports). - InteractsWithFlexFileUpload — split into
FlexFileUploadSecurity,FlexFileUploadStorage,FlexFileUploadDisplaytraits; composer trait ~25 lines. - flex-date-time-field.js — further split with lazy
time-panel.js; main entry ~476 lines. - PHPStan —
Translations::get()for type-safe__(); baseline regenerated (~385 entries);composer analysein CI;scripts/prune-phpstan-baseline.mjsfor stale-entry cleanup. - CI E2E — always-on unified fixture smoke (
field-smoke: select coordinator + file-upload + schedule). - Docs —
COMPONENTS.md,README.md,DEVELOPMENT.md§3/§20,docs/shared-concepts.md,docs/hold-confirm-action.mdsynced with conditional preload, hold-confirm per-action, alpine entry-only, lazy mount.
Added
-
Support/Translations.php— typed translation helper for PHPStan level 8. -
resources/css/utilities-baseline.css— shared@theme/@utilitybaseline; imported by all 54 lazy CSS entries. -
tests/js/flex-file-upload.test.mjs— unit tests for file-upload module factories. -
tests/e2e/field-smoke.{spec.mjs,html,harness.mjs}— fixture smoke without playground URL. -
scripts/audit-components.mjs+npm run audit:components— per-component matrix (blade ↔ lazy CSS ↔ dist ↔ Alpine manifest ↔ PHP/JS line budgets); exits non-zero on critical asset gaps. -
FlexFieldAlpineManifestArchTest— guards LAZY stylesheet ↔ alpine manifest ↔ dist entry alignment. -
docs/COMPATIBILITY.md— Filament ^5.0, PHP 8.3+, CI link. -
scripts/compare-bundle-metrics.mjs— prints gzip deltas vs committedbundle-metrics.json.
Added
IconPickerField— Searchable blade-icons picker with SelectField-style teleported dropdown, lazy SVG previews, paginated server search, set-filter pills, whitelist/exclude controls, and virtual scrolling for large catalogs. Stores the full icon name (heroicon-o-star,gravityui-star, etc.).IconColumn— Read-only table column for blade-icons values stored byIconPickerField; optional label, color, size, and per-request render cache. Playground demo anddocs/iconcolumn.md.- Icon catalog pipeline —
IconCatalogResolver,IconCatalogIndex, and ranked search with server-side caching; optional bundled manifest viaphp artisan fff:icons:manifestfor cold-start performance. - Icon picker configuration —
sets(),icons(),excludeIcons(),searchResultsLayout()(grid/list/icons),gridColumns(),closeOnSelect(),preload(),limitPerSet(),perPage(), plus sharedSelectFieldshell options (variant,size,clearable, affixes). - Icon picker performance — viewport SVG batching (
IntersectionObserver), client search LRU cache, next-page prefetch, and a DOM virtual window for long result sets. - Icon picker UX — combobox keyboard navigation, debounced search with highlight, horizontal scroll for set pills, full-width results divider, and skeleton states while icons load.
FieldType::IconPicker— FlexField form-builder support viaIconPickerFieldConfigurator.- Playground —
icon-picker-fieldpage with Heroicons-only, Gravity-only, multi-set pill filter, grid/whitelist, size, variant, and clearable demos. - Documentation — docs/icon-picker-field.md (full API, setup, config, performance); navigation entry under Choice & Selection in docs.json and docs/index.md.
- Tests — PHP unit/feature tests (
IconPickerFieldTest,IconCatalogResolverTest,IconPickerFieldRenderTest, SVG batch limits); JS tests for virtual window, SVG loader, search cache, keyboard highlight, and panel lifecycle.
Changed
- Playground bundle dedupe —
FlexFieldStylesheetQueue::suppressForPlaygroundBundle()prevents duplicate lazy CSS when a per-slug playground bundle already includes component stylesheets. - Critical preload scoping —
criticalPreloadStylesheets()skips global preloads on bundled playground slugs and filters preloads to stylesheets the active slug actually uses (fixes unusedflex-text-inputpreload warnings on icon-picker pages). - Icon picker panel lifecycle — reopen/refresh no longer calls Alpine
$watchcleanup (magic$watchdoes not return an unwatcher); pending-flag +panelReadywatcher handles scroll/SVG resync safely.
Fixed
- Icon picker reopen — empty or frozen results after close/reopen; scroll position preserved on pagination (
preserveScroll); virtual window getters stay in sync during scroll. - Duplicate icon-picker CSS on playground — bundle + lazy
icon-picker-field.cssno longer both apply conflicting.fff-icon-picker__toolbarrules. - Icon picker set pills — symmetric spacing above/below pills; full-width separator; single-row horizontal scroll for many sets.
Added
- Asset Injector V2 (SPA/Modal Asset Pipeline) — Re-architected lazy CSS and Alpine chunk loading. Logic extracted into a dedicated, testable
flex-field-asset-injector.jsmodule, built toresources/dist/viascripts/build-asset-injector-js.mjs, and registered as a Filament JS asset. - Shared asset queue trait —
FlexFieldAssetQueuecentralizesenqueued/emitted/pending()bookkeeping forFlexFieldStylesheetQueueandFlexFieldAlpineQueue. - Critical stylesheet preloads —
critical-stylesheet-preloadspartial preloads high-priority flex-field CSS atHEAD_ENDto shorten first-paint latency. - FOUC prevention in modals —
Livewire.hook('morph.updating')scans unattached morph fragments (toEl) fordata-fff-asset-batchmarkers (stylesheets and Alpine chunks), then appliesfff-flex-fields-assets-pendingto the live DOM target (elor its.fi-modalancestor) before morph completes. While assets load, modals show a skeleton shimmer over.fi-modal-content(shell and backdrop stay visible) instead of hiding the entire window. - Background asset preloading —
preloadBatchesIn()warms lazy CSS/JS from hiddendata-fff-asset-batchmarkers without removing them. Preload runs onrequestIdleCallbackafter page load / SPA navigation and on debounced hover over action triggers (button,a,[wire:click]), so modal fields are often styled before the user clicks. - Alpine chunk loading in morph batches — Asset batches now carry
data-fff-chunks; the injector loads missingmodulepreloadchunks alongside stylesheets. - In-flight promise cache —
inflightRequestsMap deduplicates concurrent downloads; parallel Livewire requests for the same asset share one network request. - URL normalization — Native
URLconstructor normalizes relative, absolute, and mismatched-protocol hrefs before deduplication and load checks. - Protected asset links — Stylesheets and chunks tagged with
data-fff-stylesheet,data-fff-alpine-chunk, ordata-fff-playground-bundleare never removed during injector dedupe. - Injector optimizations — O(1)
Mapindices for loaded stylesheets/chunks;link.sheetcheck before treating a DOM link as loaded;Promise.allSettledso 404s do not block remaining assets; dedupe removes duplicate injected links instead of re-appending them. - Playground page stylesheets —
playground-page-stylesheetspartial pushes per-slug playground bundles via@push('styles')with protecteddata-fff-playground-bundlemarkers. - JavaScript test suite — Unit tests in
tests/js/flex-field-asset-injector.test.mjscover URL normalization, in-flight dedupe, morphel/toElpending state, conditionalprepareModal, background preload, protected links, and failed-load recovery. - Webcam & URL file import — New auxiliary upload sources for
flex-file-upload, opt-in viaallowWebcamUpload()andallowUrlUpload(). Webcam capture supports front/back camera toggle and torch where available; URL import fetches remote images into nativeFileobjects injected into FilePond, preserving Filament image manipulation hooks andmultiple()support. Playground demos and docs/flexfileupload-and-fleximageupload.md updated. - PHP tests —
FlexFileUploadSourcesTestfeature coverage for webcam/URL sources, staged previews, SSRF rejection, and disabled-source guards.
Changed
load-stylesheetalways emits — Field registration immediately includesemit-assets(no longer deferred-only), so lazy CSS is not lost on full-page renders.emit-assetsdual emit path — Full-page requests push<link>tags via@push('styles'); Livewire partial updates emit inline<link>/modulepreloadtags for the injector to process.queued-stylesheetsinjector — Flushespending()stylesheet and chunk queues atSTYLES_AFTERandBODY_ENDhook points.- Morph pending resolution —
beginPendingMorph({ el, toEl })scans batches ontoElbut resolves pending targets from liveel, keeping modal state attached to the real DOM node through morph. - Modal open pending is conditional —
prepareModal(x-modal-opened) applies skeleton pending only whenrootNeedsAssetLoading()is true; cached assets open instantly with no pending flash. - Teleported menu z-index in modals —
teleported-menu.cssraises dropdown stacking when a Filament modal is open (:has(.fi-modal.fi-modal-open)).
Fixed
- Global CSS regression after V2 — Lazy stylesheets load again on full-page Filament views; V2 had registered assets without emitting them until
BODY_END. - Playground missing CSS — Playground bundle links are protected from injector dedupe; per-slug stylesheets push into the layout
stylesstack. - Modal stuck hidden after morph — Pending state now tracks the live modal (
el) instead of detachedtoEl, somorph.updatedreliably releasesfff-flex-fields-assets-pending. - Teleported menu behind modal — Country/phone/timezone dropdowns render above open modals instead of underneath the overlay.
- Upload source panel layout gap — Inactive webcam/URL panels are removed from document flow (
position: absolute) so switching tabs does not leave empty vertical space above FilePond. - Recursive stylesheet dependency resolution — Depth-first traversal in
FlexFieldAssets::stylesheetsFor()resolves deeply nested CSS dependencies in correct bottom-up cascade order (e.g.schedule-field→timezone-field→flex-time-segments).
Security
- URL import SSRF guard — Remote file import rejects unsafe URLs (localhost, private IPs, metadata endpoints) before fetch, matching server-side scrape safety patterns.
Added
BarcodeScannerField— FlexTextInput shell with Filament modal scanner, manual entry fallback, multi-format support (QR, EAN, UPC, Code 128/39, ITF, PDF417, Data Matrix), torch toggle, continuous scanning, success sound, auto-submit, and EAN/UPC checksum validation.- Hybrid scan engine — native
BarcodeDetectorwhen supported (Chrome/Edge on desktop), automatic ZXing fallback elsewhere (including mobile); engine badge shown in the viewport. - Barcode configuration — chainable
formats(),continuous(),beepOnScan()(default on),autoSubmit(),cameraFacing(),preferredDeviceId(),allowCameraSwitch(),scanDelay(),scanInterval(),decodeFps(),pauseWhenHidden(),storeDetectedFormat(),allowManualInput(),scanButtonLabel(),modalHeading(),validateChecksum(), andbarcodeRule(). - Barcode scanner UX — toolbar under the preview with switch camera and torch; decode pauses while the browser tab is hidden (battery-friendly); optional structured state
{ value, format }from camera symbology. - Mobile camera pipeline — iOS/WebKit video portal (body-mounted preview + synced overlay) so
<video srcObject>renders inside the Filament modal; camera starts after the modal transition completes. - Scan feedback — rounded scan frame, animated scan line, success flash; bundled MP3 on desktop; transient Web Audio beep on mobile (no lock-screen media player).
- Lazy ZXing assets —
@zxing/browserloaded via dynamic import in a separate JS chunk; field CSS/JS lazy-loaded per instance. - Playground —
barcode-scanner-fieldpage (default, EAN-only, continuous, checksum, scan-only). - Documentation — docs/barcode-scanner-field.md; entries in README.md (Text & input), docs/index.md, and COMPONENTS.md.
- Tests — PHP unit/feature tests, JS unit tests for barcode validation and engine helpers, Playwright smoke test for barcode scanner playground.
- i18n — English and Polish strings under
barcode_scanner.*.
Changed
- Scanner modal — native
<x-filament::modal>(teleported, Filament transitions, close button); spacing below modal description before the preview. - Mobile camera switch — toggles
facingMode(environment↔user) instead of cycling opaquedeviceIdvalues (reliable front/back on iPhone). - Scan controls layout — switch camera and torch moved from viewport overlay to a toolbar below the video (Filament-style buttons, always clickable).
- Scan overlay — minimal Apple-style rounded frame and scan line; compact engine badge in the top-left corner.
Fixed
- iOS black camera preview — video preview no longer stays black inside the teleported Filament modal (body portal + overlay sync).
- Mobile switch / torch taps — controls no longer use
$dispatchfrom a detached portal (events never reached Alpine); directswitchCamera()/toggleTorch()handlers. - Layout on mobile — portal position resyncs after layout; camera switch no longer hides the toolbar or leaves a black gap above the preview; toolbar stays below the viewport and is not covered by the fixed video layer.
Quality, security, accessibility, and test hardening for LinkPreviewField and ScheduleField (both introduced in 2.4.5), plus new
SocialLinksField.Added
SocialLinksField— social profile link editor with platform picker (add-from-dropdown UX), one URL row per platform, built-in brand icons, per-platform hostname validation, and optional custom platforms (customPlatforms()with label, hosts, icon SVG).- Social links configuration —
platforms()whitelist,excludePlatforms(),maxLinks(), variants/sizes (primary,secondary,soft,flat,ghost;sm/md/lg),autoFormatUrls()(trim + prependhttps://on blur), and opt-inreorderable()(default off). - Social links validation — server rules via
SocialLinkValidator; client-side mirror blocks submit and syncs row errors to Livewire ($wire.addError); empty in-progress rows kept in state for validation, stripped ondehydrate(); legacy associative map state normalized on hydrate. - Social links SSR — server-rendered list/toolbar fallback to prevent layout jump before Alpine hydration; teleported platform menu with keyboard navigation and click-outside close.
- Playground —
social-links-fieldpage (default, limited platforms, excluded platforms, custom platforms, reorderable, empty). - Documentation — docs/social-links-field.md; entries in README.md (Text & input), docs/index.md, and COMPONENTS.md.
- Tests — PHP unit/feature tests for
SocialLinksField; JS unit tests forsocial-link-validationandsocial-links-field; Playwright smoke test for social links playground. - Link preview & schedule tests — comprehensive SSRF unit tests for
UrlMetaScraper; JS unit tests forlink-preview-field,url-meta-scrape,schedule-field, andschedule-validation; PHP↔JS contract fixtures for overlap/min/max validation; Livewire feature test (ScheduleFieldRenderTest); Playwright playground smoke tests for link preview and schedule field.
Changed
ScheduleFielddefault control size issm; timezone selector renders atmdindependent of field size.- Schedule toolbar — compact add-slot / add-break buttons; copy-to-weekdays uses a confirm dialog before overwriting weekday schedules.
flex-time-segments— teleported time picker menu styling;from/topickers constrain options so start ≤ end within each slot; time options exposerole="option"andaria-selected.- Link preview — image preload timeout (5s) aligned with scrape timeout constants;
@media (prefers-reduced-motion: reduce)disables card/shimmer transitions. - Documentation —
SocialLinksFieldlisted under Text & input (withLinkPreviewField), not Date & time; COMPONENTS.md TOC reordered accordingly.
Fixed
ScheduleField::required()—isEmptyState()now treats schedules with no enabled days containing slots as empty (normalization no longer bypasses required validation).- Schedule inline validation — client overlap/range errors block form submit and sync the first error to Livewire via
$wire.addError. - Link preview client scrape —
isScrapeCandidate()blocks localhost, loopback, private IPs, and metadata hostnames (matching server-side SSRF rules). - Prefixed URL display, dark-mode card colors, and horizontal skeleton width (carried from 2.4.5 polish).
SocialLinksFieldUI — uniform icon sizing in picker/list, dark-mode legibility on add trigger/button, circular remove/reorder buttons with default gray background, no layout jump on reload (SSR + hydration overlay).
Security
- SSRF — expanded
UrlMetaScraper::isScrapableUrl()test coverage for localhost, private/link-local IPs,.local/.internalhostnames, and cloud metadata endpoints; redirect targets re-validated on each hop. - Rate limit —
link_preview.rate_limit_per_minutedefault changed from0(disabled) to30per user per minute. - OG images —
normalizeImageUrl()rejectsdata:URIs; onlyhttp://andhttps://images are returned.
Added
-
ScheduleField— weekly schedule editor with day toggles, from/to time slots, add break, copy-to-weekdays, optional timezone selector, and overlap validation. -
Playground —
schedule-fieldpage with variants, sizes, weekdays-only, and split-shift demos. -
Documentation — docs/schedule-field.md and
ScheduleFieldsection in COMPONENTS.md. - Tests — PHP unit tests for validation, API, and config; JS unit tests for schedule overlap validation helpers.
-
LinkPreviewField— URL input with live Open Graph preview cards on the FlexTextInput shell (primary,secondary,soft,flat,ghostvariants; prefix/suffix affixes). -
Three preview layouts —
horizontal(compact row, square edge-to-edge thumb),vertical(narrow card), andcard(full-width social card, max 500px). -
Server-side URL meta scraper —
UrlMetaScraperwith SSRF-safe fetching (stream until</head>, redirect handling, server cache),GET /flex-fields/url-meta/scraperoute, and configurable cache TTL / rate limit. -
Client-side preview pipeline — debounced scrape, in-memory cache, in-flight deduplication, abort on type, skeleton shimmer, image preload, and minimum skeleton duration on page load (
previewMinSkeletonMs, default 500ms). -
Optional SSR initial preview —
resolveInitialPreviewOnServer()(defaulttrue) hydrates prefilled URLs on first render; disable to avoid server scrape on mount. -
showVisitLink()— toggles domain as link vs plain text; visit icon uses Gravitypaperclipby default (visitIcon()/visitLabel()overridable). -
Playground —
link-preview-fieldpage with stacked layout demos. -
Documentation — docs/link-preview-field.md and
LinkPreviewFieldsection in COMPONENTS.md. -
Tests — PHP unit/feature coverage (
LinkPreviewFieldTest,UrlMetaScraperTest,UrlMetaScrapeControllerTest) and JS unit tests forurl-meta-scrape.js(prefix resolution, skeleton timing, visibility helpers).
Changed
- Link preview stylesheets declare
flex-text-inputas a lazy dependency — input shell CSS is not duplicated in the preview bundle. - Horizontal preview cards use full available width up to 500px; thumb is 1:1 and flush to the card edge.
Fixed
- Prefixed URL fields no longer show duplicate
https://in the input (display value strips the prefix; state stores the full URL). - Card layout text colors in dark mode use FlexTextInput theme tokens instead of hardcoded light-mode fallbacks.
- Preview skeleton on horizontal / soft + prefix layouts stretches to the card’s max width instead of shrinking to content.
Added
- Mapbox server proxy — Laravel routes +
MapboxGeocodingClientkeep the Mapbox token server-side; JS uses cached proxy URLs (geocodeSearchUrl/geocodeReverseUrl). - Schema versioning —
FlexFieldSchema::CURRENT_VERSION,versionon schemas, andFlexFieldSchemaMigrator(upgrades legacyfield→fields,name→slug, etc.). - Flex field audit trail —
HasFlexFieldsrecords who/when/what on value changes to configurableflex_field_auditJSON column. SelectFieldJS extraction — ~1000-line inlinex-initmoved toresources/js/components/select-field.js, loaded viax-load.- JS unit tests —
theme-utils,geocoding-cache,geocoding-list-keyboardcoverage intests/js/. - Playwright E2E — optional playground geo tests (
tests/e2e/playground-geo.spec.mjs, setFLEX_FIELDS_PLAYGROUND_URL). docs/tags-field.md— full TagsField API reference.- Z-index CSS tokens (
--fff-z-dropdown, etc.) andtheme-utils.jsfor consistent dark-mode menu styling. - Geocoding keyboard mixin + client-side geocoding cache shared by map and address fields.
- Geocoding translations —
geocoding.failedandgeocoding.rate_limitedin bundledenandpllanguage files.
Changed
MapPickerField/AddressAutocompleteFieldblades documentwire:ignore+$entangle+wire:keyremount strategy. Current guidance: Tabs, modals & Livewire.- README compare table, differentiation summary, geo proxy & audit bullets, country registry mention, and CI test counts updated.
Added
searchTypes()onMapPickerFieldandAddressAutocompleteFieldto limit Mapbox geocoding results to specific place types (address,poi,place, etc.) or leave unset for all types.MapboxSearchTypeenum with supported Mapbox Geocoding API type values.- Shared exclusive-dropdown registry (
flex-dropdown-registry.js) with a{ isOpen(), close() }controller contract. - Alpine overlay store (
Alpine.store('fffOverlays')) andwireExclusiveFlexDropdown()helper that auto-wires each field’s open state and closes other menus on open. - Dedicated overlay-coordinator JS chunk, preloaded for
SelectFieldand searchable select menus. - JavaScript unit tests for the dropdown registry (
tests/js/flex-dropdown-registry.test.mjs).
Changed
- Unified exclusive-dropdown behavior across
PhoneField,CountryField,TimezoneField,CurrencyField,MapPickerField,AddressAutocompleteField,FlexColorPickerField,FlexDateTimeField,VideoField,FlexTextInput,FlexTextarea, andSelectField. searchable-select-menu.jsnow registers through the overlay coordinator instead of per-field manual hooks.SelectFieldblade usesAlpine.store('fffOverlays')instead of an inline event bus.- Emoji picker panel restyled toward emoji-mart layout: category tabs with custom SVG icons on top, search field below, 9-column emoji grid.
- Emoji picker category/search icons now use inline
@gravity-ui/iconsSVGs instead of CSS masks (fixes distorted tab icons). - Emoji picker category tab click no longer shows the library’s circular
:activeflash on top of the custom hover background. - Emoji picker skin tone menu no longer bleeds into the emoji grid after the search row was moved below category tabs.
- Emoji picker toggle button now closes on second click instead of closing and immediately reopening.
- Mapbox geocoding dropdown (
AddressAutocompleteField,MapPickerField) is now teleported tobodywith the same glass/backdrop-blur menu styling asSelectField, including improved dark-mode detection and stronger contrast over map tiles. - Mapbox geocoding dropdown no longer floats above the Filament topbar on scroll (
z-indexlowered from 80 to 20).
Removed
- Legacy
fff:flex-dropdown-openDOM event bus andwindow.__fffFlexDropdownCoordinator. - Per-instance
prepareExclusiveDropdownOpen()calls and DOM-scanning close logic.
Fixed
- Multiple dropdowns opening at once on the same page (e.g. phone country picker and
CountryFieldboth staying open) caused by a shared unbind function reused across Alpine instances. SwitchFieldtrack color staying primary after toggling OFF when the switch started in the ON state (Alpinex-bind:classstring merge kept stalefi-color-*classes).FlexTextareaFieldtoolbar actions (Attach,Send,More actions, etc.) flashing or disabling together on any submit — each action now gets a scopedlivewireTargetvia Filament’sgetLivewireClickHandler()instead of globalwire:loading.attr.- Livewire console error
Missing closing parenthesis for method "mountAction"on flex-textarea playground caused by manually builtwire:targetstrings with unescaped JSON in HTML attributes. - Flex Textarea emoji button icon disappearing on hover in dark mode — generic hover
colormatched the darkbg-zinc-600background.
Added
- Shared country registry delivered once per page via
<template id="fff-country-registry-data">with separateisoandphonepools. CountryRegistry,CountryRegistryQueue, and unit tests for compact registry payloads.- Lazy country list loading on first menu open instead of embedding full country arrays in every field instance.
createCountrySearchMixin()with debounced search, diacritic normalization, ranking, and result caching.- Keyboard navigation for country menus: Arrow Up/Down, Home, End, Enter,
aria-activedescendant,aria-posinset, andaria-setsize. - Focus trap inside open country/phone dropdown menus with focus restore on close.
- Virtual list v2 for long country lists: passive scroll,
requestAnimationFramebatching, threshold of 50 items, and top/bottom spacers. - Page-level deduplication of custom country filters via
countryFilterKeyin the shared registry payload. - Built-in translations —
enandplship inresources/lang/(default.php,countries.php,currencies.php,timezones.php) and load automatically via thefilament-flex-fields::namespace. - Publishable translations —
php artisan vendor:publish --tag=filament-flex-fields-translationscopies language files tolang/vendor/filament-flex-fields/for customization (optional; overrides merge on top of bundled strings). VERSIONfile and this changelog.
Changed
CountryFielduses theisoregistry pool;PhoneFielduses thephoneregistry pool.- Country and phone blades no longer embed
countries: @js($countries)in Alpinex-data. - Stylesheet loader now enqueues only the pool required by each field type (
isofor country,phonefor phone); both pools are still combined automatically when both field types appear on the same page. - Playground segment CSS is loaded on
livewire:navigatingonly; hover/focus preload was removed to avoid unused preload warnings in DevTools. - Country dropdown menu text colors in dark mode now use dedicated CSS tokens for readable contrast.
Fixed
- Empty country dropdown after registry refactor (Alpine getter init order, SPA navigation cache reset, and reliable inline
<template>delivery). - Virtual list total height freezing at
0when getters ran before filtered data existed.