Jake Vanderwerf
2026-01-29 e6672fe38ce5d99f3b3f026154f777aded7361de
inc/meta/MetaForm.php
@@ -202,6 +202,7 @@
      $validationAttrs = $this->buildValidationAttributes($field);
      $conditional = array_key_exists('condition', $field) ? $this->handleConditionalField($field) : '';
      $pattern = array_key_exists('pattern', $field) ? $field['pattern'] : '';
      $customData = '';
      if (array_key_exists('data', $field) && !empty($field['data'])) {
         foreach ($field['data'] as $key => $v) {
@@ -212,6 +213,7 @@
      <div class="field <?= esc_attr($field['type']) ?> <?= esc_attr($name) ?>"
         <?= $conditional ?>
          data-field="<?= esc_attr($name) ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
         <?= $validationAttrs ?>>
         <?php $this->renderLabel($name, $field); ?>
@@ -224,6 +226,7 @@
               value="<?= esc_attr($data['value']) ?>"
               <?= $inputAttrs ?>
               <?= $customData?>
               <?= $pattern?>
            >
            <span class="validation-icon success" hidden aria-hidden="true">
                    <?= jvbIcon('check-circle') ?>
@@ -366,6 +369,7 @@
      <div class="field <?= esc_attr($field['type']) ?> <?= esc_attr($name) ?>"
         <?= $conditional ?>
          data-field="<?= esc_attr($name) ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
         <?= $validationAttrs ?>>
         <?php $this->renderLabel($name, $field); ?>
@@ -424,6 +428,7 @@
      <div class="field <?= esc_attr($field['type']) ?> <?= esc_attr($name) ?> row"
         <?= $conditional ?>
          data-field="<?= esc_attr($name) ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
         <?= $validationAttrs ?>>
         <?php $this->renderLabel($name, $field); ?>
@@ -474,6 +479,7 @@
      <div class="field <?= esc_attr($field['type']) ?> <?= esc_attr($name) ?>"
         <?= $conditional ?>
          data-field="<?= esc_attr($name) ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
         <?= $validationAttrs ?>>
         <?php $this->renderLabel($name, $field); ?>
@@ -514,6 +520,7 @@
      <div class="field <?= esc_attr($field['type']) ?> <?= esc_attr($name) ?>"
         <?= $conditional ?>
          data-field="<?= esc_attr($name) ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
         <?= $validationAttrs ?>>
         <fieldset>
@@ -559,6 +566,7 @@
      <div class="field <?= esc_attr($field['type']) ?> <?= esc_attr($name) ?>"
         <?= $conditional ?>
          data-field="<?= esc_attr($name) ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
         <?= $validationAttrs ?>>
         <fieldset>
@@ -600,6 +608,7 @@
      <div class="field true-false <?= esc_attr($name) ?> row btw"
         <?= $conditional ?>
          data-field="<?= esc_attr($name) ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
         <?= $validationAttrs ?>>
         <label class="toggle-switch row" <?= $describedBy ?>>
@@ -642,6 +651,7 @@
      ?>
      <div class="field repeater <?=$name?>"
          data-field="<?= esc_attr($name); ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
         <?= $describedBy ?>
         <?= $row_label ? 'data-label="' . esc_attr($row_label) . '"' : ''; ?>
         <?=$conditional?>>
@@ -663,7 +673,7 @@
            ?>
         </div>
         <template class="<?=uniqid('repeaterTemplate')?>">
         <template class="<?=uniqid('repeaterRow')?>">
            <?php $this->renderRepeaterRow($field['fields'], array(), '', '', $rowTitle); ?>
         </template>
@@ -756,7 +766,8 @@
      ?>
      <<?= $fieldset?> class="field group <?= esc_attr($name) ?>"
         <?= $conditional ?>
              data-field="<?= esc_attr($name) ?>"
         data-field="<?= esc_attr($name) ?>"
         data-field-type="<?=esc_attr($field['type'])?>"
         <?= $validationAttrs ?>
         <?= $describedBy ?>>
         <<?=$legend?>><?= esc_html($field['label']) ?></<?=$legend?>>
@@ -780,7 +791,9 @@
   {
      foreach ($field['fields'] as $field_name => $config) {
         // Set the group context for proper field naming
         $config['group'] = $groupName;
         if (!array_key_exists('wrap', $field) || $field['wrap'] !== 'details') {
            $config['group'] = $groupName;
         }
         // Get the value for this specific field
         $field_value = $values[$field_name] ?? '';
@@ -834,7 +847,7 @@
      // Build accept attribute for input
      $acceptExtensions = $this->getMimeExtensions($acceptedTypes);
      $acceptAttr = implode(',', $acceptExtensions);
      $acceptAttr = implode(',', $acceptedTypes);
      // Determine field attributes
      $subtype = $config['subtype'] ?? 'image';
@@ -880,6 +893,8 @@
      }
      ?>
      <div class="field upload <?= esc_attr($name) ?>"
          data-field="<?=esc_attr($name)?>"
          data-field-type="upload"
         <?= $dataAttrString ?>
         <?= $conditional ?>>
@@ -1100,71 +1115,69 @@
      $dataID = ($id) ? ['id' => $id] : '';
      ?>
      <div class="item upload"<?= ($id) ? ' data-id="'.$id.'"' : '' ?>>
            <div class="preview">
               <?php jvbRenderProgressBar('',true) ?>
               <input type="checkbox" class="upload-select" name="select-item" id="select-item<?=$addID?>">
               <label for="select-item<?=$addID?>" aria-label="Select image">
                  <?= ($attachment) ?: '<img>
                  <video></video>
                  <span></span>' ?>
               </label>
               <div class="item-actions row btw">
                  <div class="radio-button">
                     <input type="radio" class="featured btn" name="featured" id="featured" hidden>
                     <label for="featured">
                        <?=jvbIcon('star')?>
                        <?=jvbIcon('star', ['style' => 'fill'])?>
                        <span class="screen-reader-text">Set as featured image</span>
                     </label>
                  </div>
                  <button type="button" data-action="delete-upload" title="Remove from Group">
                     <?=jvbIcon('trash')?>
                  </button>
         <div class="preview">
            <?php jvbRenderProgressBar('',true) ?>
            <input type="checkbox" class="upload-select" name="select-item" id="select-item<?=$addID?>">
            <label for="select-item<?=$addID?>" aria-label="Select image">
               <?= ($attachment) ?: '<img>
                <video></video>
                <span></span>' ?>
            </label>
            <div class="item-actions row btw">
               <div class="radio-button">
                  <input type="radio" class="featured btn" name="featured" id="featured" hidden>
                  <label for="featured">
                     <?=jvbIcon('star')?>
                     <?=jvbIcon('star', ['style' => 'fill'])?>
                     <span class="screen-reader-text">Set as featured image</span>
                  </label>
               </div>
               <button type="button" data-action="delete-upload" title="Remove from Group">
                  <?=jvbIcon('trash')?>
               </button>
            </div>
            <details>
               <summary class="row btw"><?=jvbIcon('pencil-simple')?><span>Edit Info</span></summary>
         </div>
         <details>
            <summary class="row btw"><?=jvbIcon('pencil-simple')?><span>Edit Info</span></summary>
      <?php
            <?php
      $fields = array_key_exists('fields', $config) ? $config['fields'] : [];
      $fields = array_merge([
         'upload_data'  => [
            'type'   => 'group',
            'wrap'   => 'details',
            'label'  => 'Image Info',
            'hint'   => 'These will be automatically generated if left blank.',
            'fields' => [
               'image-title'.$addID => [
                  'type'   => 'text',
                  'label'  => 'Image Title',
                  'value'  => $title,
                  'data'   => $dataID
               ],
               'image-alt-text'.$addID => [
                  'type'   => 'text',
                  'label'  => 'Alt Text',
                  'value'  => $alt,
                  'hint'   => 'Alt text helps the visually impaired, as well as some benefits for SEO.',
                  'data'   => $dataID
               ],
               'image-caption'.$addID => [
                  'type'   => 'textarea',
                  'value'  => $caption,
                  'label'  => 'Image Caption',
                  'data'   => $dataID
            $fields = [
               'image_data' => [
                  'type'   => 'group',
                  'wrap'   => 'details',
                  'label'  => 'Image Fields',
                  'fields' => [
                     'image-title'.$addID => [
                        'type'  => 'text',
                        'label' => 'Image Title',
                        'value' => $title,
                        'data'  => $dataID
                     ],
                     'image-alt-text'.$addID => [
                        'type'  => 'text',
                        'label' => 'Alt Text',
                        'value' => $alt,
                        'hint'  => 'Alt text helps the visually impaired, as well as some benefits for SEO.',
                        'data'  => $dataID
                     ],
                     'image-caption'.$addID => [
                        'type'  => 'textarea',
                        'value' => $caption,
                        'label' => 'Image Caption',
                        'data'  => $dataID
                     ]
                  ]
               ]
            ]
         ]
      ], $fields);
      $meta = new MetaManager($id);
      foreach ($fields as $field => $config) {
         $meta->render('form', $field, $config);
      }
      ?>
            </details>
            ];
            $fields = array_key_exists('fields', $config) ? array_merge($fields, $config['fields']) : $fields;
            $meta = new MetaManager($id);
            foreach ($fields as $field => $config) {
               $meta->render('form', $field, $config);
            }
            ?>
         </details>
      </div>
      <?php
   }
@@ -1182,7 +1195,7 @@
               <?php jvbRenderProgressBar('',true) ?>
               <input type="checkbox" class="upload-select" name="select-item" id="select-item<?=$addID?>">
               <label for="select-item<?=$addID?>" aria-label="Select image">
                  <?= ($attachment) ? $attachment : '<img>
                  <?= ($attachment) ?: '<img>
                  <video></video>
                  <span></span>'; ?>
               </label>
@@ -1255,7 +1268,7 @@
               <?php jvbRenderProgressBar('',true) ?>
               <input type="checkbox" class="upload-select" name="select-item" id="select-item<?=$addID?>">
               <label for="select-item<?=$addID?>" aria-label="Select image">
                  <?= ($attachment) ? $attachment : '<img>
                  <?= ($attachment) ?: '<img>
                  <video></video>
                  <span></span>'; ?>
               </label>
@@ -1351,13 +1364,12 @@
    * Generic selector field renderer
    * Handles both taxonomy and post selectors with consistent structure
    */
   private function renderSelectorField(string $name, mixed $value, array $field, string $type): void
   public function renderSelectorField(string $name, mixed $value, array $field, string $type): void
   {
      $conditional = $this->handleConditionalField($field);
      $validationAttrs = $this->buildValidationAttributes($field);
      $describedBy = (!empty($field['description'])) ? ' aria-describedby="' . $name . '-help"' : '';
      $isSimple = (array_key_exists('mode', $field) && $field['mode']==='simple');
      // Parse selected values
      $value = (is_array($value)) ? array_filter(array_map('absint', $value)): $value;
      $selected = ($value === '') ? [] : (is_array($value) ? $value : explode(',', $value));
@@ -1411,7 +1423,8 @@
      <div class="field selector <?= esc_attr($type) ?> <?= esc_attr($name) ?>"
         <?= $conditional ?>
          data-field="<?= esc_attr($name) ?>"
          data-type="selector" data-subtype="<?= esc_attr($type)?>"
          data-field-type="selector"
          data-type="<?=esc_attr($field['type'])?>"
         <?= $validationAttrs ?>
         <?= $describedBy ?>>
@@ -1477,6 +1490,7 @@
      <div class="field location <?= esc_attr($field_id) ?>"
          data-field="<?= esc_attr($field_id) ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
          data-location-field-init="<?= $json_config ?>"<?=$describedBy?>>
         <?php
@@ -1581,11 +1595,11 @@
      // Default types based on subtype
      $defaults = [
         'image' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
         'video' => ['video/mp4', 'video/webm', 'video/ogg'],
         'document' => ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
         'any' => ['image/*', 'video/*', 'application/pdf']
         'image' => ['image/*'],
         'video' => ['video/*'],
         'document' => ['application/pdf', 'application/msword', 'application/vnd.ms-excel', 'text/plain', '.odt','application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
      ];
      $defaults['any'] = array_merge($defaults['image'], $defaults['video'], $defaults['document']);
      return $defaults[$config['subtype']] ?? $defaults['image'];
   }
@@ -1639,6 +1653,7 @@
      ?>
      <div class="field tag-list <?= esc_attr($name) ?>"
          data-field="<?= esc_attr($name) ?>"
          data-field-type="<?=esc_attr($field['type'])?>"
          data-tag-format="<?= esc_attr($tagFormat) ?>"
         <?= $describedBy ?>
         <?= $conditional ?>
@@ -1681,7 +1696,7 @@
         </div>
         <!-- Template for new tags -->
         <template class="tag-template">
         <template class="<?=uniqid('tagListItem')?>">
            <?php $this->renderTagItem($field['fields'], [], '', $name, $tagFormat); ?>
         </template>
@@ -1715,7 +1730,8 @@
            <input type="hidden"
                  name="<?= esc_attr($full_name) ?>"
                  value="<?= esc_attr($value) ?>"
                  data-field="<?= esc_attr($field_name) ?>" />
                  data-field="<?= esc_attr($field_name) ?>"
                  data-field-type="<?=esc_attr($field_config['type'])?>" />
         <?php endforeach; ?>
         <button type="button" class="remove-tag" aria-label="Remove">