✨feature: Implement project-type taxonomy archive template and enhance breadcrumb navigation
Sync TODOs with Issues / sync_todos (push) Successful in 8s
Sync TODOs with Issues / sync_todos (push) Successful in 8s
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* Project Type Taxonomy Archive Template (plugin override)
|
||||
*
|
||||
* The projects-portfolio plugin's template loader looks for this filename
|
||||
* (archive-project-type.php) via locate_template(); when present it is used
|
||||
* in place of the plugin's bundled template. We mirror the pattern used by
|
||||
* the post-type archive and the Recent Projects block: a flex-row head
|
||||
* with the term title on the left and an "All Projects" link on the right,
|
||||
* a divider, then the project card grid.
|
||||
*
|
||||
* @package KsPortfolio
|
||||
*/
|
||||
|
||||
namespace KsPortfolio;
|
||||
|
||||
get_header();
|
||||
|
||||
$queried = get_queried_object();
|
||||
$term_name = $queried && ! is_wp_error( $queried ) ? $queried->name : __( 'Projects', 'ks-portfolio' );
|
||||
?>
|
||||
<section class="tax-project container mx-auto my-section">
|
||||
<?php if ( have_posts() ) : ?>
|
||||
<div class="tax-project__grid grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<?php
|
||||
while ( have_posts() ) :
|
||||
the_post();
|
||||
get_template_part( 'views/components/project-card' );
|
||||
endwhile;
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div class="tax-project__pagination">
|
||||
<?php
|
||||
the_posts_pagination(
|
||||
array(
|
||||
'mid_size' => 2,
|
||||
'prev_text' => '« ' . esc_html__( 'Previous', 'ks-portfolio' ),
|
||||
'next_text' => esc_html__( 'Next', 'ks-portfolio' ) . ' »',
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class="tax-project__empty">
|
||||
<h2 class="tax-project__empty-title"><?php echo esc_html__( 'Nothing here yet…', 'ks-portfolio' ); ?></h2>
|
||||
<p class="tax-project__empty-desc"><?php echo esc_html__( 'No published projects found in this category.', 'ks-portfolio' ); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
@@ -46,7 +46,11 @@ class Breadcrumbs {
|
||||
// Static Page - add the parent pages if any, and the page title
|
||||
$breadcrumbs = array_merge( $breadcrumbs, self::getStaticPageBreadcrumbs( $post ) );
|
||||
} elseif ( is_category() || is_tag() || is_tax() ) {
|
||||
// Taxonomy Archive - add the taxonomy term name
|
||||
// Taxonomy Archive - if this is a project-type term, surface the
|
||||
// Projects archive as a clickable parent in the trail.
|
||||
if ( is_tax( 'project-type' ) ) {
|
||||
$breadcrumbs[] = self::getProjectsArchiveLinkBreadcrumb();
|
||||
}
|
||||
$breadcrumbs[] = self::getTaxonomyArchiveBreadcrumb();
|
||||
} elseif ( is_post_type_archive() ) {
|
||||
// Post Type Archive - add the post type name
|
||||
@@ -205,6 +209,24 @@ class Breadcrumbs {
|
||||
return $postType ? array( 'label' => $postType->labels->name ) : array();
|
||||
}
|
||||
|
||||
/** Generates a clickable Projects archive breadcrumb.
|
||||
*
|
||||
* Used in the taxonomy archive trail so users have a working "back to
|
||||
* all projects" link in the breadcrumb. Distinct from
|
||||
* getPostTypeArchiveBreadcrumb(), which omits the URL because on the
|
||||
* post-type archive itself that crumb is the active leaf and shouldn't
|
||||
* link to itself.
|
||||
*
|
||||
* @return array Breadcrumb data with both label and url.
|
||||
*/
|
||||
private static function getProjectsArchiveLinkBreadcrumb() {
|
||||
$url = get_post_type_archive_link( 'projects' );
|
||||
return array(
|
||||
'url' => $url ? $url : '',
|
||||
'label' => __( 'Projects', 'ks-portfolio' ),
|
||||
);
|
||||
}
|
||||
|
||||
/** Generates breadcrumbs for date-based archives.
|
||||
*
|
||||
* This method is responsible for creating breadcrumb navigation
|
||||
|
||||
@@ -154,6 +154,8 @@ function getTheTitle() {
|
||||
if ( is_home() || is_single() ) {
|
||||
$title = get_the_title( get_option( 'page_for_posts', true ) );
|
||||
} elseif ( is_archive() ) {
|
||||
// get_the_archive_title() is filtered below to drop the
|
||||
// "Project Type:" / "Category:" prefix — see stripArchiveTitlePrefix().
|
||||
$title = get_the_archive_title();
|
||||
} elseif ( is_search() ) {
|
||||
$title = sprintf(
|
||||
@@ -170,6 +172,26 @@ function getTheTitle() {
|
||||
return $title;
|
||||
}
|
||||
|
||||
/** Strip the "Project Type:" / "Category:" / "Tag:" prefix from archive titles.
|
||||
*
|
||||
* WordPress's get_the_archive_title() returns plain text with the taxonomy
|
||||
* or post-type label concatenated before a colon (e.g. "Project Type: Plugins",
|
||||
* "Category: WordPress", "Tag: php"). The site's hero partial uses the raw
|
||||
* archive title as its <h1>, and templates render their own context-appropriate
|
||||
* headers, so the prefix is redundant and visually noisy.
|
||||
*
|
||||
* @param string $title The full archive title, including prefix.
|
||||
* @return string The title with the prefix removed.
|
||||
*/
|
||||
function stripArchiveTitlePrefix( $title ) {
|
||||
// Drop the "<Label>: " prefix WordPress prepends. The label may be wrapped
|
||||
// in a <span> on some archive types (date archives) — strip that too.
|
||||
$title = (string) $title;
|
||||
$title = preg_replace( '/^\s*(?:<[^>]+>\s*)*[^:<]+:\s*/u', '', $title );
|
||||
return $title;
|
||||
}
|
||||
add_filter( 'get_the_archive_title', __NAMESPACE__ . '\\stripArchiveTitlePrefix' );
|
||||
|
||||
/** Wraps iframes and embed elements in a div with a specific class.
|
||||
*
|
||||
* This function searches for iframe and embed elements within the provided
|
||||
@@ -198,6 +220,26 @@ function divWrapper( $content ) {
|
||||
|
||||
add_filter( 'the_content', __NAMESPACE__ . '\\divWrapper' );
|
||||
|
||||
/** Strip the "<Label>: " prefix from the document title.
|
||||
*
|
||||
* The SEO Framework wipes out all `pre_get_document_title` filters at
|
||||
* `template_redirect` priority 20 and composes the title itself. To strip
|
||||
* the archive prefix consistently across <title>, og:title, and twitter:title
|
||||
* (which all derive from TSF's generated archive title list), hook the
|
||||
* archive-title-items filter and return the unprefixed title.
|
||||
*
|
||||
* @param array $items [title, prefix, title_without_prefix]
|
||||
* @return array The same items with the prefix removed from the title.
|
||||
*/
|
||||
function stripArchiveTitleItemsPrefix( $items ) {
|
||||
if ( ! is_array( $items ) || empty( $items[2] ) ) {
|
||||
return $items;
|
||||
}
|
||||
$items[0] = $items[2];
|
||||
return $items;
|
||||
}
|
||||
add_filter( 'the_seo_framework_generated_archive_title_items', __NAMESPACE__ . '\\stripArchiveTitleItemsPrefix' );
|
||||
|
||||
/** Selectively add sidebar to page.
|
||||
*
|
||||
* This function adds a custom field group to the WordPress editor for
|
||||
|
||||
@@ -207,12 +207,34 @@
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.archive-projects__back-link {
|
||||
color: var(--color-primary, #b6c4ff);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
/* Project-type taxonomy archive — the page hero renders the heading,
|
||||
* breadcrumbs handle navigation. */
|
||||
.tax-project { padding-top: 2rem; padding-bottom: 4rem; }
|
||||
|
||||
.tax-project__pagination {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.tax-project__empty {
|
||||
text-align: center;
|
||||
padding: 4rem 1rem;
|
||||
color: var(--color-on-surface-variant, #c5c5d3);
|
||||
}
|
||||
|
||||
.tax-project__empty-title {
|
||||
font-weight: 700;
|
||||
font-size: 1.5rem;
|
||||
margin: 0 0 0.5rem;
|
||||
color: var(--color-on-surface, #e3e1e9);
|
||||
}
|
||||
|
||||
.tax-project__pagination a,
|
||||
.tax-project__pagination span {
|
||||
background: var(--color-surface-container, #1e1f25);
|
||||
color: var(--color-on-surface, #e3e1e9);
|
||||
border: 1px solid var(--color-outline-variant, #444651);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.archive-projects__back-link:hover { text-decoration: underline; }
|
||||
|
||||
.single-project__hero {
|
||||
background: var(--color-surface-container-low, #1a1b21);
|
||||
|
||||
@@ -12,66 +12,57 @@
|
||||
|
||||
/* desktop */
|
||||
@media screen and (min-width: 62.5rem) {
|
||||
.nav-main .menu-vdi {
|
||||
@apply flex items-center justify-end p-0 m-0;
|
||||
.nav-main .menu-vdi {
|
||||
@apply flex items-center justify-end p-0 m-0;
|
||||
|
||||
>li {
|
||||
>a,
|
||||
>.menu-vdi__toggle {
|
||||
/* text*/
|
||||
@apply font-bold text-20px text-black hover:text-light no-underline leading-snug;
|
||||
/* spacing & display */
|
||||
@apply mx-4 p-0 no-underline;
|
||||
}
|
||||
>li {
|
||||
>a,
|
||||
>.menu-vdi__toggle {
|
||||
/* text*/
|
||||
@apply font-bold text-20px text-black hover:text-light no-underline leading-snug;
|
||||
/* spacing & display */
|
||||
@apply mx-4 p-0 no-underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 62.5rem) {
|
||||
.menu-vdi {
|
||||
.menu-vdi__toggle {
|
||||
@apply flex items-center gap-2;
|
||||
}
|
||||
.menu-vdi {
|
||||
.menu-vdi__toggle { @apply flex items-center gap-2; }
|
||||
|
||||
.menu-vdi__item--parent {
|
||||
@apply relative;
|
||||
}
|
||||
.menu-vdi__item--parent { @apply relative; }
|
||||
|
||||
.menu-vdi__submenu {
|
||||
@apply bg-white shadow-lg left-4 w-64 flex-col;
|
||||
top: calc(100% + 1rem);
|
||||
.menu-vdi__submenu {
|
||||
@apply bg-white shadow-lg left-4 w-64 flex-col;
|
||||
top: calc(100% + 1rem);
|
||||
|
||||
>li {
|
||||
@apply w-full;
|
||||
}
|
||||
>li { @apply w-full; }
|
||||
|
||||
.menu-vdi__item {
|
||||
/* text */
|
||||
@apply font-bold text-18px text-black hover:text-light no-underline leading-snug;
|
||||
/* spacing & display */
|
||||
@apply block w-full;
|
||||
/* interaction */
|
||||
@apply focus-visible:bg-secondary-200 hover:bg-secondary-200;
|
||||
.menu-vdi__item {
|
||||
/* text */
|
||||
@apply font-bold text-18px text-black! hover:text-light! no-underline leading-snug;
|
||||
/* spacing & display */
|
||||
@apply block w-full;
|
||||
/* interaction */
|
||||
@apply focus-visible:bg-secondary-200 hover:bg-secondary-200;
|
||||
|
||||
a {
|
||||
@apply block w-full;
|
||||
a { @apply block w-full; }
|
||||
}
|
||||
|
||||
a.menu-vdi__item,
|
||||
.menu-vdi__item a { @apply p-4; }
|
||||
}
|
||||
}
|
||||
|
||||
a.menu-vdi__item,
|
||||
.menu-vdi__item a {
|
||||
@apply p-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-main a,
|
||||
.nav-aux a {
|
||||
color: var(--color-on-surface, #e3e1e9);
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-main a:hover,
|
||||
.nav-aux a:hover {
|
||||
color: var(--color-primary, #b6c4ff);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Project Type Taxonomy Archive Template
|
||||
*
|
||||
* @package KsPortfolio
|
||||
*/
|
||||
|
||||
namespace KsPortfolio;
|
||||
|
||||
get_header();
|
||||
|
||||
$queried = get_queried_object();
|
||||
$term_name = $queried && ! is_wp_error( $queried ) ? $queried->name : __( 'Projects', 'ks-portfolio' );
|
||||
?>
|
||||
<section class="archive-projects">
|
||||
<div class="container mx-auto my-section">
|
||||
<header class="archive-projects__head">
|
||||
<h1 class="archive-projects__title"><?php echo esc_html( $term_name ); ?></h1>
|
||||
<p class="archive-projects__intro">
|
||||
<a class="archive-projects__back-link" href="<?php echo esc_url( get_post_type_archive_link( 'projects' ) ); ?>">
|
||||
<?php echo esc_html__( '← All Projects', 'ks-portfolio' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<?php if ( have_posts() ) : ?>
|
||||
<div class="archive-projects__grid grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<?php
|
||||
while ( have_posts() ) :
|
||||
the_post();
|
||||
get_template_part( 'views/components/project-card' );
|
||||
endwhile;
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div class="archive-projects__pagination">
|
||||
<?php
|
||||
the_posts_pagination(
|
||||
array(
|
||||
'mid_size' => 2,
|
||||
'prev_text' => '« ' . esc_html__( 'Previous', 'ks-portfolio' ),
|
||||
'next_text' => esc_html__( 'Next', 'ks-portfolio' ) . ' »',
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class="archive-projects__empty">
|
||||
<h2 class="archive-projects__empty-title"><?php echo esc_html__( 'Nothing here yet…', 'ks-portfolio' ); ?></h2>
|
||||
<p class="archive-projects__empty-desc"><?php echo esc_html__( 'No published projects found in this category.', 'ks-portfolio' ); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
Reference in New Issue
Block a user