Home
Wiki
Home
Wiki
  1. 3. Reusables
  • Back to home
  • 1. Themes
  • Vs Code
    • Getting Started
  • Kitchenware
    • Layout
      • New Layout
      • Legacy Layout
    • Components
      • Announcement
      • Banner Carousel
      • Banner With Products Carousel
      • Blog Category List
      • Blog List
      • Brand List
      • Brands Carousel
      • Breadcrumb
      • Call To Action
      • Cart
      • Categories List
      • Change Password
      • Checkout
      • Cookie Manager
      • Filter list
      • Footer
      • Forgot Password
      • Form
      • Hero Carousel
      • Icon Block
      • Invitation
      • Last Visited Products
      • Layout
      • Login
      • Map
      • Nav Bar
      • Offer
      • Product Attachments
      • Product Attributes
      • Product Documentation
      • Product Expected
      • Product Modal
      • Products Block
      • Products Carousel
      • Product Single
      • Profile
      • Quote
      • Register
      • Related Products
      • Search
      • Stores
      • Subscribe Newsletter
      • Text with Image
      • Top Bar
      • Video
    • Reusables
      • Getting Started
    • Assets
      • Getting Started
    • SDK
      • Products
        • _findProductsByCategory
        • _findProductsByIds
        • _findProductsByTitle
        • _findProductsByFilter
        • _findProductsByCriteria
        • _findProductsAndCalculate
        • _findProductsThenCalculate
        • _getProductAttributeSet
        • _setLastVisited
      • Categories
        • _findCategoryTreeById
        • _findCategoriesByIds
        • _findCategoryByAlias
        • _findCategoryTreeByAlias
        • _getCategoryContent
      • Collections
        • _getCollectionContent
        • _findCollectionsByIds
        • _findCollectionsByIdsThenCalculate
      • Brands
        • _getBrandContent
        • _findBrandsByIds
      • Cart
        • _addToCartMulti
        • _addToCart
        • _setCart
        • _clearCart
        • _setCartListener
        • _removeFromCart
        • _calculateCart
      • Checkout
        • _startCheckout
        • _updateCheckout
        • _completeCheckout
      • Shopping Lists
        • _getShoppingLists
        • _updateShoppingList
        • _createShoppingList
        • _deleteShoppingList
        • _getShoppingListByAlias
      • Navigation
        • _getFooterMenu
        • _getHeaderMenu
      • Users
        • _getUserById
      • Utils
        • _calculateCurrency
        • _getCurrencySymbol
        • _getCulture
        • _subscribeToNewsletter
        • _findUnitsByIds
  • Noir
    • 0. Introduction
    • 1. Structure
      • Overview
      • LayoutA.liquid
      • ComponentsList.liquid
      • Metas.liquid
      • CssVariables.liquid
      • Json.liquid
      • GoogleTagManager.liquid
      • StagingButton.liquid
    • 2. Components
      • Overview
      • Announcement
      • BannerCarousel
      • BlogCategoryList
      • BlogList
      • BrandList
      • Breadcrumb
      • Cart
      • CategoriesList
      • ChangePassword
      • Checkout
      • CookieManager
      • FilterList
      • Footer
      • ForgotPassword
      • Form
      • IconBlock
      • Invitation
      • LastVisitedProducts
      • Login
      • Map
      • NavBar
      • ProductAttachments
      • ProductAttributes
      • ProductComparison
      • ProductDocumentation
      • ProductMixList
      • ProductsBlock
      • ProductsCarousel
      • ProductSingle
      • Profile
      • Register
      • RelatedProducts
      • SingleBlog
      • Stores
      • TextWithImage
      • ThankYouPage
      • TopBar
      • Wishlist
    • 3. Reusables
      • Overview
      • Addresses
      • BillingRetail
      • AddressForm
      • AnnouncementModal
      • BackToTop
      • Company
      • General
      • Login
      • LoginModal
      • Orders
      • Payment
      • ProductAttachments
      • ProductAttributes
      • ProductComparisonButton
      • ProductComparisonFloatingButton
      • ProductGridItem
      • ProductListItem
      • ShoppingListsButton
      • ProductModal
      • ProfileInfo
      • PromptModal
      • Register
      • Shipping
      • ShoppingLists
      • ShoppingListsNavbar
      • Toast
      • Users
      • VariantContent
      • WishlistButton
      • Services
    • 4. Assets
      • Fonts
      • Images
      • Templates
      • Javascript
        • Overview
        • theme.js
      • Css / Scss
        • Overview
        • ThemeClasses
    • 5. SDK
      • Overview
      • LiquidGlobals
      • ServicesSDK
  1. 3. Reusables

PromptModal

Purpose#

The PromptModal reusable provides a single, theme-wide modal container that can render dynamic prompt forms (inputs, groups, buttons) driven by the global Alpine modal store.
It is intended to be mounted once (in the layout) and then opened/configured by other components/reusables by setting $store.modal.current.

Where it's rendered#

This reusable is rendered globally from Structure/LayoutA.liquid:
{% render 'Reusables\\PromptModal\\Default' %}

Inputs (Liquid render parameters)#

This reusable is rendered without Liquid parameters.
All runtime input is provided via the global Alpine store $store.modal.

Runtime input (Alpine modal store contract)#

Required: provide a real storefront model sample.
This reusable is driven by the $store.modal Alpine store. To document the contract correctly we need a real sample of the modal store shape at runtime (especially $store.modal.current).
Until a real storefront sample is provided, do not treat any inferred shape as authoritative.

Model shape (storefront example)#

{
  "title": "Sample title",
  "groups": [
    {
      "description": "Sample description",
      "inputs": [
        {
          "type": "checkbox",
          "label": "Sample text",
          "labelHidden": true,
          "name": "shoppingLists",
          "value": "",
          "required": false,
          "options": [
            {
              "key": "Shopping List Id",
              "value": "Sample text",
              "sticky": false
            },
            ...
          ]
        },
        {
          "type": "text",
          "label": "Sample text",
          "isHidden": true,
          "placeholder": "Sample text",
          "name": "newShoppingList",
          "value": "",
          "required": false,
          "options": [
            {
              "key": "Shopping List Id",
              "value": "Sample text"
            },
            ...
          ],
          "button": {
            "label": "Sample button text",
            "ariaLabel": "Sample text",
            "class": "btn--primary"
          }
        }
      ]
    }
  ],
  "data": [
    {
      "key": "shoppingLists",
      "value": "",
      "type": "checkbox",
      "required": false
    },
    {
      "key": "newShoppingList",
      "value": "",
      "type": "text",
      "isHidden": true,
      "required": false
    }
  ],
  "buttons": [
    {
      "label": "Sample button text",
      "class": "btn btn--lg btn--primary"
    }
  ]
}

Required fields#

To render a prompt form (inputs), the template expects:
$store.modal.visible: boolean
$store.modal.close(): function
$store.modal.current: object
current.groups: array (required if you want inputs)
each group:
group.inputs: array
each input:
type: string
name: string (used as key for $store.modal.errors[name] and current.data lookup)
required: boolean
Additionally, for data binding:
$store.modal.current.data: array of { key, value } entries
for each input, the template binds with:
$store.modal.current.data.find(field => field.key === input.name).value

Optional fields#

Used by the template when present:
$store.modal.current.title: string
$store.modal.current.message: string (HTML)
Per-group:
group.header: string
group.description: string (HTML)
Per-input:
label: string
labelHidden: boolean
isHidden: boolean
placeholder: string
helperText: string

Template behavior (Liquid + Alpine)#

Visibility and lifecycle#

Outer modal is shown when $store.modal.visible is true.
Clicking outside the modal box closes it via $store.modal.close().
On transition end (@after-leave) the template triggers $store.modal.close() again, which is expected to fully reset modal state.

Title and message#

Title is rendered when $store.modal.current?.title exists.
Message HTML is rendered when $store.modal.current?.message exists.

Dynamic input groups#

When $store.modal.current?.groups is present and non-empty, the template renders a dynamic form driven by those groups.
The inputs UI is managed by x-data="promptmodalreusabledefault".
The form controller can be force-reinitialized when $store.modal.shouldReinitialize becomes true:
if ($store.modal.shouldReinitialize) {
  reInit();
  $store.modal.shouldReinitialize = false;
}

Supported input types in Liquid#

The template includes UI blocks for the following input.type values:
text
textarea
email
file
checkbox (renders a checkbox group and maintains a -required hidden input)
The JS also supports validation for:
select
radio (through a .radio-wrap + -required hidden input pattern)
hidden inputs used as validation proxies for checkbox/radio groups
button: object (text input supports an Enter key handler that triggers button.action)
acceptedFileTypes: string[] (used for accept)
multipleFiles: boolean
options: array (checkbox/radio/select)
onchange(event): function (checkbox)

JavaScript#

Global object#

Global object: promptmodalreusabledefault (in Reusables/PromptModal/Default.js).
It is used directly as an Alpine component object:
Template: x-data="promptmodalreusabledefault"

State#

isFormValid: boolean
fields: array of DOM elements (initialized in reInit())

Methods#

reInit()#

Rebuilds internal field references and wires up validation handlers.
Key behaviors:
Collects all inputs under the modal content using:
[name]:not(.skipped)
Then filters out disabled and submit inputs.
For each field:
Adds a blur listener to validate.
For SELECT:
reads initial value from Alpine.store('modal').current.data.
toggles the isEmpty class on .select-wrap.
validates on change.
For checkbox, radio, file:
validates on change.
for switch toggles (.switch-wrap), also validates when the switch UI is clicked.
Initializes .radio-wrap groups:
Uses a hidden input named ${groupName}-required to validate required selection.
On radio change, copies the selected value into the hidden input and also updates Alpine.store('modal').current.data[0].value.
Initializes checkbox groups:
Listens on the modal root for checkbox changes.
Aggregates checked values into a comma-joined string stored in the hidden ${groupName}-required field.
Also updates the corresponding entry in Alpine.store('modal').current.data (as an array of selected values).
Ends by calling updateFormValidity().

toggleIsEmptyClass(field)#

Only applies to SELECT controls.
Toggles the .isEmpty class on the closest .select-wrap when the selected value is empty or the first option is selected.

validateField(field)#

Validates a single field and updates error state and UI classes.
Handled cases:
Hidden input inside .checkbox-wrap:
Validates required selection.
Writes to Alpine.store('modal').errors[hiddenInputName].
Toggles .invalid / .valid on .checkbox-wrap.
Hidden input inside .radio-wrap:
Validates required selection.
Writes to Alpine.store('modal').errors[hiddenInputName].
Toggles .invalid / .valid on .radio-wrap.
Calls updateFormValidity() and returns.
Required radio:
Ensures at least one radio in the group is checked.
Writes to Alpine.store('modal').errors[field.name].
Required checkbox:
Validates the whole group once (only when called on the first checkbox in the group).
Writes group-level error to Alpine.store('modal').errors[groupName].
If inside .consent, also updates Alpine.store('modal').current.data[0].value to boolean.
If inside .switch-wrap, toggles .invalid / .valid on the switch wrapper.
Required SELECT:
Empty value or first option is invalid.
Generic required fields:
Empty string is invalid.
file:
Validates allowed types based on the accept attribute.
Validates max size (10 MB).
Error types: invalidFileType, fileTooLarge.
email:
Validates format via regex.
Error type: invalidEmail.
UI feedback:
Sets invalid / valid classes on wrappers.
Updates .icon-state to show warning/check icons.
Calls updateFormValidity() at the end.

updateFormValidity()#

Sets isFormValid to true if every tracked field is valid.
Logic:
A field is considered valid when:
Alpine.store('modal').errors[field.name] is empty
and if required, it is not empty.

isDataValid()#

Validates all fields and returns a boolean.
Calls validateField for every field.
Returns false when isFormValid is false.
This is used by other reusables (example: Shopping Lists / Users) to guard modal submit actions.

Global Alpine stores#

This reusable depends on:
$store.modal
visible
current
errors
shouldReinitialize
close()

Services / API calls#

This reusable does not call services directly.
Form submission is expected to be implemented by the caller via $store.modal.current button actions (e.g. button.action(modalData) in other reusables).

Dependencies#

Liquid: Reusables/PromptModal/Default.liquid
JS: Reusables/PromptModal/Default.js
Translations: Reusables/PromptModal/Default.json
Alpine stores: $store.modal

Notes#

The template binds input values by searching $store.modal.current.data for matching keys. If an input exists in groups.inputs but no matching { key, value } exists in current.data, the template will throw at runtime.
Checkbox groups use both:
a hidden -required input for validation
and an array value in current.data for data semantics
Callers should treat the modal data as the source of truth.
isDataValid() is used as a cross-reusable validation gate (e.g. in Reusables/ShoppingLists/Default.js, Reusables/Users/Default.js, and Reusables/ShoppingListsButton/Default.js).
Modified at 2026-04-14 13:18:56
Previous
ProfileInfo
Next
Register
Built with