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
    • 6. FAQ
    • 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
      • Offer
      • ProductAttachments
      • ProductAttributes
      • ProductComparison
      • ProductDocumentation
      • ProductMixList
      • ProductsBlock
      • ProductsCarousel
      • ProductSingle
      • Profile
      • Quote
      • Register
      • RelatedProducts
      • SingleBlog
      • Stores
      • TextWithImage
      • ThankYouPage
      • TopBar
      • Wishlist
    • 3. Reusables
      • _Overview
      • Addresses
      • AddressForm
      • AnnouncementModal
      • BackToTop
      • BillingRetail
      • Company
      • General
      • Login
      • LoginModal
      • MonthlyTransactions
      • Offers
      • Orders
      • Payment
      • ProductAttachments
      • ProductAttributes
      • ProductComparisonButton
      • ProductComparisonFloatingButton
      • ProductGridItem
      • ProductListItem
      • ProductModal
      • ProfileInfo
      • PromptModal
      • Quotes
      • Register
      • Services
      • Shipping
      • ShoppingLists
      • ShoppingListsButton
      • ShoppingListsNavbar
      • Toast
      • Transactions
      • Users
      • VariantContent
      • WishlistButton
    • 4. Assets
      • Fonts
      • Images
      • Templates
      • Javascript
        • _Overview
        • theme.js
      • Css - Scss
        • _Overview
        • ThemeClasses
    • 5. SDK
      • _Overview
      • LiquidGlobals
      • ServicesSDK
  1. 3. Reusables

PromptModal

Purpose#

PromptModal is the configurable modal/form reusable used by multiple account, checkout, and management workflows.
It provides shared prompt rendering, validation helpers, and action wiring for dynamic modal content.

Where it's rendered#

In 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)

Data contract (JS runtime)#

Document runtime state shape, factories, and main state fields.

JavaScript#

This reusable calls into the shared services reusable:
servicesreusabledefault.generateTextFromAI(dataToGenerateFrom, cultureCode, currency)
Used by promptmodalreusabledefault.generateTextFromAI(...) to request AI-generated text.
On success, it writes the returned text into $store.modal.current.data for the inputNameToFill field.
On failure, it shows a toast error and stops loading.

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.

initDatePickers()#

Initializes flatpickr for any date inputs rendered inside the modal (inputs with data-datepicker-options).
For switch toggles (.switch-wrap), it 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-06-15 06:55:54
Previous
ProfileInfo
Next
Quotes
Built with