From 46d681c6b825d21b3f698d793c4e630c687d90ad Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 21 May 2026 21:41:53 +0000
Subject: [PATCH] =Major CustomBlocks.php overhaul, expanding block support and customization from the editor. theme.json should now be updated on new themes to set brand colours, etc. Also note: major change to .col vs .row alignment: simplifying it to .top .bottom vs the confusion of the differences for .col/.row .start and .a-start

---
 inc/meta/Form.php |  257 +++++++++++++++++++++++++++++---------------------
 1 files changed, 149 insertions(+), 108 deletions(-)

diff --git a/inc/meta/Form.php b/inc/meta/Form.php
index fa37937..2cf3f05 100644
--- a/inc/meta/Form.php
+++ b/inc/meta/Form.php
@@ -2,6 +2,7 @@
 namespace JVBase\meta;
 
 use DateTime;
+use JVBase\registrar\Registrar;
 
 if (!defined('ABSPATH')) {
 	exit;
@@ -87,8 +88,11 @@
 	// Helper Methods
 	// ─────────────────────────────────────────────────────────────
 
-	public static function fieldWrap(string $name, string $content, array $config): string
+	public static function fieldWrap(string $name, string $content, array $config, bool $col = false): string
 	{
+		if (!array_key_exists('type', $config)) {
+			jvbDump('No type set for '.$name.' with config: '.print_r($config, true));
+		}
 		$classes = static::buildClasses($config);
 		$datasets = static::buildDatasets($config);
 
@@ -106,7 +110,7 @@
 		$output .= static::buildCharacterLimit($config);
 		$output .= static::buildLabel($name, $config);
 		if (!array_key_exists('skipInput', $config)) {
-			$output .= static::buildInput($content);
+			$output .= static::buildInput($content, $col);
 		} else {
 			$output .= $content;
 		}
@@ -210,14 +214,15 @@
 			return '';
 		}
 
-		public static function buildInput(string $content):string
+		public static function buildInput(string $content, bool $col = false):string
 		{
 			return sprintf(
-				'<div class="field-input-wrapper">
+				'<div class="wrapper row%s">
 			%s
-			<span class="validation-icon success" hidden aria-hidden="true">%s</span>
-			<span class="validation-icon error" hidden aria-hidden="true">%s</span>
+			<span class="validation success" hidden aria-hidden="true">%s</span>
+			<span class="validation error" hidden aria-hidden="true">%s</span>
 			</div><span class="validation-message" hidden role="alert"></span>',
+				$col ? ' col' : '',
 				$content,
 				jvbIcon('check-circle'),
 				jvbIcon('x-circle')
@@ -458,19 +463,21 @@
 		if (!array_key_exists('class', $config)) {
 			$config['class'] = [];
 		}
-		$config['class'][] ='row btw';
+		$config['class'][] ='row x-btw';
 
 		$checked = filter_var($value, FILTER_VALIDATE_BOOLEAN);
 
 		$input = sprintf(
-			'<label class="toggle-switch row">
+			'<label class="switch row">
                 <input type="checkbox" value="1"%s%s />
                 <div class="slider"></div>
+                %s
                 <span class="toggle-label">%s</span>
             </label>',
 			static::inputAttrs($name, $config),
 			$checked ? ' checked' : '',
-			array_key_exists('required', $config) && $config['required']===true ? '<span class="required" aria-label="required">*</span>' : ''
+			array_key_exists('required', $config) && $config['required']===true ? '<span class="required" aria-label="required">*</span>' : '',
+			$config['label']??''
 		);
 
 		unset($config['label']);
@@ -584,10 +591,15 @@
 				? '<span class="required" aria-label="required">*</span>' : ''
 		);
 
+
 		foreach ($options as $optValue => $optConfig) {
+			$class = '';
 			if (is_array($optConfig)) {
 				$optLabel    = $optConfig['label'] ?? $optValue;
 				$optIcon     = $optConfig['icon'] ?? null;
+				if ($optIcon) {
+					$class = ' class="btn"';
+				}
 				$optDisabled = !empty($optConfig['disabled']) ? ' disabled' : '';
 			} else {
 				$optLabel    = $optConfig;
@@ -602,11 +614,12 @@
 			$optId = esc_attr($idPrefix . $name . '-' . $optValue);
 
 			$radios .= sprintf(
-				'<input type="radio" name="%s" id="%s" value="%s"%s%s%s />
+				'<input type="radio" name="%s" id="%s" value="%s"%s%s%s%s />
             <label class="radio-option" for="%s" title="%s">%s</label>',
 				esc_attr($name),
 				$optId,
 				esc_attr($optValue),
+				$class,
 				checked($value, $optValue, false),
 				$optDisabled,
 				$inputClass,
@@ -628,7 +641,7 @@
 			//File Type
 			'subtype'	=> 'image',		//'image', 'video', 'document', 'any'
 			'accepted'	=> null,		//null = use subtype defaults, or define an array of specific MIME types
-			//Upload Behavious
+			//Upload Behaviours
 			'multiple'	=> false,		//single or multiple uploads
 			'limit'		=> 15, 			//Max number of uploads (0 = unlimited)
 			'mode'		=> 'direct',	// 'direct' or 'selection' TODO: unneeded?
@@ -637,7 +650,8 @@
 			'max_size'	=> null,		//override default size limits
 			'convert'	=> 'webp',		//Image conversion format
 			'quality'	=> 90,			//Conversion quality
-			'inputData' => []
+			'inputData' => [],
+			'data'	=> []
 		];
 		$config = array_merge($defaults, $config);
 
@@ -647,7 +661,7 @@
 			return '';
 		}
 		$validate = [
-			'subtype'	=> ['image', 'video', 'document', 'any'],
+			'subtype'	=> ['image', 'video', 'document', 'any','timeline'],
 			'mode'		=> ['direct', 'selection'],
 			'destination'=> ['meta', 'post', 'post_group']
 		];
@@ -664,7 +678,9 @@
 		//Add upload config to the datasets (handled by fieldWrap())
 		$attrs = ['subtype', 'mode', 'destination', 'max_size'];
 		foreach ($attrs as $attr) {
-			$config['data'][$attr] = $config[$attr];
+			if (array_key_exists($attr, $config)) {
+				$config['data'][$attr] = $config[$attr];
+			}
 		}
 		$config['data']['upload-field'] = '';
 		$config['data']['type'] = $config['multiple'] ? 'gallery' : 'single';
@@ -674,16 +690,9 @@
 			$content = $config['content'];
 
 			$config['data']['content'] = $content;
-			$plural =
-				JVB_CONTENT[$content]['plural']
-				?? JVB_TAXONOMY[$content]['plural']
-				?? JVB_USER[$content]['plural']
-				?? str_replace('_', ' ', $content) . 's';
-
-			$singular = JVB_CONTENT[$content]['singular']
-				?? JVB_TAXONOMY[$content]['singular']
-				?? JVB_USER[$content]['singular']
-				?? str_replace('_', ' ',$config['content']);
+			$registrar = Registrar::getInstance($content);
+			$plural = $registrar->getPlural()??str_replace('_', ' ', $content).'s';
+			$singular = $registrar->getSingular()??str_replace('_', ' ', $content);
 		}
 		if ($config['limit'] > 0) {
 			$config['data']['max-files'] = $config['limit'];
@@ -691,7 +700,13 @@
 
 		$attachmentIds = static::parseIds($value);
 
+
 		$input = sprintf(
+			'<input type="hidden" name="%s" value="%s">',
+			esc_attr($name),
+			esc_attr(!empty($attachmentIds) ? implode(',', $attachmentIds) : '')
+		);
+		$input .= sprintf(
 			'<div class="file-upload-container">
 			<div class="file-upload-wrapper">
 				<input type="file"
@@ -745,6 +760,8 @@
 		}
 
 		$input .= '</div>'; // closes .file-upload-container
+
+
 		unset($config['description']);
 		unset($config['label']);
 		return static::fieldWrap($name, $input, $config);
@@ -756,7 +773,7 @@
 					<div class="preview-actions">
 						<div class="selection-controls">
 							<div class="selected">
-								<div class="field">
+								<div class="field checkbox">
 									<input type="checkbox" id="select-all-uploads" data-select-all data-selects="item-grid" name="select-all-uploads">
 									<label for="select-all-uploads">
 										Select All
@@ -767,7 +784,7 @@
 								</div>
 							</div>
 
-							<div class="selection-actions row btw" hidden>
+							<div class="selection-actions row x-btw" hidden>
 								<button type="button" data-action="add-to-group">
 									%sGroup
 								</button>
@@ -831,9 +848,9 @@
 				protected static function renderUploadItemActions(?int $attachmentId = null):string
 				{
 					return sprintf(
-						'<div class="item-actions row btw">
-						<div class="radio-button">
-							<input type="radio" class="featured btn" name="featured" id="featured%d" hidden>
+						'<div class="item-actions row x-btw">
+						<div class="btn">
+							<input type="radio" class="featured btn" name="featured" id="featured%s" hidden>
 							<label for="featured">
 								%s%s<span class="screen-reader-text">Set as featured image</span>
 							</label>
@@ -899,12 +916,12 @@
 								'label' => 'Video Caption',
 								'data'  => ['id' => $ID]
 							],
-							'image-description' => [
-								'type'  => 'textarea',
-								'value' => $description,
-								'label' => 'Video Description',
-								'data'  => ['id' => $ID]
-							]
+//							'image-description' => [
+//								'type'  => 'textarea',
+//								'value' => $description,
+//								'label' => 'Video Description',
+//								'data'  => ['id' => $ID]
+//							]
 						]
 					];
 
@@ -967,12 +984,12 @@
 								'label' => 'File Caption',
 								'data'  => ['id' => $ID]
 							],
-							'image-description' => [
-								'type'  => 'textarea',
-								'value' => $description,
-								'label' => 'File Description',
-								'data'  => ['id' => $ID]
-							]
+//							'image-description' => [
+//								'type'  => 'textarea',
+//								'value' => $description,
+//								'label' => 'File Description',
+//								'data'  => ['id' => $ID]
+//							]
 						]
 					];
 
@@ -985,9 +1002,9 @@
 
 					return $out;
 				}
-				public static function renderImagePreview(?int $ID = null, ?array $additionalFields = null):string
+				public static function renderImagePreview(?int $ID = null, ?array $additionalFields = null, bool $isTemplate = false):string
 				{
-					$out = static::renderUploadItemStart($ID);
+					$out = static::renderUploadItemStart($ID, $isTemplate);
 					//add image preview
 					if ($ID) {
 						$out .= jvbFormatImage($ID, 'tiny', 'medium');
@@ -1029,30 +1046,30 @@
 								'label' => 'Image Caption',
 								'data'  => ['id' => $ID]
 							],
-							'image-description' => [
-								'type'  => 'textarea',
-								'value' => $description,
-								'label' => 'Image Description',
-								'data'  => ['id' => $ID]
-							]
+//							'image-description' => [
+//								'type'  => 'textarea',
+//								'value' => $description,
+//								'label' => 'Image Description',
+//								'data'  => ['id' => $ID]
+//							]
 						]
 					];
 
 					$out .= static::render('image_data', '', $fields);
-
-					$out .= static::renderUploadItemMetaEnd();
 					if ($additionalFields) {
 						$out .= static::additionalFields($additionalFields);
 					}
+					$out .= static::renderUploadItemMetaEnd();
 
 					return $out;
 				}
 				protected static function additionalFields(array $fields):string
 				{
-					$out = '';
+					$out = '<details class="fields"><summary>'.jvbDashIcon('edit').'Edit Fields</summary>';
 					foreach ($fields as $name => $config) {
 						$out .= static::render($name, '', $config);
 					}
+					$out .= '</details>';
 					return $out;
 				}
 
@@ -1127,6 +1144,9 @@
 
 		protected static function getAcceptedTypesLabel(array $config):string
 		{
+			if (!array_key_exists('subtype', $config) || $config['subtype'] === 'timeline') {
+				$config['subtype'] = 'image';
+			}
 			$labels = [
 				'image'	=> 'JPG, JPEG, PNG, GIF, or WEBP',
 				'video'	=> 'MP4, WEBM, or MOV',
@@ -1178,17 +1198,16 @@
 			'type'		=> $config['subtype'],
 		], $config);
 
-		$icon = match ($config['subtype']) {
-			'taxonomy' => JVB_TAXONOMY[$config['taxonomy']]['icon'] ?? jvbDefaultIcon(),
-			'content' => JVB_CONTENT[$config['content']]['icon'] ?? jvbDefaultIcon(),
-			'user' => JVB_USER[$config['role']]['icon'] ?? 'user',
-			default => jvbDefaultIcon(),
-		};
+		$registrar = Registrar::getInstance($config[$config['subtype']]);
+		$icon = jvbDefaultIcon();
+		if ($registrar){
+			$icon = $registrar->getIcon()??jvbDefaultIcon();
+		}
 
 		$containerId = sprintf('%s-%s-selector', $name, $config['subtype']);
 
 		$input = sprintf(
-			'<div class="row btw">
+			'<div class="row x-btw">
     <input type="hidden" name="%s" value="%s">
     <label for="%s-autocomplete">%s<span>%s</span></label>',
 			esc_attr($name),
@@ -1205,59 +1224,64 @@
 		}
 		$plural = static::getPlural($config);
 		$input .= sprintf(
-			'<div class="selected-items row" role="region" aria-label="Selected %s"></div>',
+			'<div class="selected-items row left" role="region" aria-label="Selected %s"></div>',
 			$plural[1]??''
 		);
 
+		$input .= '</div>'; //Close the first div.row.btw
+
 		$config['type'] = 'selector';
 		unset($config['label']);
 		unset($config['description']);
 		unset($config['hint']);
 		$config['skipInput'] = true;
-		return static::fieldWrap($containerId, $input, $config);
+		return static::fieldWrap($name, $input, $config);
 	}
 
 		protected static function getPlural(array $config):array
 		{
+			$single = $plural = '';
 			switch ($config['subtype']) {
 				case 'taxonomy':
-					if (array_key_exists($config['taxonomy'], JVB_TAXONOMY)) {
-						$single = JVB_TAXONOMY[$config['taxonomy']]['singular'];
-						$plural = JVB_TAXONOMY[$config['taxonomy']]['plural'];
+					$registrar = Registrar::getInstance($config['taxonomy']);
+					if ($registrar) {
+						$single = $registrar->getSingular();
+						$plural = $registrar->getPlural();
 					} else {
 						$taxonomy = get_taxonomy($config['taxonomy']);
-						if (!$taxonomy) {
-							return [];
+						if ($taxonomy) {
+							$single = $taxonomy->labels->singular_name;
+							$plural = $taxonomy->labels->name;
 						}
-						$single = $taxonomy->labels->singular_name;
-						$plural = $taxonomy->labels->name;
 					}
 					break;
 				case 'content':
-					if (array_key_exists($config['content'], JVB_CONTENT)) {
-						$single = JVB_CONTENT[$config['content']]['singular'];
-						$plural = JVB_CONTENT[$config['content']]['plural'];
+					$registrar = Registrar::getInstance($config['content']);
+					if ($registrar) {
+						$single = $registrar->getSingular();
+						$plural = $registrar->getPlural();
 					} else {
 						$postType = get_post_type_object($config['content']);
-						if (!$postType) {
-							return '';
+						if ($postType) {
+							$single = $postType->labels->singular_name;
+							$plural = $postType->labels->name;
 						}
-						$single = $postType->labels->singular_name;
-						$plural = $postType->labels->name;
+
 					}
 					break;
 
 				case 'user':
-					if (array_key_exists($config['user'], JVB_USER)) {
-						$single = JVB_USER[$config['user']]['singular'];
-						$plural = JVB_USER[$config['user']]['plural'];
+					$registrar = Registrar::getInstance($config['user']);
+
+					if ($registrar) {
+						$single = $registrar->getSingular();
+						$plural = $registrar->getPlural();
 					} else {
 						$user = get_role($config['user']);
-						if (!$user) {
-							return '';
+						if ($user) {
+							$single = 'User';
+							$plural = 'Users';
 						}
-						$single = 'User';
-						$plural = 'Users';
 					}
 					break;
 			}
@@ -1268,9 +1292,10 @@
 		{
 			switch ($config['subtype']) {
 				case 'taxonomy':
-					if (array_key_exists($config['taxonomy'], JVB_TAXONOMY)) {
-						$single = JVB_TAXONOMY[$config['taxonomy']]['singular'];
-						$plural = JVB_TAXONOMY[$config['taxonomy']]['plural'];
+					$registrar = Registrar::getInstance($config['taxonomy']);
+					if ($registrar) {
+						$single = $registrar->getSingular();
+						$plural = $registrar->getPlural();
 					} else {
 						$taxonomy = get_taxonomy($config['taxonomy']);
 						if (!$taxonomy) {
@@ -1287,9 +1312,10 @@
 					);
 					break;
 				case 'content':
-					if (array_key_exists($config['content'], JVB_CONTENT)) {
-						$single = JVB_CONTENT[$config['content']]['singular'];
-						$plural = JVB_CONTENT[$config['content']]['plural'];
+					$registrar = Registrar::getInstance($config['content']);
+					if ($registrar) {
+						$single = $registrar->getSingular();
+						$plural = $registrar->getPlural();
 					} else {
 						$postType = get_post_type_object($config['content']);
 						if (!$postType) {
@@ -1307,9 +1333,10 @@
 					break;
 
 				case 'user':
-					if (array_key_exists($config['user'], JVB_USER)) {
-						$single = JVB_USER[$config['user']]['singular'];
-						$plural = JVB_USER[$config['user']]['plural'];
+					$registrar = Registrar::getInstance($config['user']);
+					if ($registrar){
+						$single = $registrar->getSingular();
+						$plural = $registrar->getPlural();
 					} else {
 						$user = get_role($config['user']);
 						if (!$user) {
@@ -1471,7 +1498,7 @@
 		$config['data']['tag-format'] = esc_attr($tagFormat);
 
 		$input = sprintf(
-			'<h3>%s</h3><div class="row start wrap">',
+			'<h3>%s</h3><div class="row left wrap">',
 			esc_html($config['label']??'')
 		);
 
@@ -1486,7 +1513,7 @@
 			$input .= static::render($newName, '', $fieldConfig);
 		}
 		$input .= sprintf(
-			'<button type="button" class="button add-tag">%s<span>%s</span></button></div>',
+			'<button type="button" class="button add-tag">%s<span>%s</span></button>',
 			jvbIcon('plus'),
 			$config['add_label']??'Add'
 		);
@@ -1504,7 +1531,7 @@
 
 		unset($config['label']);
 
-		return static::fieldWrap($name, $input, $config);
+		return static::fieldWrap($name, $input, $config, true);
 	}
 		protected static function renderTagItems(array $fields, mixed $value, string $name, string $tagFormat):string
 		{
@@ -1589,8 +1616,8 @@
 	{
 		$fields = $config['fields'] ?? [];
 		$rows = is_array($value) ? $value : [];
-		if(array_key_exists('row_label', $config)) {
-			$config['data']['label'] = esc_attr($config['row_label']);
+		if(array_key_exists('add_label', $config)) {
+			$config['data']['label'] = esc_attr($config['add_label']);
 		}
 
 		$input = sprintf(
@@ -1617,6 +1644,7 @@
 		array_key_exists('add_label', $config) ? $config['add_label'] : 'Add Item'
 		);
 
+		unset($config['label']);
 		return static::fieldWrap($name, $input, $config);
 	}
 		protected static function renderRepeaterRow(array $fields, array $values, int|string $index, string $name, string $rowTitle='New Item'):string
@@ -1629,7 +1657,7 @@
 							%s
 						</button>
 						<details%s>
-							<summary class="row btw repeater-row-header">
+							<summary class="row x-btw repeater-row-header">
 								<span class="drag-handle">%s</span>
 								<span class="row-number">#%s</span>
 								<span class="row-title">%s</span>
@@ -1663,8 +1691,8 @@
 		$fields = $config['fields'] ?? [];
 		$values = is_array($value) ? $value : [];
 
-		$wrapper = (array_key_exists('wrap', $config)) ? 'details' : 'fieldset';
-		$legend = (array_key_exists('wrap', $config)) ? 'summary' : 'legend';
+		$wrapper = (array_key_exists('wrap', $config) && $config['wrap'] === 'details') ? 'details' : 'fieldset';
+		$legend = (array_key_exists('wrap', $config) && $config['wrap'] === 'details') ? 'summary' : 'legend';
 
 		$output = sprintf(
 			'<%s><%s>%s</%s>'
@@ -1701,8 +1729,8 @@
 					<div class="items-wrap">
 						<!-- Common/Favorite terms section -->
 						<details class="favourite-terms" hidden>
-							<summary class="title row btw">Your Go Tos:</summary>
-							<ul class="favourite-list row btw"></ul>
+							<summary class="title row x-btw">Your Go Tos:</summary>
+							<ul class="favourite-list row x-btw"></ul>
 						</details>
 
 						<!-- Pagination info -->
@@ -1720,7 +1748,7 @@
 							{ <span>loading items</span> }
 						</p>
 						<!-- Terms list -->
-						<ul class="items-container col start" role="listbox" aria-label="Available terms">
+						<ul class="items-container col top" role="listbox" aria-label="Available terms">
 							<!-- Terms will be populated here -->
 						</ul>
 
@@ -1739,7 +1767,7 @@
 
 					<!-- Create new term section -->
 					<details class="create-term" hidden>
-						<summary class="row btw">Add New Term</summary>
+						<summary class="row x-btw">Add New Term</summary>
 						<div class="create-new-term-section">
 							<form class="create-term" data-nocache data-form-id="create-term" data-save="terms">
 								<div class="form-group">
@@ -1798,20 +1826,33 @@
 		);
 	}
 
-	public static function search(string $placeholder = 'Search...', string $id = 'search'):string
+	public static function search(string $placeholder = 'Search...', string $id = 'search', string $label = '', string $buttonText = '',bool $buttonInside = false,  bool $hideSearch = false):string
 	{
 		$id = sanitize_title($id);
+		$label = empty($label) ? '' : sprintf(
+			'<h3>%s</h3>',
+			$label
+		);
+		$buttonText = empty($buttonText) ? '' : sprintf(
+			'<span>%s</span>',
+			$buttonText
+		);
+		$hideSearch = $hideSearch ? ' hidden' : '';
 		return sprintf(
-			'<div class="search-container row start nowrap">
-				<input type="search" id="%s" placeholder="%s">
+			'%s<div class="search-container row left nowrap%s">
+				<input type="search" id="%s" placeholder="%s"%s>
 				<button title="Clear Search" type="button" class="clear-search" aria-label="Clear search"
 					onclick="this.previousElementSibling.value = \'\'; this.previousElementSibling.focus();">%s</button>
-				<button type="button" title="Search" class="toggle search" aria-label="Toggles search input visually" onclick="this.parentNode.classList.toggle(\'open\');this.previousElementSibling.previousElementSibling.focus();">%s</button>
+				<button type="button" title="Search" class="toggle search" aria-label="Toggles search input visually" onclick="this.parentNode.classList.toggle(\'open\');this.previousElementSibling.previousElementSibling.focus();">%s%s</button>
 				</div>',
+			$label,
+			$buttonInside ? ' insideButton' : '',
 			$id,
 			$placeholder,
+			$hideSearch,
 			jvbIcon('x', ['title' => 'Clear Search']),
-			jvbIcon('magnifying-glass')
+			jvbIcon('magnifying-glass'),
+			$buttonText
 		);
 	}
 }

--
Gitblit v1.10.0