import { LitElement, html, css } from 'lit';
import { property } from 'lit/decorators.js';
/**
* Content item for navigation
*/
export interface ContentNavigationItem {
title: string;
href?: string;
}
/**
* Event detail for navigate event
*/
export interface NavigateEventDetail {
direction: 'previous' | 'next' | 'parent';
title: string;
href?: string;
}
/**
* Custom event dispatched when navigation occurs
*/
export type NavigateEvent = CustomEvent;
/**
* Props interface for ContentPagination component
*/
export interface ContentPaginationProps {
/**
* Previous content item
*/
previous?: ContentNavigationItem;
/**
* Next content item
*/
next?: ContentNavigationItem;
/**
* Parent/overview content item
*/
parent?: ContentNavigationItem;
/**
* Alternative aria-label for the navigation
*/
ariaLabel?: string;
/**
* Whether to display borders around navigation links
*/
bordered?: boolean;
/**
* Event callback fired when navigation occurs (for SPA routing)
*/
onNavigate?: (event: NavigateEvent) => void;
}
/**
* ContentPagination component for navigating between content pages
*
* @fires {NavigateEvent} navigate - Fired when a navigation link is clicked
* @csspart ag-content-pagination-container - The outer container element
* @csspart ag-content-pagination-parent - The parent navigation item
* @csspart ag-content-pagination-nav - The previous/next navigation container
* @csspart ag-content-pagination-link - Individual navigation link/button
*
* @slot previous-icon - Icon for previous navigation (default: ←)
* @slot next-icon - Icon for next navigation (default: →)
* @slot parent-icon - Icon for parent navigation (default: ↑)
*
* @example
* ```html
*
* ```
*/
export class ContentPagination extends LitElement implements ContentPaginationProps {
@property({ type: Object, attribute: false })
declare previous?: ContentNavigationItem;
@property({ type: Object, attribute: false })
declare next?: ContentNavigationItem;
@property({ type: Object, attribute: false })
declare parent?: ContentNavigationItem;
@property({ type: String, attribute: 'aria-label' })
declare ariaLabel: string;
@property({ type: Boolean, reflect: true })
declare bordered: boolean;
@property({ attribute: false })
declare onNavigate?: (event: NavigateEvent) => void;
@property({ type: Boolean, reflect: true, attribute: 'has-parent' })
private _hasParentAndChild = false;
constructor() {
super();
this.ariaLabel = 'content navigation';
this.bordered = false;
}
willUpdate(changedProperties: Map) {
if (changedProperties.has('parent') || changedProperties.has('previous') || changedProperties.has('next')) {
this._hasParentAndChild = !!this.parent && (!!this.previous || !!this.next);
}
}
private _renderChevronIcon(rotationClass = '') {
return html``;
}
private _handleNavigate(
direction: 'previous' | 'next' | 'parent',
item: ContentNavigationItem | undefined,
event: MouseEvent
) {
if (!item) return; // safeguard for TypeScript & eslint
// If href is provided and no custom handler, let browser handle it
if (item.href && !this.onNavigate) {
return;
}
if (this.onNavigate) {
event.preventDefault();
}
const navigateEvent = new CustomEvent('navigate', {
detail: {
direction,
title: item.title,
href: item.href,
},
bubbles: true,
composed: true,
});
this.dispatchEvent(navigateEvent);
if (this.onNavigate) {
this.onNavigate(navigateEvent);
}
}
static styles = css`
:host {
display: block;
width: 100%;
}
.rotate-180 {
transform: rotate(180deg);
}
.content-pagination-container {
display: flex;
flex-direction: column;
gap: var(--ag-space-1);
}
.content-pagination-parent {
display: flex;
align-items: center;
padding: var(--ag-space-2) var(--ag-space-5);
border-radius: var(--ag-radius-md);
}
.content-pagination-parent-link {
display: flex;
align-items: center;
gap: var(--ag-space-1);
color: var(--ag-text-primary);
text-decoration: none;
font-size: var(--ag-font-size-sm);
font-weight: 500;
cursor: pointer;
background: none;
border: none;
font-family: inherit;
}
.content-pagination-parent-link:hover {
color: var(--ag-primary);
}
.content-pagination-parent-link:focus-visible {
outline: var(--ag-focus-width) solid rgba(var(--ag-focus), 0.5);
outline-offset: var(--ag-focus-offset);
border-radius: var(--ag-radius-sm);
}
.content-pagination-nav {
display: flex;
justify-content: space-between;
gap: var(--ag-space-4);
padding-block-start: var(--ag-space-2);
border-top: 0px solid transparent;
}
.content-pagination-nav-with-parent {
border-top: 1px solid var(--ag-border);
}
.content-pagination-item {
display: flex;
flex: 0 1 auto;
min-inline-size: 0;
max-inline-size: 45%;
}
.content-pagination-item-previous {
justify-content: flex-start;
}
.content-pagination-item-next {
justify-content: flex-end;
margin-inline-start: auto;
}
.content-pagination-link {
display: flex;
align-items: center;
gap: var(--ag-space-2, 0.5rem);
padding: var(--ag-space-3) var(--ag-space-4);
border-radius: var(--ag-radius-md);
background-color: var(--ag-background-primary);
color: var(--ag-text-primary);
text-decoration: none;
font-size: var(--ag-font-size-base);
font-weight: 500;
cursor: pointer;
transition: all 0.15s ease-in-out;
font-family: inherit;
min-width: 0;
}
:host([bordered]) .content-pagination-link {
border: 1px solid var(--ag-border);
}
.content-pagination-link:hover {
background-color: var(--ag-background-secondary);
color: var(--ag-primary);
}
.content-pagination-link:focus-visible {
outline: var(--ag-focus-width) solid rgba(var(--ag-focus), 0.5);
outline-offset: var(--ag-focus-offset);
}
.content-pagination-link:active {
transform: translateY(1px);
}
button.content-pagination-link {
background-color: transparent;
border: 0;
}
:host([bordered]) button.content-pagination-link {
border: 1px solid var(--ag-border);
}
.content-pagination-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.content-pagination-icon {
flex-shrink: 0;
font-size: 1.25em;
line-height: 1;
}
/* Responsive: stack on very small screens */
@media (max-width: 640px) {
.content-pagination-nav {
flex-direction: column;
gap: var(--ag-space-3);
}
.content-pagination-item {
max-inline-size: 100%;
}
.content-pagination-item-next {
margin-inline-start: 0;
}
}
@media (prefers-reduced-motion) {
.content-pagination-link {
transition: none;
}
.content-pagination-link:active {
transform: none;
}
}
`;
render() {
return html`
`;
}
}