| | |
| | | $validationAttrs = $this->buildValidationAttributes($field); |
| | | $conditional = array_key_exists('condition', $field) ? $this->handleConditionalField($field) : ''; |
| | | |
| | | $customData = ''; |
| | | if (array_key_exists('data', $field) && !empty($field['data'])) { |
| | | foreach ($field['data'] as $key => $v) { |
| | | $customData .= ($v === '') ? ' data-' . $key : ' data-' . $key . '="' . $v . '"'; |
| | | } |
| | | } |
| | | ?> |
| | | <div class="field <?= esc_attr($field['type']) ?> <?= esc_attr($name) ?>" |
| | | <?= $conditional ?> |
| | |
| | | name="<?= esc_attr($data['name']) ?>" |
| | | value="<?= esc_attr($data['value']) ?>" |
| | | <?= $inputAttrs ?> |
| | | <?= $customData?> |
| | | > |
| | | <span class="validation-icon success" hidden aria-hidden="true"> |
| | | <?= jvbIcon('check-circle') ?> |
| | |
| | | class="decrease" |
| | | title="<?= array_key_exists('remove', $field) ? $field['remove'] : 'Decrease amount' ?>" |
| | | aria-label="Decrease <?= esc_attr($field['label']) ?>"> |
| | | <?= jvbIcon('minus') ?> |
| | | <?= jvbIcon('minus-square') ?> |
| | | </button> |
| | | |
| | | <input type="number" |
| | |
| | | class="increase" |
| | | title="<?= array_key_exists('add', $field) ? $field['add'] : 'Increase amount' ?>" |
| | | aria-label="Increase <?= esc_attr($field['label']) ?>"> |
| | | <?= jvbIcon('add') ?> |
| | | <?= jvbIcon('plus-square') ?> |
| | | </button> |
| | | </div> |
| | | |
| | |
| | | <select |
| | | id="<?= esc_attr($data['id']) ?>" |
| | | name="<?= esc_attr($data['name']) ?>" |
| | | <?= $inputAttrs ?> |
| | | > |
| | | <?= $inputAttrs ?>> |
| | | <?php foreach ($field['options'] as $key => $label) : ?> |
| | | <option value="<?= esc_attr($key) ?>" <?php selected($value, $key); ?>> |
| | | <?= esc_html($label) ?> |
| | |
| | | |
| | | private function renderRepeaterField(string $name, mixed $value, array $field):void |
| | | { |
| | | error_log('Rendering Repeater Field!'); |
| | | $values = is_array($value) ? $value : array(); |
| | | |
| | | $conditional = $this->handleConditionalField($field); |
| | |
| | | <div class="repeater-row" data-index="<?= esc_attr($index); ?>"> |
| | | <details <?= (is_string($index)) ? 'open' : ''; ?>> |
| | | <summary class="repeater-row-header row btw"> |
| | | <span class="drag-handle"><?= jvbIcon('grab'); ?></span> |
| | | <span class="drag-handle"><?= jvbIcon('dots-six-vertical'); ?></span> |
| | | <span class="row-number">#<?= esc_html($display_number); ?></span> |
| | | <span class="row-title"><?= esc_html($this->getRowTitle($fields, $values, $rowTitle)); ?></span> |
| | | <button type="button" class="remove-row" title="Remove"> |
| | | <?= jvbIcon('delete', ['title'=>'Remove']); ?> |
| | | <?= jvbIcon('trash', ['title'=>'Remove']); ?> |
| | | </button> |
| | | </summary> |
| | | <div class="repeater-row-content"> |
| | |
| | | protected function renderGroupField(string $name, mixed $value, array $field): void |
| | | { |
| | | if (!array_key_exists('fields', $field) || empty($field['fields'])) { |
| | | error_log('No fields to render'); |
| | | return; |
| | | } |
| | | |
| | | |
| | | $values = is_array($value) ? $value : []; |
| | | $original = $name; |
| | | |
| | |
| | | $conditional = $this->handleConditionalField($field); |
| | | $validationAttrs = $this->buildValidationAttributes($field); |
| | | $describedBy = (!empty($field['description'])) ? ' aria-describedby="' . $name . '-help"' : ''; |
| | | |
| | | $fieldset = (array_key_exists('wrap', $field) && $field['wrap'] === 'details') ? 'details' : 'fieldset'; |
| | | $legend = (array_key_exists('wrap', $field) && $field['wrap'] === 'details') ? 'summary' : 'legend'; |
| | | ?> |
| | | <fieldset class="field group <?= esc_attr($name) ?>" |
| | | <<?= $fieldset?> class="field group <?= esc_attr($name) ?>" |
| | | <?= $conditional ?> |
| | | data-field="<?= esc_attr($name) ?>" |
| | | <?= $validationAttrs ?> |
| | | <?= $describedBy ?>> |
| | | <legend><?= esc_html($field['label']) ?></legend> |
| | | <<?=$legend?>><?= esc_html($field['label']) ?></<?=$legend?>> |
| | | |
| | | <?php $this->renderHintAndDescription($field, $name); ?> |
| | | |
| | |
| | | </div> |
| | | |
| | | <span class="validation-message" hidden role="alert"></span> |
| | | </fieldset> |
| | | </<?= $fieldset?>> |
| | | <?php |
| | | } |
| | | |
| | |
| | | //Processing Options |
| | | 'max_size' => null, // Override default size limits |
| | | 'convert' => 'webp', // Image conversion format |
| | | 'quality' => 80, // Conversion quality |
| | | 'quality' => 90, // Conversion quality |
| | | 'create_thumbnails' => true, |
| | | ]; |
| | | $config = array_merge($defaultConfig, $field); |
| | |
| | | <?php endif; ?> |
| | | <div class="file-error"></div> |
| | | </div> |
| | | <?php jvbRenderProgressBar(); ?> |
| | | </div> |
| | | |
| | | |
| | |
| | | <div class="selection-controls"> |
| | | <div class="selected"> |
| | | <div class="field"> |
| | | <input type="checkbox" id="select-all-uploads" name="select-all-uploads"> |
| | | <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 |
| | | </label> |
| | |
| | | |
| | | <div class="selection-actions row btw" hidden> |
| | | <button type="button" data-action="add-to-group"> |
| | | <?= jvbIcon('add') ?> |
| | | <?= jvbIcon('plus-square') ?> |
| | | Group |
| | | </button> |
| | | <button type="button" data-action="delete-upload"> |
| | | <?= jvbIcon('delete') ?> |
| | | <?= jvbIcon('trash') ?> |
| | | Delete |
| | | </button> |
| | | </div> |
| | | </div> |
| | | |
| | | <button type="button" data-action="upload" class="submit-uploads"> |
| | | <?= jvbIcon('upload') ?> Upload <?= esc_html($plural ?? 'Content'); ?> |
| | | <?= jvbIcon('cloud-arrow-up') ?> Upload <?= esc_html($plural ?? 'Content'); ?> |
| | | </button> |
| | | </div> |
| | | <?php endif; ?> |
| | |
| | | </div> |
| | | |
| | | <?php if ($destination === 'post_group') : ?> |
| | | <p class="hint"><?= jvbIcon('elbow-left-up') ?> These will become individual <?= $plural ?> <?= jvbIcon('elbow-right-up')?></p> |
| | | <p class="hint"><?= jvbIcon('arrow-elbow-left-up') ?> These will become individual <?= $plural ?> <?= jvbIcon('arrow-elbow-right-up')?></p> |
| | | </div> |
| | | <div class="sidebar flex col"> |
| | | <div class="header"> |
| | |
| | | <p>Drag here to create a new <?= $singular ?>!</p> |
| | | </div> |
| | | </div> |
| | | <p class="hint"><?= jvbIcon('elbow-left-up') ?> Each group will become its own <?= $singular ?> <?= jvbIcon('elbow-right-up')?></p> |
| | | <p class="hint"><?= jvbIcon('arrow-elbow-left-up') ?> Each group will become its own <?= $singular ?> <?= jvbIcon('arrow-elbow-right-up')?></p> |
| | | </div> |
| | | </div> |
| | | <?php endif; ?> |
| | |
| | | <?php |
| | | } |
| | | |
| | | private function renderExistingAttachment(int $attachmentId, string $subtype): string |
| | | { |
| | | ob_start(); |
| | | |
| | | switch ($subtype) { |
| | | case 'image': |
| | | $this->renderImagePreview($attachmentId); |
| | | break; |
| | | case 'video': |
| | | $this->renderVideoPreview($attachmentId); |
| | | break; |
| | | case 'document': |
| | | case 'file': |
| | | $this->renderFilePreview($attachmentId); |
| | | break; |
| | | default: |
| | | $this->renderImagePreview($attachmentId); |
| | | break; |
| | | } |
| | | |
| | | return ob_get_clean(); |
| | | } |
| | | |
| | | /** |
| | | * Get max file size for subtype |
| | | */ |
| | |
| | | <?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> |
| | |
| | | </div> |
| | | |
| | | <button type="button" data-action="delete-upload" title="Remove from Group"> |
| | | <?=jvbIcon('delete')?> |
| | | <?=jvbIcon('trash')?> |
| | | </button> |
| | | </div> |
| | | </div> |
| | | <details>'; |
| | | <summary class="row btw"><?=jvbIcon('edit')?><span>Edit Info</span></summary> |
| | | <details> |
| | | <summary class="row btw"><?=jvbIcon('pencil-simple')?><span>Edit Info</span></summary> |
| | | |
| | | <?php |
| | | |
| | |
| | | ] |
| | | ], $fields); |
| | | |
| | | $this->render('upload_data', null, $fields); |
| | | $meta = new MetaManager($id); |
| | | foreach ($fields as $field => $config) { |
| | | $meta->render('form', $field, $config); |
| | | } |
| | | ?> |
| | | </details> |
| | | </div> |
| | |
| | | </div> |
| | | |
| | | <button type="button" data-action="delete-upload" title="Remove from Group"> |
| | | <?=jvbIcon('delete')?> |
| | | <?=jvbIcon('trash')?> |
| | | </button> |
| | | </div> |
| | | </div> |
| | | <details>'; |
| | | <summary class="row btw"><?=jvbIcon('edit')?><span>Edit Info</span></summary> |
| | | <summary class="row btw"><?=jvbIcon('pencil-simple')?><span>Edit Info</span></summary> |
| | | |
| | | <?php |
| | | $fields = array_key_exists('fields', $config) ? $config['fields'] : []; |
| | |
| | | </div> |
| | | |
| | | <button type="button" data-action="delete-upload" title="Remove from Group"> |
| | | <?=jvbIcon('delete')?> |
| | | <?=jvbIcon('trash')?> |
| | | </button> |
| | | </div> |
| | | </div> |
| | | <details>'; |
| | | <summary class="row btw"><?=jvbIcon('edit')?><span>Edit Info</span></summary> |
| | | <summary class="row btw"><?=jvbIcon('pencil-simple')?><span>Edit Info</span></summary> |
| | | |
| | | <?php |
| | | $fields = array_key_exists('fields', $config) ? $config['fields'] : []; |
| | |
| | | * 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)); |
| | |
| | | } |
| | | |
| | | ?> |
| | | <div class="field <?= esc_attr($type) ?> <?= esc_attr($name) ?>" |
| | | <div class="field selector <?= esc_attr($type) ?> <?= esc_attr($name) ?>" |
| | | <?= $conditional ?> |
| | | data-field="<?= esc_attr($name) ?>" |
| | | data-type="selector" data-subtype="<?= esc_attr($type)?>" |
| | | <?= $validationAttrs ?> |
| | | <?= $describedBy ?>> |
| | | |
| | |
| | | |
| | | return is_numeric($value) ? [$value] : []; |
| | | } |
| | | /** |
| | | * Render tag list field - inline tag input interface |
| | | */ |
| | | protected function renderTagListField(string $name, mixed $value, array $field): void |
| | | { |
| | | $values = is_array($value) ? $value : []; |
| | | $conditional = $this->handleConditionalField($field); |
| | | $validationAttrs = $this->buildValidationAttributes($field); |
| | | |
| | | if (array_key_exists('group', $field)) { |
| | | $name = $field['group'] . '::' . $name; |
| | | } |
| | | |
| | | $describedBy = (!empty($field['description'])) ? ' aria-describedby="' . $name . '-help"' : ''; |
| | | |
| | | // Tag display format - defaults to first field value |
| | | $tagFormat = $field['tag_format'] ?? 'first_field'; |
| | | ?> |
| | | <div class="field tag-list <?= esc_attr($name) ?>" |
| | | data-field="<?= esc_attr($name) ?>" |
| | | data-tag-format="<?= esc_attr($tagFormat) ?>" |
| | | <?= $describedBy ?> |
| | | <?= $conditional ?> |
| | | <?= $validationAttrs ?>> |
| | | |
| | | <?php if (!empty($field['label'])): ?> |
| | | <h3><?= esc_html($field['label']) ?></h3> |
| | | <?php endif; ?> |
| | | |
| | | <!-- Inline input row --> |
| | | <div class="tag-input-row"> |
| | | <?php foreach ($field['fields'] as $subfield_name => $subfield_config): ?> |
| | | <?php |
| | | $subfield_config['label'] = $subfield_config['label'] ?? ucfirst($subfield_name); |
| | | $input_name = 'new_' . $subfield_name; |
| | | |
| | | // Store required state but don't render it on the input |
| | | // This prevents form submission validation but allows JS validation |
| | | |
| | | if (array_key_exists('required', $subfield_config)) { |
| | | $subfield_config['data']['required'] = true; |
| | | unset($subfield_config['required']); // Remove required for HTML rendering |
| | | } |
| | | $subfield_config['data']['ignore'] = true; |
| | | |
| | | $this->render($input_name, '', $subfield_config, false, false); |
| | | ?> |
| | | <?php endforeach; ?> |
| | | |
| | | <button type="button" class="button add-tag-item"> |
| | | <?= jvbIcon('plus') ?> <?= $field['add_label'] ?? 'Add' ?> |
| | | </button> |
| | | </div> |
| | | |
| | | <!-- Tags display --> |
| | | <div class="tag-items"> |
| | | <?php foreach ($values as $index => $item_data): ?> |
| | | <?php $this->renderTagItem($field['fields'], $item_data, $index, $name, $tagFormat); ?> |
| | | <?php endforeach; ?> |
| | | </div> |
| | | |
| | | <!-- Template for new tags --> |
| | | <template class="tag-template"> |
| | | <?php $this->renderTagItem($field['fields'], [], '', $name, $tagFormat); ?> |
| | | </template> |
| | | |
| | | <?php if (!empty($field['hint'])): ?> |
| | | <?php $this->renderHint($field['hint']); ?> |
| | | <?php endif; ?> |
| | | |
| | | <?php if (!empty($field['description'])): ?> |
| | | <?php $this->renderDescription($field['description'], $name); ?> |
| | | <?php endif; ?> |
| | | </div> |
| | | <?php |
| | | } |
| | | |
| | | /** |
| | | * Render individual tag item |
| | | */ |
| | | protected function renderTagItem(array $fields, array $data, int|string $index, string $base_name, string $format): void |
| | | { |
| | | $tag_text = $this->getTagDisplayText($fields, $data, $format); |
| | | ?> |
| | | <div class="tag-item" data-index="<?= esc_attr($index) ?>"> |
| | | <span class="tag-label"><?= esc_html($tag_text) ?></span> |
| | | |
| | | <!-- Hidden inputs for data --> |
| | | <?php foreach ($fields as $field_name => $field_config): ?> |
| | | <?php |
| | | $value = $data[$field_name] ?? ''; |
| | | $full_name = is_string($index) ? $field_name : "{$base_name}:{$index}:{$field_name}"; |
| | | ?> |
| | | <input type="hidden" |
| | | name="<?= esc_attr($full_name) ?>" |
| | | value="<?= esc_attr($value) ?>" |
| | | data-field="<?= esc_attr($field_name) ?>" /> |
| | | <?php endforeach; ?> |
| | | |
| | | <button type="button" class="remove-tag" aria-label="Remove"> |
| | | <?= jvbIcon('x') ?> |
| | | </button> |
| | | </div> |
| | | <?php |
| | | } |
| | | |
| | | /** |
| | | * Get tag display text based on format |
| | | */ |
| | | protected function getTagDisplayText(array $fields, array $data, string $format): string |
| | | { |
| | | if (empty($data)) { |
| | | return 'New Item'; |
| | | } |
| | | |
| | | switch ($format) { |
| | | case 'first_field': |
| | | // Use the first field's value |
| | | $first_key = array_key_first($fields); |
| | | return $data[$first_key] ?? 'New Item'; |
| | | |
| | | case 'all_fields': |
| | | // Show all field values separated by commas |
| | | $values = array_filter(array_values($data)); |
| | | return implode(', ', $values) ?: 'New Item'; |
| | | |
| | | case 'custom': |
| | | // Custom format - would need callback |
| | | return 'New Item'; |
| | | |
| | | default: |
| | | // Format is a template string like "{name} ({email})" |
| | | if (strpos($format, '{') !== false) { |
| | | $text = $format; |
| | | foreach ($data as $key => $value) { |
| | | $text = str_replace('{' . $key . '}', $value, $text); |
| | | } |
| | | return $text; |
| | | } |
| | | // Use specific field name |
| | | return $data[$format] ?? 'New Item'; |
| | | } |
| | | } |
| | | } |