| 1 |
/** |
| 2 |
* WPBC BFB: Textarea Renderer (Schema-driven) |
| 3 |
* ===================================================================================================================== |
| 4 |
* File: /includes/page-form-builder/field-packs/textarea/_out/textarea.js |
| 5 |
* ===================================================================================================================== |
| 6 |
*/ |
| 7 |
(function (w) { |
| 8 |
'use strict'; |
| 9 |
|
| 10 |
var Core = w.WPBC_BFB_Core || {}; |
| 11 |
var registry = Core.WPBC_BFB_Field_Renderer_Registry; |
| 12 |
var Base = Core.WPBC_BFB_Field_Base; |
| 13 |
|
| 14 |
if ( ! registry || typeof registry.register !== 'function' || ! Base ) { |
| 15 |
_wpbc?.dev?.error?.( 'WPBC_BFB_Field_Textarea', 'Core registry/base missing' ); |
| 16 |
return; |
| 17 |
} |
| 18 |
|
| 19 |
/** |
| 20 |
* WPBC BFB: Field Renderer for "textarea" (Schema-driven, template-literal render) |
| 21 |
* |
| 22 |
* Contracts: |
| 23 |
* - Registry: WPBC_BFB_Field_Renderer_Registry.register( 'textarea', Class ) |
| 24 |
* - Class API: static get_defaults(), static render(el, data, ctx), static on_field_drop(data, el, ctx?) [optional] |
| 25 |
* |
| 26 |
* Notes: |
| 27 |
* - Keep defaults aligned with PHP schema->props->default. |
| 28 |
* - Uses WPBC_BFB_Sanitize helpers from Core. |
| 29 |
* - Uses Overlay.ensure(...) so field controls (handle, settings, etc.) appear. |
| 30 |
*/ |
| 31 |
class WPBC_BFB_Field_Textarea extends Base { |
| 32 |
|
| 33 |
/** |
| 34 |
* Return default props for "textarea" field. |
| 35 |
* Must stay in sync with PHP schema defaults. |
| 36 |
* |
| 37 |
* @returns {Object} |
| 38 |
*/ |
| 39 |
static get_defaults() { |
| 40 |
return { |
| 41 |
type : 'textarea', |
| 42 |
label : 'Textarea', |
| 43 |
name : '', |
| 44 |
html_id : '', |
| 45 |
placeholder : '', |
| 46 |
required : false, |
| 47 |
minlength : null, |
| 48 |
maxlength : null, |
| 49 |
rows : 4, |
| 50 |
cssclass : '', |
| 51 |
help : '', |
| 52 |
default_value: '', |
| 53 |
min_width : '260px' |
| 54 |
}; |
| 55 |
} |
| 56 |
|
| 57 |
/** |
| 58 |
* Render the preview markup into the field element. |
| 59 |
* |
| 60 |
* @param {HTMLElement} el Field root element inside the canvas. |
| 61 |
* @param {Object} data Field props (already normalized by schema). |
| 62 |
* @param {Object} ctx Context: { builder, sanit, ... } |
| 63 |
*/ |
| 64 |
static render(el, data, ctx) { |
| 65 |
if ( ! el ) { |
| 66 |
return; |
| 67 |
} |
| 68 |
|
| 69 |
// Normalize against defaults first. |
| 70 |
const d = this.normalize_data( data ); |
| 71 |
|
| 72 |
// ----- Core sanitize helpers (static) ----- |
| 73 |
const eh = (v) => Core.WPBC_BFB_Sanitize.escape_html( v ); |
| 74 |
const sid = (v) => Core.WPBC_BFB_Sanitize.sanitize_html_id( v ); |
| 75 |
const sname = (v) => Core.WPBC_BFB_Sanitize.sanitize_html_name( v ); |
| 76 |
const sclass = (v) => Core.WPBC_BFB_Sanitize.sanitize_css_classlist( v ); |
| 77 |
const truthy = (v) => Core.WPBC_BFB_Sanitize.is_truthy( v ); |
| 78 |
|
| 79 |
// Sanitize public id/name for the control itself. |
| 80 |
const html_id = d.html_id ? sid( String( d.html_id ) ) : ''; |
| 81 |
const name_val = sname( String( d.name || d.id || 'field' ) ); |
| 82 |
const css_next = sclass( String( d.cssclass || '' ) ); |
| 83 |
|
| 84 |
// Keep dataset in sync (do not mutate wrapper classes). |
| 85 |
if ( 'cssclass' in d && el.dataset.cssclass !== css_next ) { |
| 86 |
el.dataset.cssclass = css_next; |
| 87 |
} |
| 88 |
if ( 'html_id' in d && el.dataset.html_id !== html_id ) { |
| 89 |
el.dataset.html_id = html_id; |
| 90 |
} |
| 91 |
// NEW: persist min_width for the Min-Width guard / layout controller. |
| 92 |
if ( d.min_width ) { |
| 93 |
el.dataset.min_width = String( d.min_width ); |
| 94 |
el.style.setProperty( '--wpbc-col-min', String( d.min_width ) ); |
| 95 |
} |
| 96 |
|
| 97 |
// Flags / numeric constraints. |
| 98 |
const is_required = truthy( d.required ); |
| 99 |
const has_minlength = (d.minlength != null && d.minlength !== '' && Number.isFinite( +d.minlength )); |
| 100 |
const has_maxlength = (d.maxlength != null && d.maxlength !== '' && Number.isFinite( +d.maxlength )); |
| 101 |
const rows_num = (d.rows != null && Number.isFinite( +d.rows ) && +d.rows > 0) |
| 102 |
? Math.max( 1, Math.min( 50, +d.rows ) ) |
| 103 |
: 4; |
| 104 |
|
| 105 |
const minlength_num = has_minlength ? Number( d.minlength ) : null; |
| 106 |
const maxlength_num = has_maxlength ? Number( d.maxlength ) : null; |
| 107 |
|
| 108 |
// Attribute fragments. |
| 109 |
const id_attr = html_id ? ` id="${eh( html_id )}"` : ''; |
| 110 |
const name_attr = ` name="${eh( name_val )}"`; |
| 111 |
// Include the base preview class so the canvas styles apply. |
| 112 |
const cls_attr = ` class="wpbc_bfb__preview-input wpbc_bfb__preview-textarea${css_next ? ' ' + eh( css_next ) : ''}"`; |
| 113 |
const ph_attr = d.placeholder ? ` placeholder="${eh( d.placeholder )}"` : ''; |
| 114 |
const req_attr = is_required ? ' required aria-required="true"' : ''; |
| 115 |
const minlength_attr = has_minlength ? ` minlength="${minlength_num}"` : ''; |
| 116 |
const maxlength_attr = has_maxlength ? ` maxlength="${maxlength_num}"` : ''; |
| 117 |
const rows_attr = ` rows="${rows_num}"`; |
| 118 |
|
| 119 |
const label_html = (d.label != null && d.label !== '') |
| 120 |
? `<label class="wpbc_bfb__field-label"${html_id ? ` for="${eh( html_id )}"` : ''}>${eh( d.label )}${is_required ? '<span aria-hidden="true">*</span>' : ''}</label>` |
| 121 |
: ''; |
| 122 |
|
| 123 |
const help_html = d.help ? `<div class="wpbc_bfb__help">${eh( d.help )}</div>` : ''; |
| 124 |
const default_text = (d.default_value != null && d.default_value !== '') ? eh( String( d.default_value ) ) : ''; |
| 125 |
|
| 126 |
el.innerHTML = ` |
| 127 |
<span class="wpbc_bfb__noaction wpbc_bfb__no-drag-zone" inert=""> |
| 128 |
${label_html} |
| 129 |
<span class="wpbc_wrap_text wpdev-form-control-wrap"> |
| 130 |
<textarea${cls_attr}${ph_attr}${name_attr} tabindex="-1" aria-disabled="true"${id_attr}${rows_attr}${req_attr}${minlength_attr}${maxlength_attr}>${default_text}</textarea> |
| 131 |
</span> |
| 132 |
${help_html} |
| 133 |
</span> |
| 134 |
`; |
| 135 |
|
| 136 |
// Overlay (handles/toolbars). |
| 137 |
Core.UI?.WPBC_BFB_Overlay?.ensure?.( ctx?.builder, el ); |
| 138 |
} |
| 139 |
|
| 140 |
/** |
| 141 |
* Optional hook executed after field is dropped from the palette. |
| 142 |
* |
| 143 |
* @param {Object} data Palette/field data. |
| 144 |
* @param {HTMLElement} el Newly created field element. |
| 145 |
* @param {Object} ctx Context { builder, sanit, context: 'drop' | 'load' | 'preview' } |
| 146 |
* @returns {void} |
| 147 |
*/ |
| 148 |
static on_field_drop(data, el, ctx) { |
| 149 |
super.on_field_drop?.( data, el, ctx ); |
| 150 |
} |
| 151 |
} |
| 152 |
|
| 153 |
try { |
| 154 |
registry.register( 'textarea', WPBC_BFB_Field_Textarea ); |
| 155 |
} catch ( e ) { |
| 156 |
_wpbc?.dev?.error?.( 'WPBC_BFB_Field_Textarea.register', e ); |
| 157 |
} |
| 158 |
|
| 159 |
|
| 160 |
// ----------------------------------------------------------------------------------------------------------------- |
| 161 |
// Export for "Booking Form" (Advanced Form shortcode) |
| 162 |
// ----------------------------------------------------------------------------------------------------------------- |
| 163 |
/** |
| 164 |
* Booking Form exporter callback for "textarea". |
| 165 |
* |
| 166 |
* Produces shortcodes equivalent to the legacy exporter: |
| 167 |
* [textarea* your-name 40x4 id:your-name class:your-class "Default text"] |
| 168 |
* |
| 169 |
* Labels and help text are handled in the same centralized way as other packs. |
| 170 |
*/ |
| 171 |
function register_textarea_booking_form_exporter() { |
| 172 |
|
| 173 |
var Exp = w.WPBC_BFB_Exporter; |
| 174 |
if ( ! Exp || typeof Exp.register !== 'function' ) { return; } |
| 175 |
if ( typeof Exp.has_exporter === 'function' && Exp.has_exporter( 'textarea' ) ) { return; } |
| 176 |
|
| 177 |
var S = Core.WPBC_BFB_Sanitize || {}; |
| 178 |
|
| 179 |
/** |
| 180 |
* @type {WPBC_BFB_ExporterCallback} |
| 181 |
*/ |
| 182 |
Exp.register( 'textarea', function( field, emit, extras ) { |
| 183 |
extras = extras || {}; |
| 184 |
|
| 185 |
var cfg = extras.cfg || {}; |
| 186 |
var ctx = extras.ctx; |
| 187 |
var addLabels = cfg.addLabels !== false; |
| 188 |
|
| 189 |
// Required marker (same semantics as text field). |
| 190 |
var is_req = Exp.is_required( field ); |
| 191 |
var req_mark = is_req ? '*' : ''; |
| 192 |
|
| 193 |
// Shared helpers keep naming / id / classes consistent across packs. |
| 194 |
var name = Exp.compute_name( 'textarea', field ); |
| 195 |
var id_opt = Exp.id_option( field, ctx ); |
| 196 |
var cls_opts = Exp.class_options( field ); |
| 197 |
var ph_attr = Exp.ph_attr( field && field.placeholder ); |
| 198 |
var def_text = Exp.default_text_suffix( field ); |
| 199 |
|
| 200 |
// Rows token: [textarea name x5] — rows come from schema/Inspector (`field.rows`). |
| 201 |
// Clamp into [1,50] to mirror Inspector constraints. |
| 202 |
var rows_token = ''; |
| 203 |
if ( field && field.rows != null && field.rows !== '' ) { |
| 204 |
var r = Number( field.rows ); |
| 205 |
if ( ! Number.isFinite( r ) ) { |
| 206 |
r = 4; |
| 207 |
} |
| 208 |
if ( r < 1 ) { r = 1; } |
| 209 |
if ( r > 50 ) { r = 50; } |
| 210 |
rows_token = ' x' + String( r ); |
| 211 |
} |
| 212 |
|
| 213 |
// Final shortcode body (rows-only sizing; no columns), e.g.: |
| 214 |
// [textarea* your-message x5 id:your-message class:... "Default text"] |
| 215 |
var body = '[textarea' + req_mark + ' ' + name + rows_token + id_opt + cls_opts + ph_attr + def_text + ']'; |
| 216 |
|
| 217 |
// Label behavior mirrors legacy emit_label_then(). |
| 218 |
var raw_label = ( field && typeof field.label === 'string' ) ? field.label : ''; |
| 219 |
var label = raw_label.trim(); |
| 220 |
|
| 221 |
if ( label && addLabels ) { |
| 222 |
emit( '<l>' + (S.escape_html ? S.escape_html( label ) : label) + req_mark + '</l>' ); |
| 223 |
emit( '<br>' + body ); |
| 224 |
} else { |
| 225 |
emit( body ); |
| 226 |
} |
| 227 |
// Help text is appended centrally by WPBC_BFB_Exporter.render_field_node(). |
| 228 |
} ); |
| 229 |
} |
| 230 |
|
| 231 |
if ( w.WPBC_BFB_Exporter && typeof w.WPBC_BFB_Exporter.register === 'function' ) { |
| 232 |
register_textarea_booking_form_exporter(); |
| 233 |
} else if ( typeof document !== 'undefined' ) { |
| 234 |
document.addEventListener( 'wpbc:bfb:exporter-ready', register_textarea_booking_form_exporter, { once: true } ); |
| 235 |
} |
| 236 |
|
| 237 |
|
| 238 |
// ----------------------------------------------------------------------------------------------------------------- |
| 239 |
// Export for "Booking Data" (Content of booking fields data) |
| 240 |
// ----------------------------------------------------------------------------------------------------------------- |
| 241 |
/** |
| 242 |
* Booking Data exporter ("Content of booking fields data") for "textarea". |
| 243 |
* |
| 244 |
* Default line format: |
| 245 |
* <b>Label</b>: <f>[field_name]</f><br> |
| 246 |
*/ |
| 247 |
function register_textarea_booking_data_exporter() { |
| 248 |
|
| 249 |
var C = w.WPBC_BFB_ContentExporter; |
| 250 |
if ( ! C || typeof C.register !== 'function' ) { return; } |
| 251 |
if ( typeof C.has_exporter === 'function' && C.has_exporter( 'textarea' ) ) { return; } |
| 252 |
|
| 253 |
C.register( 'textarea', function( field, emit, extras ) { |
| 254 |
extras = extras || {}; |
| 255 |
var cfg = extras.cfg || {}; |
| 256 |
|
| 257 |
var Exp = w.WPBC_BFB_Exporter; |
| 258 |
if ( ! Exp || typeof Exp.compute_name !== 'function' ) { return; } |
| 259 |
|
| 260 |
var name = Exp.compute_name( 'textarea', field ); |
| 261 |
if ( ! name ) { return; } |
| 262 |
|
| 263 |
var label = ( field && typeof field.label === 'string' && field.label.trim() ) ? field.label.trim() : name; |
| 264 |
|
| 265 |
// Shared helper keeps formatting consistent across all packs. |
| 266 |
C.emit_line_bold_field( emit, label, name, cfg ); |
| 267 |
} ); |
| 268 |
} |
| 269 |
|
| 270 |
if ( w.WPBC_BFB_ContentExporter && typeof w.WPBC_BFB_ContentExporter.register === 'function' ) { |
| 271 |
register_textarea_booking_data_exporter(); |
| 272 |
} else if ( typeof document !== 'undefined' ) { |
| 273 |
document.addEventListener( 'wpbc:bfb:content-exporter-ready', register_textarea_booking_data_exporter, { once: true } ); |
| 274 |
} |
| 275 |
|
| 276 |
})( window ); |
| 277 |
|