handleConditionalField($config) : '';
if (!array_key_exists('type', $config)) {
return $out;
}
if (array_key_exists('display', $config) && $config['display'] === 'hidden'){
$out = ' ';
if (!$return) {
echo $out;
}
return $out;
}
ob_start();
$type = array_map( 'ucfirst', explode('_', $config['type']));
$type = implode('', $type);
$method = 'render' . $type . 'Field';
$nameTemp = implode('', array_map('ucfirst', explode('_', $name)));
$nameMethod = 'render'.$nameTemp.'Field';
if(function_exists($nameMethod)) {
call_user_func($nameMethod, $value, $config);
} elseif (function_exists($method)) {
call_user_func($method, $value, $config);
} elseif (method_exists($this, $method)) {
$this->$method($name, $value, $config);
}
$out = ob_get_clean();
do_action('jvbRenderFormField', $name, $config, $value);
$out = apply_filters('jvbFilterRenderFormField', $out, $name, $config, $value);
if (!$return) {
echo $out;
}
return $out;
}
public function renderTextField(string $name, mixed $value, array $field):void
{
// Use field-specific value if provided, otherwise use the meta value
$display_value = isset($field['value']) ? $field['value'] : $value;
$conditional = $this->handleConditionalField($field);
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
$placeholder = (array_key_exists('placeholder', $field)) ? ' placeholder="'.$field['placeholder'].'"' : '';
$autocomplete = (array_key_exists('autocomplete', $field)) ? ' autocomplete="'.$field['autocomplete'].'"' : '';
?>
data-field="=$name?>">
= esc_html($field['label']); ?>
0 /= esc_attr($field['limit']); ?>
= $autocomplete ?>
= !empty($field['required']) ? 'required' : ''; ?>
= $describedBy ?>
>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
outputCharacterCountJS();
}
}
private function renderTelField(string $name, mixed $value, array $field):void
{$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
// Use field-specific value if provided, otherwise use the meta value
$display_value = isset($field['value']) ? $field['value'] : $value;
$conditional = $this->handleConditionalField($field);
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
$placeholder = (array_key_exists('placeholder', $field)) ? ' placeholder="'.$field['placeholder'].'"' : '';
$autocomplete = (array_key_exists('autocomplete', $field)) ? ' autocomplete="'.$field['autocomplete'].'"' : '';
?>
data-field="=$name?>">
= esc_html($field['label']); ?>
0 /= esc_attr($field['limit']); ?>
= $autocomplete?>
= $describedBy ?>
= !empty($field['required']) ? 'required' : ''; ?>
>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
outputCharacterCountJS();
}
}
private function renderEmailField(string $name, mixed $value, array $field):void
{
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
// Use field-specific value if provided, otherwise use the meta value
$display_value = isset($field['value']) ? $field['value'] : $value;
$conditional = $this->handleConditionalField($field);
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
$placeholder = (array_key_exists('placeholder', $field)) ? ' placeholder="'.$field['placeholder'].'"' : '';
$autocomplete = (array_key_exists('autocomplete', $field)) ? ' autocomplete="'.$field['autocomplete'].'"' : '';
?>
data-field="=$name?>">
= esc_html($field['label']); ?>
0 /= esc_attr($field['limit']); ?>
= esc_attr($name); ?>"
name="= array_key_exists('base', $field) ? esc_attr($field['base']) : ''?>= esc_attr($name); ?>"
value="= esc_attr($display_value); ?>"
= $placeholder ?>
= $autocomplete ?>
= $describedBy ?>
= !empty($field['required']) ? 'required' : ''; ?>
>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
outputCharacterCountJS();
}
}
private function renderUrlField(string $name, mixed $value, array $field):void
{
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
// Use field-specific value if provided, otherwise use the meta value
$display_value = isset($field['value']) ? $field['value'] : $value;
$conditional = $this->handleConditionalField($field);
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
$placeholder = (array_key_exists('placeholder', $field)) ? ' placeholder="'.$field['placeholder'].'"' : '';
$autocomplete = (array_key_exists('autocomplete', $field)) ? ' autocomplete="'.$field['autocomplete'].'"' : '';
?>
data-field="=$name?>">
= esc_html($field['label']); ?>
0 /= esc_attr($field['limit']); ?>
= $describedBy ?>
= $autocomplete ?>
= !empty($field['required']) ? 'required' : ''; ?>
>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
outputCharacterCountJS();
}
}
private function renderNumberField(string $name, mixed $value, array $field):void
{
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
$description = 'Tip: hold Ctrl/Command to increase 5x Shift to increase 10x, Or Ctrl/Command + Shift to increase 50x ';
$description .= $field['description']??'';
$conditional = $this->handleConditionalField($field);
$min = isset($field['min']) ? (float)$field['min'] : 0;
$max = isset($field['max']) ? (float)$field['max'] : 100;
$step = isset($field['step']) ? (float)$field['step'] : 1;
$data = '';
if (array_key_exists('data', $field) && !empty($field['data'])) {
foreach($field['data'] as $key => $v) {
if ($v === '') {
$data .= ' data-'.$key;
} else {
$data .= ' data-'.$key.'="'.$v.'"';
}
}
}
if (empty($value)) {
$value = $field['default']??0;
}
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
$autocomplete = (array_key_exists('autocomplete', $field)) ? ' autocomplete="'.$field['autocomplete'].'"' : '';
?>
data-field="=$name?>">
= esc_html($field['label']); ?>
>
= jvbIcon('minus')?>
= $autocomplete ?>
= !empty($field['required']) ? 'required' : ''; ?>>
= jvbIcon('add')?>
renderDescription($description, $name); ?>
handleConditionalField($field);
$quill = (array_key_exists('quill', $field) && $field['quill'] == true) ? ' data-editor="true"' : '';
if ($quill !== '') {
$allowImages = array_key_exists('allowImage', $field);
$quill .= ($allowImages) ? ' data-allowimage="true"' : ' data-allowimage="false"';
}
// Handle array values
if (is_array($value)) {
$value = implode(', ', $value);
}
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
$placeholder = (array_key_exists('placeholder', $field)) ? ' placeholder="'.$field['placeholder'].'"' : '';
?>
data-field="=$name?>">
= esc_html($field['label']); ?>
0 /= esc_attr($field['limit']); ?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
renderCheckboxField($name, $value, $field);
}
private function renderCheckboxField(string $name, mixed $value, array $field):void
{
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
$value = !is_array($value) ? explode(',', $value) : $value;
$limit = isset($field['limit']) ? (int)$field['limit'] : 0;
$conditional = $this->handleConditionalField($field);
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
?>
=$conditional?> data-field="=$name?>"=$describedBy?>>
= esc_html($field['label']); ?>
$label) : ?>
= esc_attr($name)?>[]"
value="= esc_attr($key); ?>"
= (in_array($key, $value)) ? 'checked' : ''; ?>
= !empty($field['required']) ? 'required' : ''; ?>
>
= esc_html($label); ?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
handleConditionalField($field);
if (!array_key_exists('label', $field)) {
error_log('No label for: '.print_r($name, true));
}
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
?>
data-field="=$name?>"=$describedBy?>>
= esc_html($field['label']); ?>
$label) : ?>
= (in_array($key, $value)) ? 'checked' : ''; ?>
>
= esc_html($label); ?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
handleConditionalField($field);
$row_label = isset($field['row_label']) ? $field['row_label'] : '';
$rowTitle = (array_key_exists('new_row', $field)) ? $field['new_row'] : 'New Item';
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
?>
= $row_label ? 'data-label="' . esc_attr($row_label) . '"' : ''; ?>
=$conditional?>>
= esc_html($field['label']); ?>
$row) {
$this->renderRepeaterRow($field['fields'], $row, $index, $name, $rowTitle);
}
}
?>
renderRepeaterRow($field['fields'], array(), '', '', $rowTitle); ?>
= jvbIcon('plus', ['title'=> 'Add']); ?> = (array_key_exists('add_label', $field)) ? $field['add_label'] : 'Add Item'; ?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
>
$field) :
if ($base_name === '') {
$field_name = $slug;
} else {
$field_name = sprintf('%s:%s:%s', $base_name, $index, $slug);
}
$field_value = isset($values[$slug]) ? $values[$slug] : '';
$name = $field_name;
$this->render($name, $field_value, $field);
endforeach;
?>
$field) {
if (in_array($field['type'], ['text', 'textarea']) &&
isset($values[$slug]) &&
!empty($values[$slug])) {
return $values[$slug];
}
}
return $rowTitle;
}
private function renderTaxonomyField(string $name, mixed $value, array $field):void
{
$conditional = $this->handleConditionalField($field);
$taxonomy = $field['taxonomy'];
// Get currently selected terms
$selected_terms = ($value === '') ? [] : explode(',', $value);
// Convert selected term IDs to the format expected by single modal
$processedSelected = [];
if (!empty($selected_terms)) {
foreach ($selected_terms as $termId) {
if (is_numeric($termId)) {
$term = get_term($termId, $taxonomy);
if ($term && !is_wp_error($term)) {
$processedSelected[$term->term_id] = [
'name' => $term->name,
'path' => TaxonomySelector::getTermPath($term)
];
}
}
}
}
// Create configuration for single modal system
$config = [
'taxonomy' => $taxonomy,
'max' => $field['limit'] ?? 0,
'search' => $field['search'] ?? true,
'createNew' => $field['createNew'] ?? false,
'selected' => $processedSelected,
'base' => $field['base'] ?? '',
];
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
?>
data-field="=$name?>">
';
echo $tax->render([], $extra);
?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
handleConditionalField($field);
// Process selected posts
$selected_posts = $value;
if (is_string($selected_posts)) {
$selected_posts = !empty($selected_posts) ? explode(',', $selected_posts) : [];
} elseif (!is_array($selected_posts)) {
$selected_posts = [];
}
// Configure the post selector
$config = [
'multiple' => $field['multiple'] ?? true,
'maxSelections' => $field['limit'] ?? 0,
'search' => true,
'placeholder' => $field['placeholder'] ?? 'Search posts...',
'noResults' => 'No posts found',
'shop_id' => $field['shop_id'] ?? null,
'onClose' => 'updateMetaFormPost'
];
$postSelector = new PostSelector($field['post_type'], $config);
$containerId = $name . '-post-selector';
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
?>
data-field="=$name?>">
= $postSelector->render($selected_posts, $containerId) ?>
data-post-type="= esc_attr($field['post_type']) ?>"
value="= esc_attr(is_array($selected_posts) ? implode(',', $selected_posts) : $selected_posts) ?>">
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
handleConditionalField($field);
// Ensure value is an array
$values = is_array($value) ? $value : [];
$original = $name;
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
$hidden = (array_key_exists('mode', $field) && $field['mode'] === 'hidden');
if (!$hidden) {
?>
data-field="=$name?>"= $describedBy?>>
= esc_html($field['label']) ?>
"= $describedBy ?>>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
$config) {
// Set the group context for proper field naming
$config['group'] = $name;
// Get the value for this specific field
$field_value = $values[$field_name] ?? '';
// Handle conditional fields within the group
if (isset($config['condition'])) {
// Convert condition field reference to group context
$condition_field = $config['condition']['field'];
if (!str_contains($condition_field, '::')) {
$config['condition']['field'] = $name . '::' . $condition_field;
}
}
$this->render($field_name, $field_value, $config);
}
?>
connect('maps');
if (!$googleMaps->isSetUp()) {
echo 'Google Maps not configured. Please configure in Integrations settings.
';
return;
}
// Extract stored values
if (is_string($value)) {
$value = maybe_unserialize($value);
}
$stored_data = is_array($value) ? $value : [];
$address = $stored_data['address'] ?? '';
$lat = $stored_data['lat'] ?? '';
$lng = $stored_data['lng'] ?? '';
// Generate unique field ID
$field_id = esc_attr($name);
$map_id = $field_id . '_map';
// Handle grouped fields
if (array_key_exists('group', $field)) {
$name = $field['group'] . '::' . $name;
}
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
// Prepare configuration for JavaScript initialization
$js_config = [
'fieldId' => $field_id,
'initialCoords' => (!empty($lat) && !empty($lng)) ? [
'lat' => (float)$lat,
'lng' => (float)$lng
] : null
];
// IMPORTANT: Properly escape the JSON for use in HTML attribute
$json_config = htmlspecialchars(json_encode($js_config), ENT_QUOTES, 'UTF-8');
?>
>
Current location: '.esc_html($stored_data['street']).'';
echo '
Search below to change:
';
}
?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
handleConditionalField($field);
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
?>
>
= esc_html($field['label']); ?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
>
Available Items
Loading...
No items found
= jvbIcon('arrow-right', ['title' => 'Add selected']); ?>
= jvbIcon('arrow-left', ['title' => 'Remove selected']); ?>
Selected Items
(= esc_html($limit); ?> max)
post_type, $post_types)) {
$item_type = 'post';
$item_title = $post->post_title;
$item_object = $post->post_type;
}
}
// Check if it's a term
if (empty($item_type) && in_array('term', $object_types)) {
foreach ($taxonomies as $taxonomy) {
$term = get_term($item_id, $taxonomy);
if (!is_wp_error($term) && $term) {
$item_type = 'term';
$item_title = $term->name;
$item_object = $term->taxonomy;
break;
}
}
}
// Only output if we found the item
if (!empty($item_type) && !empty($item_title)) {
?>
= esc_html($item_title); ?>
= esc_html(ucfirst($item_object)); ?>
= jvbIcon('close', ['title' => 'Remove']); ?>
handleConditionalField($field);
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
?>
data-field="=$name?>">
>
= !empty($field['required']) ? 'required' : ''; ?>
>
= esc_html($field['label']); ?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
'image', // 'image', 'video', 'document', 'any'
'accepted_types' => null, // null = use subtype defaults, or array of specific MIME types
//Upload Behaviour
'multiple' => false, // Single or multiple uploads
'limit' => 0, // Max number of uploads (0 = unlimited)
'mode' => 'direct', // 'direct' or 'selection'
//Destination
'destination' => 'meta', // 'meta', 'post', 'post_group'
//Processing Options
'max_size' => null, // Override default size limits
'convert' => 'webp', // Image conversion format
'quality' => 80, // Conversion quality
'create_thumbnails' => true,
];
$config = array_merge($defaultConfig, $field);
// Validate destination config
if (in_array($config['destination'], ['post', 'post_group']) && empty($config['content'])) {
error_log("Upload field '{$name}' has destination '{$config['destination']}' but no content defined");
return;
}
// Get accepted types
$acceptedTypes = $this->getAllowedTypes($config);
// Build accept attribute for input
$acceptExtensions = $this->getMimeExtensions($acceptedTypes);
$acceptAttr = implode(',', $acceptExtensions);
// Determine field attributes
$subtype = $config['subtype'] ?? 'image';
$multiple = $config['multiple'] ?? false;
$limit = $config['limit'] ?? 0;
$mode = $config['mode'] ?? 'direct';
$destination = $config['destination'];
// Get existing attachments
$attachmentIds = $this->parseAttachmentIds($value);
// Determine field type for UI
$fieldType = $multiple ? 'gallery' : 'single';
// Build data attributes
$dataAttrs = [
'data-field' => $name,
'data-upload-field' => '',
'data-mode' => $mode,
'data-type' => $fieldType,
'data-subtype' => $subtype,
'data-destination' => $destination,
];
if (!empty($field['content'])) {
$dataAttrs['data-content'] = $field['content'];
}
if ($limit > 0) {
$dataAttrs['data-limit'] = $limit;
}
// Build data attributes
$conditional = $this->handleConditionalField($field);
$describedBy = !empty($field['description']) ? ' aria-describedby="' . esc_attr($name) . '-help"' : '';
if (!empty($field['group'])) {
$name = $field['group'] . '::' . $name;
}
// Convert data attributes to string
$dataAttrString = '';
foreach ($dataAttrs as $attr => $val) {
$dataAttrString .= ' ' . $attr . ($val !== '' ? '="' . esc_attr($val) . '"' : '');
}
?>
= $conditional ?>>
= !empty($field['required']) ? 'required' : '' ?>>
= esc_html($field['label']) ?>
= esc_html($field['description']) ?>
Click to upload or drag and drop
= esc_html($this->getAcceptedTypesLabel($subtype, $acceptExtensions)) ?>
(max. = esc_html($this->formatFileSize($this->getMaxFileSize($subtype))) ?>)
You can group images to create separate = $plural ?>.
If a =$singular?> has multiple images, you can select the = jvbIcon('star')?> to set an image as the main one.
= esc_html($field['upload_description']); ?>
= jvbIcon('add') ?>
Group
= jvbIcon('delete') ?>
Delete
= jvbIcon('upload') ?> Upload = esc_html($plural ?? 'Content'); ?>
Processing files...
0/0 '); ?>
renderExistingAttachment($attachmentId, $subtype);
}
?>
= jvbIcon('elbow-left-up') ?> These will become individual = $plural ?> = jvbIcon('elbow-right-up')?>
>
[
'image/jpeg',
'image/png',
'image/gif',
'image/webp'
],
'video' => [
'video/mp4',
'video/webm',
'video/ogg',
'video/ogv',
'video/quicktime'
],
'document' => [
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'text/plain',
'text/csv'
],
'any' => [] // Will be merged from all types
];
// If specific types are defined, use those
if (!empty($config['accepted_types'])) {
return is_array($config['accepted_types'])
? $config['accepted_types']
: [$config['accepted_types']];
}
// Otherwise use subtype defaults
$subtype = $config['subtype'] ?? 'image';
if ($subtype === 'any') {
return array_merge(
$typeMap['image'],
$typeMap['video'],
$typeMap['document']
);
}
return $typeMap[$subtype] ?? $typeMap['image'];
}
/**
* Parse attachment IDs from value
*/
private function parseAttachmentIds(mixed $value): array
{
if (empty($value)) return [];
if (is_array($value)) {
return array_filter(array_map('absint', $value));
}
return array_filter(array_map('absint', explode(',', $value)));
}
/**
* Get file extensions for MIME types
*/
private function getMimeExtensions(array $mimeTypes): array
{
$extensionMap = [
'image/jpeg' => ['.jpg', '.jpeg'],
'image/png' => ['.png'],
'image/gif' => ['.gif'],
'image/webp' => ['.webp'],
'video/mp4' => ['.mp4'],
'video/webm' => ['.webm'],
'video/ogg' => ['.ogg'],
'video/ogv' => ['.ogv'],
'video/quicktime' => ['.mov'],
'application/pdf' => ['.pdf'],
'application/msword' => ['.doc'],
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => ['.docx'],
'application/vnd.ms-excel' => ['.xls'],
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => ['.xlsx'],
'text/plain' => ['.txt'],
'text/csv' => ['.csv'],
];
$extensions = [];
foreach ($mimeTypes as $mime) {
if (isset($extensionMap[$mime])) {
$extensions = array_merge($extensions, $extensionMap[$mime]);
}
}
return array_unique($extensions);
}
/**
* Get max file size for subtype
*/
private function getMaxFileSize(string $subtype): int
{
$sizes = [
'image' => 5242880, // 5MB
'video' => 104857600, // 100MB
'document' => 10485760 // 10MB
];
return $sizes[$subtype] ?? $sizes['image'];
}
/**
* Get human-readable file size label
*/
private function getMaxFileSizeLabel(string $subtype): string
{
$bytes = $this->getMaxFileSize($subtype);
$mb = round($bytes / 1048576);
return "{$mb}MB";
}
/**
* Format file size for display
*/
private function formatFileSize(int $bytes): string
{
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 1) . 'GB';
}
if ($bytes >= 1048576) {
return number_format($bytes / 1048576, 1) . 'MB';
}
if ($bytes >= 1024) {
return number_format($bytes / 1024, 1) . 'KB';
}
return $bytes . 'B';
}
/**
* Get accepted types label
*/
private function getAcceptedTypesLabel(string $subtype, array $extensions): string
{
$labels = [
'image' => 'JPG, PNG, GIF, or WEBP',
'video' => 'MP4, WEBM, or MOV',
'document' => 'PDF, DOC, XLS, or TXT',
'any' => 'Images, Videos, or Documents'
];
return $labels[$subtype] ?? strtoupper(implode(', ', array_map(function($ext) {
return ltrim($ext, '.');
}, array_slice($extensions, 0, 3))));
}
/**
* Render existing attachment
*/
private function renderExistingAttachment(int $attachmentId, string $subtype): string
{
$attachment = get_post($attachmentId);
if (!$attachment) return '';
$url = wp_get_attachment_url($attachmentId);
$thumbUrl = $subtype === 'image'
? wp_get_attachment_image_url($attachmentId, 'medium')
: $url;
ob_start();
?>
= jvbIcon('document') ?>
= esc_html(basename($url)) ?>
Remove = esc_attr($subtype) ?>
×
= ' data-type="'.$dataType.'"'?>>
>
= esc_html($field['label']); ?>
= esc_html($field['description']); ?>
Click to upload or drag and drop
JPG, PNG, GIF, or WEBP (max. 5MB)
You can group images to create separate = $plural ?>.
If a =$singular?> has multiple images, you can select the = jvbIcon('star')?> to set an image as the main one.
= esc_html($field['upload_description']); ?>
= jvbIcon('add') ?>
Create New = $singular ?>
= jvbIcon('delete') ?>
Remove
= jvbIcon('upload') ?> Upload = esc_html($plural ?? 'Content'); ?>
Processing files...
0/0 '); ?>
= jvbIcon('elbow-left-up') ?> These will become individual = $plural ?> = jvbIcon('elbow-right-up')?>
>
handleConditionalField($field);
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
//TODO: This can probably just be a wrapper for renderImageField...
?>
>
= esc_html($field['label']); ?>
';
echo '
';
echo '
' . jvbIcon('trash', ['title'=>'Remove']) . ' ';
echo '
';
}
}
}
?>
>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
handleConditionalField($field);
$default = isset($field['default']) ? $field['default'] : '';
$value = !empty($value) ? $value : $default;
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
?>
>
= esc_html($field['label']); ?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
= !empty($field['required']) ? 'required' : ''; ?>
>
$label) : ?>
>
= esc_html($label); ?>
$method_name();
}
echo ($content == '') ? '' : sprintf(
'%s
',
esc_attr($name),
$content
);
}
private function renderDateField(string $name, mixed $value, array $field):void
{
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
$conditional = $this->handleConditionalField($field);
$format = !empty($field['format']) ? $field['format'] : 'Y-m-d';
// Format the date if we have a value
if (!empty($value)) {
$date = DateTime::createFromFormat($format, $value);
if ($date) {
$value = $date->format('Y-m-d'); // HTML date input requires Y-m-d format
}
}
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
?>
data-field="=$name?>">
= esc_html($field['label']); ?>
>
data-format="= esc_attr($format); ?>"
>
= jvbIcon('event') ?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
handleConditionalField($field);
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
// Convert various time formats to HTML time input format (HH:MM)
if (!empty($value)) {
// If it's already in HH:MM format, use as-is
if (preg_match('/^\d{2}:\d{2}$/', $value)) {
// Value is already in correct format
} else {
// Try to parse and convert
$timestamp = strtotime($value);
if ($timestamp !== false) {
$value = date('H:i', $timestamp);
} else {
$value = '';
}
}
}
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
?>
data-field="=$name?>">
= esc_html($field['label']); ?>
>
= !empty($field['min']) ? 'min="' . esc_attr($field['min']) . '"' : ''; ?>
= !empty($field['max']) ? 'max="' . esc_attr($field['max']) . '"' : ''; ?>
= !empty($field['step']) ? 'step="' . esc_attr($field['step']) . '"' : ''; ?>
>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
handleConditionalField($field);
$describedBy = (!empty($field['description'])) ? ' aria-describedby="'.$name.'-help"' : '';
// Convert datetime to HTML datetime-local format (YYYY-MM-DDTHH:MM)
if (!empty($value)) {
$date = DateTime::createFromFormat('Y-m-d H:i:s', $value);
if (!$date) {
// Try alternative formats
$formats = ['Y-m-d\TH:i:s', 'Y-m-d\TH:i', 'Y-m-d H:i'];
foreach ($formats as $format) {
$date = DateTime::createFromFormat($format, $value);
if ($date) break;
}
}
if ($date) {
$value = $date->format('Y-m-d\TH:i'); // HTML datetime-local format
} else {
$value = '';
}
}
if (array_key_exists('group', $field)) {
$name = $field['group'].'::'.$name;
}
?>
data-field="=$name?>">
= esc_html($field['label']); ?>
>
= !empty($field['min']) ? 'min="' . esc_attr($field['min']) . '"' : ''; ?>
= !empty($field['max']) ? 'max="' . esc_attr($field['max']) . '"' : ''; ?>
= !empty($field['step']) ? 'step="' . esc_attr($field['step']) . '"' : ''; ?>
>
= jvbIcon('event') ?>
renderHint($field['hint']); } ?>
renderDescription($field['description'], $name); } ?>
'.jvbIcon('help').'
';
echo $out;
}
protected function renderHint(array|string $hint):void
{
if (is_array($hint)) {
$out = '';
foreach($hint as $h) {
$out .= ''.$h.'
';
}
} else {
$out = ''.$hint.'
';
}
echo $out;
}
}