id = sanitize_key($id); $this->taxonomy = jvbCheckBase($taxonomy); $this->name = jvbNoBase($taxonomy); $this->title = JVB_TAXONOMY[$this->name]['plural']; $this->base = $config['base']??''; $this->config = wp_parse_args($config, [ 'types' => false, // for feed block implementation 'max' => 0, // 0 = unlimited 'search' => true, 'label' => $this->name, 'icon' => false, 'autocomplete' => false, 'createNew' => false, 'required' => false, 'hidden' => false, 'base' => '', 'name' => $this->taxonomy, 'update' => true, // Whether to update on close ]); $this->plural = JVB_TAXONOMY[$taxonomy]['plural']; $this->singular = JVB_TAXONOMY[$taxonomy]['singular']; } /** * Get the full path for a term (for hierarchical taxonomies) * * @param WP_Term $term The term object * @param bool $returnArray if true, returns the array. If false, a string of terms separated by ' → ' * @return string|array An array of terms or the full term path */ public static function getTermPath(WP_Term $term, bool $returnArray = false): string|array { if (!is_taxonomy_hierarchical($term->taxonomy)) { return $term->name; } $path = []; $currentTerm = $term; while ($currentTerm) { array_unshift($path, $currentTerm->name); if ($currentTerm->parent) { $currentTerm = get_term($currentTerm->parent); if (is_wp_error($currentTerm)) { break; } } else { break; } } return ($returnArray) ? $path : implode(' → ', $path); } /** * Get the single modal HTML structure */ public static function outputSelectorModal(): string { ob_start(); ?>
config) && $this->config['output'] === 'minimal') { return $this->renderTaxonomyToggle($selected, $extra); } // Build data attributes $dataAttrs = $this->buildDataAttributes($selected); $hasAutocomplete = ($this->config['autocomplete']) ? ' data-autocomplete' : ''; // Hidden attribute $hidden = $this->config['hidden'] ? ' hidden' : ''; ob_start(); ?>
>
renderSelectedTerm($termId); ?>
name.'" title="Filter by '.$this->singular.'">'.jvbIcon($this->config['icon']).''.$this->singular.''; } /** * Build data attributes string for the toggle button */ private function buildDataAttributes(array $selected): string { $attrs = []; // Update behavior if (!$this->config['update']) { $attrs[] = 'data-update="false"'; } // Max selection if ($this->config['max'] > 0) { $attrs[] = 'data-max="' . esc_attr($this->config['max']) . '"'; } // Search capability if ($this->config['search']) { $attrs[] = 'data-search'; } // Create new capability if ($this->config['createNew']) { $attrs[] = 'data-creatable'; } // Required if ($this->config['required']) { $attrs[] = 'data-required'; } // Post types filter (for feed blocks) if ($this->config['types'] && is_array($this->config['types'])) { $attrs[] = 'data-for="' . esc_attr(implode(',', $this->config['types'])) . '"'; } // Selected items if (!empty($selected)) { $attrs[] = 'data-selected="' . esc_attr(implode(',', $selected)) . '"'; } return implode(' ', $attrs); } /** * Render a single selected term */ private function renderSelectedTerm(int $termId): void { $term = get_term($termId, $this->taxonomy); if (!$term || is_wp_error($term)) { return; } $termPath = self::getTermPath($term); ?>