Add Splide

commit e76d41be04166ad5a9a1a17d327a9b47d46149f7
Merge: 9490b992 48b6cf37
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Fri Sep 6 01:26:29 2024 +0200

    Merge branch 'main' into splide

commit 9490b9925ff2ff78f3057a2762aa41aa61028a68
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Fri Sep 6 00:02:52 2024 +0200

    Add height and aspect_ratio params to showcase

commit 8043e35abb95d04192ba5a9399480a37db647b27
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 23:02:12 2024 +0200

    Add Splide to showcase shortcode

commit bc7344d3a89eea93a0727b88c7d7998a2f73b54b
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 22:52:26 2024 +0200

    Start documentation

commit 3e3183e5ec3da66c585f022af1214b3690ad84f6
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 22:13:41 2024 +0200

    Add option to enable Splide globally

commit 60935c24e647889beab8b158bb50aa1f4948e4da
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 20:48:44 2024 +0200

    Elementor compatibility

commit 1f7c0ca40289597af0b6a250c62ed22621a9668a
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 20:39:08 2024 +0200

    Optimize Splide for paginated article cards

commit 911c0f6f20d0c3a5bddda280618064eb1309ac5f
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 20:24:52 2024 +0200

    Turn off arrows by default

commit 567205b42031b9e075060c208d1fb93507c7acfa
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 14:05:46 2024 +0200

    Move SCSS to own file

commit 5a91f1dcf0982a78e22362262d4b982784cfdd21
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 13:57:22 2024 +0200

    Update style

commit e3c920ab33c5d6065fd7b8a7f2211d8809630df3
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 11:58:52 2024 +0200

    Add Splide to other shortcodes

commit 7c2275a0c336e7ca7072165d633dd3fbc8a19524
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Thu Sep 5 11:55:39 2024 +0200

    Update style

commit 68bff5aef85af82f1a844de293f443172a5e5d1e
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Wed Sep 4 19:16:55 2024 +0200

    Improve error handling of invalid JSON

commit ce9159425a6d6674cdef7d8f356724c79db3130b
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Wed Sep 4 19:05:01 2024 +0200

    Improve Splide loading and initialization

commit 263848a53ddfb77980f88b5b5f973311d792e712
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Wed Sep 4 16:39:18 2024 +0200

    Improve JSON preparation

commit 8c39131008a50119bd8666b558f01354140c0b00
Merge: d4373db4 57638cdc
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Wed Sep 4 14:19:28 2024 +0200

    Merge branch 'main' into splide

commit d4373db473e80b05c52565f25ba13034eb81d648
Author: Tetrakern <26898880+Tetrakern@users.noreply.github.com>
Date:   Wed Sep 4 13:48:00 2024 +0200

    Prototype
This commit is contained in:
Tetrakern 2024-09-06 01:26:42 +02:00
parent 48b6cf3719
commit a39a4ceb9b
34 changed files with 2961 additions and 2183 deletions

View File

@ -1327,6 +1327,8 @@ Renders dynamic grid of thumbnails with title, showing the latest eight posts of
* **exclude_cat_ids:** Comma-separated list of category IDs to exclude. * **exclude_cat_ids:** Comma-separated list of category IDs to exclude.
* **exclude_tag_ids:** Comma-separated list of tag IDs to exclude. * **exclude_tag_ids:** Comma-separated list of tag IDs to exclude.
* **no_cap:** Set `true` if you want to hide the caption. * **no_cap:** Set `true` if you want to hide the caption.
* **aspect_ratio:** CSS [aspect-ratio](https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio) value for the item (X/Y). Superseded by `height`.
* **height:** Override the item height.
* **class:** Additional CSS classes, separated by whitespace. * **class:** Additional CSS classes, separated by whitespace.
``` ```
@ -1339,6 +1341,18 @@ Renders dynamic grid of thumbnails with title, showing the latest eight posts of
![Showcase](repo/assets/shortcode_example_showcase.jpg?raw=true) ![Showcase](repo/assets/shortcode_example_showcase.jpg?raw=true)
### Slider
Any shortcode with the `splide` parameter listed can be turned into a slider. [Splide](https://splidejs.com/) is a flexible and lightweight slider that comes with [many options](https://splidejs.com/guides/options/) for customization, although applying them may be challenging if you are not familiar with JSONs. You can look up the details yourself.
The `splide` parameter only accepts JSON strings, such as `splide="{'type':'loop','perPage':3}"`. Note that you need to use single straight quotes due to the shortcode syntax. If there is even a minor error, the JSON will be rejected with a note, and the shortcode will default to its standard layout. Not all parameter combinations have been tested with Splide, so custom CSS may be required.
If you do not want to initialize a slider on page load, you can add the `no-auto-splide` class via the `class` parameter in the shortcode or custom HTML (where the `splide` class is). Normally, Splides assets are only enqueued when a shortcode with the necessary parameter is found in the post content, but you can enable Splide globally under **Fictioneer > General > Compatibility**.
### Sidebar ### Sidebar
Renders the theme sidebar (not displayed anywhere by default). Requires the "Disable all widgets" theme setting to be off. Note that the sidebar has next to no styling. Renders the theme sidebar (not displayed anywhere by default). Requires the "Disable all widgets" theme setting to be off. Note that the sidebar has next to no styling.

View File

@ -640,6 +640,18 @@
"oF" : 1, "oF" : 1,
"pg" : 0 "pg" : 0
}, },
"\/css\/splide.css" : {
"aP" : 1,
"bl" : 0,
"ci" : 0,
"co" : 0,
"ft" : 16,
"ma" : 0,
"oA" : 0,
"oAP" : "\/css\/splide-min.css",
"oF" : 0,
"pg" : 0
},
"\/css\/story.css" : { "\/css\/story.css" : {
"aP" : 1, "aP" : 1,
"bl" : 0, "bl" : 0,
@ -1796,6 +1808,17 @@
"sC" : 3, "sC" : 3,
"tS" : 0 "tS" : 0
}, },
"\/js\/splide.min.js" : {
"bF" : 0,
"ft" : 64,
"ma" : 0,
"mi" : 1,
"oA" : 0,
"oAP" : "\/js\/splide.min.min.js",
"oF" : 0,
"sC" : 3,
"tS" : 0
},
"\/js\/story.min.js" : { "\/js\/story.min.js" : {
"bF" : 0, "bF" : 0,
"ft" : 64, "ft" : 64,
@ -3983,6 +4006,21 @@
"pg" : 0, "pg" : 0,
"sct" : 0 "sct" : 0
}, },
"\/src\/scss\/splide.scss" : {
"aP" : 0,
"bl" : 0,
"co" : 0,
"dP" : 10,
"ec" : 0,
"ft" : 4,
"ma" : 0,
"oA" : 0,
"oAP" : "\/css\/splide.css",
"oF" : 2,
"oS" : 3,
"pg" : 0,
"sct" : 0
},
"\/src\/scss\/story.scss" : { "\/src\/scss\/story.scss" : {
"aP" : 0, "aP" : 0,
"bl" : 0, "bl" : 0,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
css/splide.css Normal file

File diff suppressed because one or more lines are too long

View File

@ -215,7 +215,8 @@ function fictioneer_get_default_shortcode_args( $attr, $def_count = -1 ) {
'footer_rating' => filter_var( $attr['footer_rating'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'footer_rating' => filter_var( $attr['footer_rating'] ?? 1, FILTER_VALIDATE_BOOLEAN ),
'classes' => esc_attr( wp_strip_all_tags( $attr['classes'] ?? $attr['class'] ?? '' ) ), 'classes' => esc_attr( wp_strip_all_tags( $attr['classes'] ?? $attr['class'] ?? '' ) ),
'infobox' => filter_var( $attr['infobox'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'infobox' => filter_var( $attr['infobox'] ?? 1, FILTER_VALIDATE_BOOLEAN ),
'source' => filter_var( $attr['source'] ?? 1, FILTER_VALIDATE_BOOLEAN ) 'source' => filter_var( $attr['source'] ?? 1, FILTER_VALIDATE_BOOLEAN ),
'splide' => sanitize_text_field( $attr['splide'] ?? '' )
); );
//--- Fixes ------------------------------------------------------------------ //--- Fixes ------------------------------------------------------------------
@ -225,6 +226,24 @@ function fictioneer_get_default_shortcode_args( $attr, $def_count = -1 ) {
$args['count'] = count( $args['post_ids'] ); $args['count'] = count( $args['post_ids'] );
} }
// Prepare Splide JSON
if ( ! empty( $args['splide'] ) ) {
$args['splide'] = str_replace( "'", '"', $args['splide'] );
if ( ! fictioneer_is_valid_json( $args['splide'] ) ) {
$args['splide'] = false;
} else {
$splide = json_decode( $args['splide'], true );
// Turn arrows are off by default
if ( ! preg_match( '/"arrows"\s*:\s*true/', $args['splide'] ) ) {
$splide['arrows'] = false;
}
$args['splide'] = json_encode( $splide );
}
}
//--- Finish ----------------------------------------------------------------- //--- Finish -----------------------------------------------------------------
return $args; return $args;
@ -381,10 +400,12 @@ function fictioneer_get_shortcode_tax_query( $args ) {
* @param string|null $attr['rel'] Optional. Relationship between taxonomies. Default 'AND'. * @param string|null $attr['rel'] Optional. Relationship between taxonomies. Default 'AND'.
* @param string|null $attr['vertical'] Optional. Whether to show the vertical variant. * @param string|null $attr['vertical'] Optional. Whether to show the vertical variant.
* @param string|null $attr['seamless'] Optional. Whether to render the image seamless. Default false (Customizer). * @param string|null $attr['seamless'] Optional. Whether to render the image seamless. Default false (Customizer).
* @param string|null $attr['aspect_ratio'] Optional. Aspect ratio for the image. Only with vertical. * @param string|null $attr['aspect_ratio'] Optional. Aspect ratio of the item. Supersedes by height. Default empty.
* @param string|null $attr['height'] Optional. Override the item height. Default empty.
* @param string|null $attr['lightbox'] Optional. Whether the thumbnail is opened in the lightbox. Default true. * @param string|null $attr['lightbox'] Optional. Whether the thumbnail is opened in the lightbox. Default true.
* @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer). * @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer).
* @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace.
* @param string|null $args['splide'] Configuration JSON for the Splide slider. Default empty.
* *
* @return string The captured shortcode HTML. * @return string The captured shortcode HTML.
*/ */
@ -398,6 +419,9 @@ function fictioneer_shortcode_showcase( $attr ) {
// Defaults // Defaults
$args = fictioneer_get_default_shortcode_args( $attr, 8 ); $args = fictioneer_get_default_shortcode_args( $attr, 8 );
// Height
$args['height'] = sanitize_text_field( $attr['height'] ?? '' );
// Specifics // Specifics
$args['no_cap'] = filter_var( $attr['no_cap'] ?? 0, FILTER_VALIDATE_BOOLEAN ); $args['no_cap'] = filter_var( $attr['no_cap'] ?? 0, FILTER_VALIDATE_BOOLEAN );
@ -421,6 +445,11 @@ function fictioneer_shortcode_showcase( $attr ) {
return ''; return '';
} }
// Extra classes
if ( $args['splide'] ?? 0 ) {
$args['classes'] .= ' splide';
}
// Transient? // Transient?
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
$base = serialize( $args ) . serialize( $attr ); $base = serialize( $args ) . serialize( $attr );
@ -440,6 +469,10 @@ function fictioneer_shortcode_showcase( $attr ) {
$html = fictioneer_minify_html( ob_get_clean() ); $html = fictioneer_minify_html( ob_get_clean() );
if ( ( $args['splide'] ?? 0 ) && strpos( $args['classes'], 'no-auto-splide' ) === false ) {
$html .= '<script class="temp-script">document.querySelectorAll(".splide:not(.no-auto-splide, .is-initialized)").forEach(e=>{"undefined"!=typeof Splide&&new Splide(e).mount()}),document.querySelector(".temp-script").remove();</script>';
}
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION );
} }
@ -491,6 +524,7 @@ add_shortcode( 'fictioneer_showcase', 'fictioneer_shortcode_showcase' );
* @param string|null $attr['footer_status'] Optional. Whether to show the chapter status. Default true. * @param string|null $attr['footer_status'] Optional. Whether to show the chapter status. Default true.
* @param string|null $attr['footer_rating'] Optional. Whether to show the story/chapter age rating. Default true. * @param string|null $attr['footer_rating'] Optional. Whether to show the story/chapter age rating. Default true.
* @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace.
* @param string|null $args['splide'] Configuration JSON for the Splide slider. Default empty.
* *
* @return string The captured shortcode HTML. * @return string The captured shortcode HTML.
*/ */
@ -506,6 +540,11 @@ function fictioneer_shortcode_latest_chapters( $attr ) {
// Type // Type
$type = sanitize_text_field( $attr['type'] ?? 'default' ); $type = sanitize_text_field( $attr['type'] ?? 'default' );
// Extra classes
if ( $args['splide'] ?? 0 ) {
$args['classes'] .= ' splide';
}
// Transient? // Transient?
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
$base = serialize( $args ) . serialize( $attr ); $base = serialize( $args ) . serialize( $attr );
@ -534,6 +573,10 @@ function fictioneer_shortcode_latest_chapters( $attr ) {
$html = fictioneer_minify_html( ob_get_clean() ); $html = fictioneer_minify_html( ob_get_clean() );
if ( ( $args['splide'] ?? 0 ) && strpos( $args['classes'], 'no-auto-splide' ) === false ) {
$html .= '<script class="temp-script">document.querySelectorAll(".splide:not(.no-auto-splide, .is-initialized)").forEach(e=>{"undefined"!=typeof Splide&&new Splide(e).mount()}),document.querySelector(".temp-script").remove();</script>';
}
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION );
} }
@ -588,6 +631,7 @@ add_shortcode( 'fictioneer_chapter_cards', 'fictioneer_shortcode_latest_chapters
* @param string|null $attr['footer_status'] Optional. Whether to show the story status. Default true. * @param string|null $attr['footer_status'] Optional. Whether to show the story status. Default true.
* @param string|null $attr['footer_rating'] Optional. Whether to show the story age rating. Default true. * @param string|null $attr['footer_rating'] Optional. Whether to show the story age rating. Default true.
* @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace.
* @param string|null $args['splide'] Configuration JSON for the Splide slider. Default empty.
* *
* @return string The captured shortcode HTML. * @return string The captured shortcode HTML.
*/ */
@ -603,6 +647,11 @@ function fictioneer_shortcode_latest_stories( $attr ) {
$args['terms'] = fictioneer_sanitize_query_var( $attr['terms'] ?? 0, ['inline', 'pills', 'none', 'false'], 'inline' ); $args['terms'] = fictioneer_sanitize_query_var( $attr['terms'] ?? 0, ['inline', 'pills', 'none', 'false'], 'inline' );
$args['max_terms'] = absint( ( $attr['max_terms'] ?? 10 ) ?: 10 ); $args['max_terms'] = absint( ( $attr['max_terms'] ?? 10 ) ?: 10 );
// Extra classes
if ( $args['splide'] ?? 0 ) {
$args['classes'] .= ' splide';
}
// Transient? // Transient?
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
$base = serialize( $args ) . serialize( $attr ); $base = serialize( $args ) . serialize( $attr );
@ -630,6 +679,10 @@ function fictioneer_shortcode_latest_stories( $attr ) {
$html = fictioneer_minify_html( ob_get_clean() ); $html = fictioneer_minify_html( ob_get_clean() );
if ( ( $args['splide'] ?? 0 ) && strpos( $args['classes'], 'no-auto-splide' ) === false ) {
$html .= '<script class="temp-script">document.querySelectorAll(".splide:not(.no-auto-splide, .is-initialized)").forEach(e=>{"undefined"!=typeof Splide&&new Splide(e).mount()}),document.querySelector(".temp-script").remove();</script>';
}
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION );
} }
@ -686,6 +739,7 @@ add_shortcode( 'fictioneer_story_cards', 'fictioneer_shortcode_latest_stories' )
* @param string|null $attr['footer_status'] Optional. Whether to show the story status. Default true. * @param string|null $attr['footer_status'] Optional. Whether to show the story status. Default true.
* @param string|null $attr['footer_rating'] Optional. Whether to show the story/chapter age rating. Default true. * @param string|null $attr['footer_rating'] Optional. Whether to show the story/chapter age rating. Default true.
* @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace.
* @param string|null $args['splide'] Configuration JSON for the Splide slider. Default empty.
* *
* @return string The captured shortcode HTML. * @return string The captured shortcode HTML.
*/ */
@ -702,6 +756,11 @@ function fictioneer_shortcode_latest_story_updates( $attr ) {
$args['terms'] = fictioneer_sanitize_query_var( $attr['terms'] ?? 0, ['inline', 'pills', 'none', 'false'], 'inline' ); $args['terms'] = fictioneer_sanitize_query_var( $attr['terms'] ?? 0, ['inline', 'pills', 'none', 'false'], 'inline' );
$args['max_terms'] = absint( ( $attr['max_terms'] ?? 10 ) ?: 10 ); $args['max_terms'] = absint( ( $attr['max_terms'] ?? 10 ) ?: 10 );
// Extra classes
if ( $args['splide'] ?? 0 ) {
$args['classes'] .= ' splide';
}
// Transient? // Transient?
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
$base = serialize( $args ) . serialize( $attr ); $base = serialize( $args ) . serialize( $attr );
@ -729,6 +788,10 @@ function fictioneer_shortcode_latest_story_updates( $attr ) {
$html = fictioneer_minify_html( ob_get_clean() ); $html = fictioneer_minify_html( ob_get_clean() );
if ( ( $args['splide'] ?? 0 ) && strpos( $args['classes'], 'no-auto-splide' ) === false ) {
$html .= '<script class="temp-script">document.querySelectorAll(".splide:not(.no-auto-splide, .is-initialized)").forEach(e=>{"undefined"!=typeof Splide&&new Splide(e).mount()}),document.querySelector(".temp-script").remove();</script>';
}
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION );
} }
@ -772,6 +835,7 @@ add_shortcode( 'fictioneer_update_cards', 'fictioneer_shortcode_latest_story_upd
* @param string|null $attr['lightbox'] Optional. Whether the thumbnail is opened in the lightbox. Default true. * @param string|null $attr['lightbox'] Optional. Whether the thumbnail is opened in the lightbox. Default true.
* @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer). * @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer).
* @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace.
* @param string|null $args['splide'] Configuration JSON for the Splide slider. Default empty.
* *
* @return string The captured shortcode HTML. * @return string The captured shortcode HTML.
*/ */
@ -787,6 +851,11 @@ function fictioneer_shortcode_latest_recommendations( $attr ) {
$args['terms'] = fictioneer_sanitize_query_var( $attr['terms'] ?? 0, ['inline', 'pills', 'none', 'false'], 'inline' ); $args['terms'] = fictioneer_sanitize_query_var( $attr['terms'] ?? 0, ['inline', 'pills', 'none', 'false'], 'inline' );
$args['max_terms'] = absint( ( $attr['max_terms'] ?? 10 ) ?: 10 ); $args['max_terms'] = absint( ( $attr['max_terms'] ?? 10 ) ?: 10 );
// Extra classes
if ( $args['splide'] ?? 0 ) {
$args['classes'] .= ' splide';
}
// Transient? // Transient?
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
$base = serialize( $args ) . serialize( $attr ); $base = serialize( $args ) . serialize( $attr );
@ -811,6 +880,10 @@ function fictioneer_shortcode_latest_recommendations( $attr ) {
$html = fictioneer_minify_html( ob_get_clean() ); $html = fictioneer_minify_html( ob_get_clean() );
if ( ( $args['splide'] ?? 0 ) && strpos( $args['classes'], 'no-auto-splide' ) === false ) {
$html .= '<script class="temp-script">document.querySelectorAll(".splide:not(.no-auto-splide, .is-initialized)").forEach(e=>{"undefined"!=typeof Splide&&new Splide(e).mount()}),document.querySelector(".temp-script").remove();</script>';
}
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION );
} }
@ -1608,6 +1681,7 @@ add_shortcode( 'fictioneer_blog', 'fictioneer_shortcode_blog' );
* @param string|null $attr['footer_date'] Optional. Whether to show the post date. Default true. * @param string|null $attr['footer_date'] Optional. Whether to show the post date. Default true.
* @param string|null $attr['footer_comments'] Optional. Whether to show the post comment count. Default true. * @param string|null $attr['footer_comments'] Optional. Whether to show the post comment count. Default true.
* @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace.
* @param string|null $args['splide'] Configuration JSON for the Splide slider. Default empty.
* *
* @return string The captured shortcode HTML. * @return string The captured shortcode HTML.
*/ */
@ -1657,6 +1731,11 @@ function fictioneer_shortcode_article_cards( $attr ) {
// ... add to args // ... add to args
$args['post_type'] = $query_post_types; $args['post_type'] = $query_post_types;
// Extra classes
if ( $args['splide'] ?? 0 ) {
$args['classes'] .= ' splide';
}
// Transient? // Transient?
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
$base = serialize( $args ) . serialize( $attr ); $base = serialize( $args ) . serialize( $attr );
@ -1675,6 +1754,10 @@ function fictioneer_shortcode_article_cards( $attr ) {
$html = fictioneer_minify_html( ob_get_clean() ); $html = fictioneer_minify_html( ob_get_clean() );
if ( ( $args['splide'] ?? 0 ) && strpos( $args['classes'], 'no-auto-splide' ) === false ) {
$html .= '<script class="temp-script">document.querySelectorAll(".splide:not(.no-auto-splide, .is-initialized)").forEach(e=>{"undefined"!=typeof Splide&&new Splide(e).mount()}),document.querySelector(".temp-script").remove();</script>';
}
if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) {
set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION );
} }

View File

@ -940,7 +940,7 @@ function fictioneer_style_queue() {
// Shortcodes // Shortcodes
if ( if (
( $post && preg_match( '/\[fictioneer_latest_[^\]]*type="list"[^\]]*\]/', $post->post_content ) ) || ( $post && preg_match( '/\[fictioneer_latest_[^\]]*type="list"[^\]]*\]/', $post->post_content ) ) ||
is_admin() // Accounts for page editors like Elementor strpos( $_SERVER['REQUEST_URI'], 'elementor' ) !== false // Accounts for page editors like Elementor
) { ) {
wp_enqueue_style( wp_enqueue_style(
'fictioneer-post-list', 'fictioneer-post-list',
@ -969,6 +969,20 @@ function fictioneer_style_queue() {
$cache_bust $cache_bust
); );
} }
// Enqueue Splide CSS
if (
get_option( 'fictioneer_enable_global_splide' ) ||
( $post && preg_match( '/\[fictioneer_[a-zA-Z0-9_]*[^\]]*splide="([^"]+)"[^\]]*\]/', $post->post_content ) ) ||
strpos( $_SERVER['REQUEST_URI'], 'elementor' ) !== false // Accounts for page editors like Elementor
) {
wp_enqueue_style(
'fictioneer-splide',
get_template_directory_uri() . '/css/splide.css',
[],
$cache_bust
);
}
} }
add_action( 'wp_enqueue_scripts', 'fictioneer_style_queue' ); add_action( 'wp_enqueue_scripts', 'fictioneer_style_queue' );
@ -1173,6 +1187,8 @@ function fictioneer_build_dynamic_scripts() {
*/ */
function fictioneer_add_custom_scripts() { function fictioneer_add_custom_scripts() {
global $post;
// Setup // Setup
$post_type = get_post_type(); $post_type = get_post_type();
$cache_bust = fictioneer_get_cache_bust(); $cache_bust = fictioneer_get_cache_bust();
@ -1350,6 +1366,15 @@ function fictioneer_add_custom_scripts() {
wp_enqueue_script( 'comment-reply' ); wp_enqueue_script( 'comment-reply' );
} }
// Enqueue Splide
if (
get_option( 'fictioneer_enable_global_splide' ) ||
( $post && preg_match( '/\[fictioneer_[a-zA-Z0-9_]*[^\]]*splide="([^"]+)"[^\]]*\]/', $post->post_content ) ) ||
strpos( $_SERVER['REQUEST_URI'], 'elementor' ) !== false // Accounts for page editors like Elementor
) {
wp_enqueue_script( 'fictioneer-splide', get_template_directory_uri() . '/js/splide.min.js', [], $cache_bust, false );
}
// DEV Utilities // DEV Utilities
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
wp_register_script( 'fictioneer-dev-scripts', get_template_directory_uri() . '/js/dev-tools.min.js', [], $cache_bust, $strategy ); wp_register_script( 'fictioneer-dev-scripts', get_template_directory_uri() . '/js/dev-tools.min.js', [], $cache_bust, $strategy );
@ -1491,7 +1516,23 @@ if ( ! is_admin() && ! get_option( 'fictioneer_enable_jquery_migrate' ) ) {
function fictioneer_ao_exclude_css( $exclude ) { function fictioneer_ao_exclude_css( $exclude ) {
return $exclude . ', fonts-base.css, fonts-full.css, bundled-fonts.css'; return $exclude . ', fonts-base.css, fonts-full.css, bundled-fonts.css';
} }
add_filter( 'autoptimize_filter_css_exclude', 'fictioneer_ao_exclude_css', 10, 1 ); add_filter( 'autoptimize_filter_css_exclude', 'fictioneer_ao_exclude_css' );
/**
* Exclude scripts from Autoptimize (if installed)
*
* @since 5.23.1
* @link https://github.com/wp-plugins/autoptimize
*
* @param string $exclude List of current excludes.
*
* @return string The updated exclusion string.
*/
function fictioneer_ao_exclude_js( $exclude ) {
return $exclude . ', fictioneer/js/splide.min.js';
}
add_filter( 'autoptimize_filter_js_exclude', 'fictioneer_ao_exclude_js' );
// ============================================================================= // =============================================================================
// OUTPUT HEAD FONTS // OUTPUT HEAD FONTS

View File

@ -695,6 +695,12 @@ define( 'FICTIONEER_OPTIONS', array(
'group' => 'fictioneer-settings-general-group', 'group' => 'fictioneer-settings-general-group',
'sanitize_callback' => 'fictioneer_sanitize_checkbox', 'sanitize_callback' => 'fictioneer_sanitize_checkbox',
'default' => 0 'default' => 0
),
'fictioneer_enable_global_splide' => array(
'name' => 'fictioneer_enable_global_splide',
'group' => 'fictioneer-settings-general-group',
'sanitize_callback' => 'fictioneer_sanitize_checkbox',
'default' => 0
) )
), ),
'integers' => array( 'integers' => array(
@ -1162,6 +1168,7 @@ function fictioneer_get_option_label( $option ) {
'fictioneer_enable_story_card_caching' => __( 'Enable caching of story cards', 'fictioneer' ), 'fictioneer_enable_story_card_caching' => __( 'Enable caching of story cards', 'fictioneer' ),
'fictioneer_enable_query_result_caching' => __( 'Enable caching of large query results', 'fictioneer' ), 'fictioneer_enable_query_result_caching' => __( 'Enable caching of large query results', 'fictioneer' ),
'fictioneer_allow_rest_save_actions' => __( 'Allow REST requests to trigger save actions', 'fictioneer' ), 'fictioneer_allow_rest_save_actions' => __( 'Allow REST requests to trigger save actions', 'fictioneer' ),
'fictioneer_enable_global_splide' => __( 'Enable Splide slider globally', 'fictioneer' ),
); );
} }

View File

@ -1319,6 +1319,20 @@ $images = get_template_directory_uri() . '/img/documentation/';
?> ?>
</div> </div>
<div class="fictioneer-card__row">
<?php
fictioneer_settings_label_checkbox(
'fictioneer_enable_global_splide',
__( 'Enable Splide slider globally', 'fictioneer' ),
__( 'Instead of limiting it to shortcodes.', 'fictioneer' ),
sprintf(
__( 'Normally, the <a href="%s" target="_blank" rel="noopener noreferrer">Splide slider</a> script and style are only loaded when a shortcode with the required parameter is found in the post content. However, if you want to use Splide in other ways, you can enable the assets globally here.', 'fictioneer' ),
'https://splidejs.com/guides/options/'
)
);
?>
</div>
<div class="fictioneer-card__row"> <div class="fictioneer-card__row">
<?php <?php
fictioneer_settings_label_checkbox( fictioneer_settings_label_checkbox(

File diff suppressed because one or more lines are too long

8
js/complete.min.js vendored

File diff suppressed because one or more lines are too long

9
js/splide.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -33,6 +33,7 @@
* @internal $args['footer_date'] Whether to show the post date. Default true. * @internal $args['footer_date'] Whether to show the post date. Default true.
* @internal $args['footer_comments'] Whether to show the post comment count. Default true. * @internal $args['footer_comments'] Whether to show the post comment count. Default true.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -40,6 +41,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$show_terms = ! in_array( $args['terms'], ['none', 'false'] ); $show_terms = ! in_array( $args['terms'], ['none', 'false'] );
// Arguments // Arguments
@ -122,191 +124,214 @@ $pag_args = array(
'add_fragment' => "#{$unique_id}" 'add_fragment' => "#{$unique_id}"
); );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section id="<?php echo $unique_id; ?>" class="scroll-margin-top article-card-block <?php echo esc_attr( $args['classes'] ); ?>"> <section id="<?php echo $unique_id; ?>" class="scroll-margin-top article-card-block <?php echo esc_attr( $args['classes'] ); ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<?php if ( $query->have_posts() ) : ?> if ( $splide ) {
echo '<div class="splide__track">';
}
?>
<ul class="grid-columns _collapse-on-mobile"> <?php if ( $query->have_posts() ) : ?>
<?php
while ( $query->have_posts() ) {
$query->the_post();
// Setup <ul class="grid-columns _collapse-on-mobile <?php if ( $splide ) { echo 'splide__list'; } ?>">
$post_id = $post->ID; <?php
$story_id = ( $post->post_type === 'fcn_story' ) ? $post_id : null; while ( $query->have_posts() ) {
$title = fictioneer_get_safe_title( $post_id, 'card-article' ); $query->the_post();
$permalink = get_permalink();
$categories = $show_terms ? get_the_terms( $post_id, 'category' ) : [];
$tags = $show_terms ? get_the_tags() : [];
$fandoms = $show_terms ? get_the_terms( $post, 'fcn_fandom' ) : [];
$characters = $show_terms ? get_the_terms( $post, 'fcn_character' ) : [];
$genres = $show_terms ? get_the_terms( $post, 'fcn_genre' ) : [];
$card_classes = [];
// Chapter story? // Setup
if ( $post->post_type === 'fcn_chapter' ) { $post_id = $post->ID;
$story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true ); $story_id = ( $post->post_type === 'fcn_story' ) ? $post_id : null;
} $title = fictioneer_get_safe_title( $post_id, 'card-article' );
$permalink = get_permalink();
$categories = $show_terms ? get_the_terms( $post_id, 'category' ) : [];
$tags = $show_terms ? get_the_tags() : [];
$fandoms = $show_terms ? get_the_terms( $post, 'fcn_fandom' ) : [];
$characters = $show_terms ? get_the_terms( $post, 'fcn_character' ) : [];
$genres = $show_terms ? get_the_terms( $post, 'fcn_genre' ) : [];
$card_classes = [];
// Extra classes // Chapter story?
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) { if ( $post->post_type === 'fcn_chapter' ) {
$card_classes[] = '_' . get_theme_mod( 'card_style' ); $story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true );
} }
if ( $args['seamless'] ) { // Extra classes
$card_classes[] = '_seamless'; if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
} $card_classes[] = '_' . get_theme_mod( 'card_style' );
}
if ( ! $show_terms ) { if ( $args['seamless'] ) {
$card_classes[] = '_no-tax'; $card_classes[] = '_seamless';
} }
if ( ! $args['footer'] ) { if ( ! $show_terms ) {
$card_classes[] = '_no-footer'; $card_classes[] = '_no-tax';
} }
if ( ! $args['footer_author'] ) { if ( ! $args['footer'] ) {
$card_classes[] = '_no-footer-author'; $card_classes[] = '_no-footer';
} }
if ( ! $args['footer_date'] ) { if ( ! $args['footer_author'] ) {
$card_classes[] = '_no-footer-date'; $card_classes[] = '_no-footer-author';
} }
if ( ! $args['footer_comments'] ) { if ( ! $args['footer_date'] ) {
$card_classes[] = '_no-footer-comments'; $card_classes[] = '_no-footer-date';
} }
// Card attributes if ( ! $args['footer_comments'] ) {
$attributes = []; $card_classes[] = '_no-footer-comments';
}
if ( $args['aspect_ratio'] ) { if ( $splide ) {
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio']; $card_classes[] = 'splide__slide';
} }
$attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-article-cards' ); // Card attributes
$attributes = [];
$card_attributes = ''; if ( $args['aspect_ratio'] ) {
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
}
foreach ( $attributes as $key => $value ) { $attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-article-cards' );
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
// Start HTML ---> ?> $card_attributes = '';
<li class="post-<?php echo $post_id; ?> card _article <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
<article class="card__body _article polygon">
<div class="card__main _article"> foreach ( $attributes as $key => $value ) {
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
<?php // Start HTML ---> ?>
if ( $args['thumbnail'] ) { <li class="post-<?php echo $post_id; ?> card _article <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
fictioneer_render_thumbnail( <article class="card__body _article polygon">
array(
'post_id' => $post_id,
'title' => $title,
'classes' => 'card__image _article cell-img',
'permalink' => $permalink,
'lightbox' => $args['lightbox'],
'vertical' => 1,
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio'] ?: '3/1'
)
);
}
?>
<h3 class="card__title cell-title _article _small"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php echo $title; ?></a></h3> <div class="card__main _article">
<?php if ( post_password_required() ) : ?> <?php
<div class="card__content _small _article cell-desc"><div class="truncate _5-5"><span><?php echo $obfuscation; ?></span></div></div> if ( $args['thumbnail'] ) {
<?php else : ?> fictioneer_render_thumbnail(
<div class="card__content _small _article cell-desc"><div class="truncate _5-5"><span><?php array(
echo wp_strip_all_tags( fictioneer_get_excerpt() ); 'post_id' => $post_id,
?></span></div></div> 'title' => $title,
<?php endif; ?> 'classes' => 'card__image _article cell-img',
'permalink' => $permalink,
'lightbox' => $args['lightbox'],
'vertical' => 1,
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio'] ?: '3/1'
)
);
}
?>
<?php if ( $categories || $tags || $genres || $fandoms || $characters ) : ?> <h3 class="card__title cell-title _article _small"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php echo $title; ?></a></h3>
<div class="card__tag-list cell-tax _small _scrolling">
<div class="card__h-scroll">
<?php
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
$terms = array_merge( <?php if ( post_password_required() ) : ?>
$categories ? fictioneer_get_term_nodes( $categories, '_inline _category' ) : [], <div class="card__content _small _article cell-desc"><div class="truncate _5-5"><span><?php echo $obfuscation; ?></span></div></div>
$fandoms ? fictioneer_get_term_nodes( $fandoms, "{$variant} _fandom" ) : [], <?php else : ?>
$genres ? fictioneer_get_term_nodes( $genres, "{$variant} _genre" ) : [], <div class="card__content _small _article cell-desc"><div class="truncate _5-5"><span><?php
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [], echo wp_strip_all_tags( fictioneer_get_excerpt() );
$characters ? fictioneer_get_term_nodes( $characters, "{$variant} _character" ) : [] ?></span></div></div>
); <?php endif; ?>
$terms = apply_filters( <?php if ( $categories || $tags || $genres || $fandoms || $characters ) : ?>
'fictioneer_filter_shortcode_article_cards_terms', <div class="card__tag-list cell-tax _small _scrolling">
$terms, $post, $args, null <div class="card__h-scroll">
); <?php
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
// Implode with separator $terms = array_merge(
echo implode( $categories ? fictioneer_get_term_nodes( $categories, '_inline _category' ) : [],
fictioneer_get_bullet_separator( 'article-cards', $args['terms'] === 'pills' ), $fandoms ? fictioneer_get_term_nodes( $fandoms, "{$variant} _fandom" ) : [],
array_slice( $terms, 0, $args['max_terms'] ) $genres ? fictioneer_get_term_nodes( $genres, "{$variant} _genre" ) : [],
); $tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
?> $characters ? fictioneer_get_term_nodes( $characters, "{$variant} _character" ) : []
);
$terms = apply_filters(
'fictioneer_filter_shortcode_article_cards_terms',
$terms, $post, $args, null
);
// Implode with separator
echo implode(
fictioneer_get_bullet_separator( 'article-cards', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
?>
</div>
</div> </div>
</div> <?php endif; ?>
<?php endif; ?>
<?php if ( $args['footer'] ) : ?> <?php if ( $args['footer'] ) : ?>
<div class="card__footer cell-footer _article _small"> <div class="card__footer cell-footer _article _small">
<div class="card__footer-box text-overflow-ellipsis"><?php <div class="card__footer-box text-overflow-ellipsis"><?php
// Build footer items // Build footer items
$footer_items = []; $footer_items = [];
if ( $args['footer_author'] && get_option( 'fictioneer_show_authors' ) ) { if ( $args['footer_author'] && get_option( 'fictioneer_show_authors' ) ) {
$footer_items['author'] = '<span class="card__footer-author"><i class="card-footer-icon fa-solid fa-circle-user"></i> ' . fictioneer_get_author_node( get_the_author_meta( 'ID' ) ) . '</span>'; $footer_items['author'] = '<span class="card__footer-author"><i class="card-footer-icon fa-solid fa-circle-user"></i> ' . fictioneer_get_author_node( get_the_author_meta( 'ID' ) ) . '</span>';
}
if ( $args['footer_date'] ) {
$format = $args['date_format'] ?: FICTIONEER_CARD_ARTICLE_FOOTER_DATE;
if ( $args['orderby'] === 'modified' ) {
$footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>';
} else {
$footer_items['publish_date'] = '<span class="card__footer-publish-date"><i class="card-footer-icon fa-solid fa-clock" title="' . esc_attr__( 'Published', 'fictioneer' ) .'"></i> ' . get_the_date( $format ) . '</span>';
} }
}
if ( $args['footer_comments'] ) { if ( $args['footer_date'] ) {
$footer_items['comments'] = '<span class="card__footer-comments"><i class="card-footer-icon fa-solid fa-message" title="' . esc_attr__( 'Comments', 'fictioneer' ) . '"></i> ' . get_comments_number( $post ) . '</span>'; $format = $args['date_format'] ?: FICTIONEER_CARD_ARTICLE_FOOTER_DATE;
}
// Filter footer items if ( $args['orderby'] === 'modified' ) {
$footer_items = apply_filters( 'fictioneer_filter_shortcode_article_card_footer', $footer_items, $post ); $footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>';
} else {
$footer_items['publish_date'] = '<span class="card__footer-publish-date"><i class="card-footer-icon fa-solid fa-clock" title="' . esc_attr__( 'Published', 'fictioneer' ) .'"></i> ' . get_the_date( $format ) . '</span>';
}
}
// Implode and render footer items if ( $args['footer_comments'] ) {
echo implode( ' ', $footer_items ); $footer_items['comments'] = '<span class="card__footer-comments"><i class="card-footer-icon fa-solid fa-message" title="' . esc_attr__( 'Comments', 'fictioneer' ) . '"></i> ' . get_comments_number( $post ) . '</span>';
?></div> }
</div>
<?php endif; ?>
</div> // Filter footer items
$footer_items = apply_filters( 'fictioneer_filter_shortcode_article_card_footer', $footer_items, $post );
</article> // Implode and render footer items
</li> echo implode( ' ', $footer_items );
<?php // <--- End HTML ?></div>
} </div>
wp_reset_postdata(); <?php endif; ?>
?>
</ul> </div>
</article>
</li>
<?php // <--- End HTML
}
wp_reset_postdata();
?>
</ul>
<?php if ( $splide ) { echo '<ul class="splide__pagination"></ul>'; } ?>
<?php if ( $args['count'] < 1 && $query->max_num_pages > 1 ) : ?>
<nav class="pagination"><?php echo fictioneer_paginate_links( $pag_args ); ?></nav>
<?php endif; ?>
<?php else : ?>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<?php if ( $args['count'] < 1 && $query->max_num_pages > 1 ) : ?>
<nav class="pagination"><?php echo fictioneer_paginate_links( $pag_args ); ?></nav>
<?php endif; ?> <?php endif; ?>
<?php else : ?> <?php if ( $splide ) { echo '</div>'; } ?>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<?php endif; ?>
</section> </section>

View File

@ -34,6 +34,7 @@
* @internal $args['footer_author'] Whether to show the chapter author. Default true. * @internal $args['footer_author'] Whether to show the chapter author. Default true.
* @internal $args['footer_words'] Whether to show the chapter word count. Default true. * @internal $args['footer_words'] Whether to show the chapter word count. Default true.
* @internal $args['footer_date'] Whether to show the chapter date. Default true. * @internal $args['footer_date'] Whether to show the chapter date. Default true.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -41,6 +42,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$card_counter = 0; $card_counter = 0;
// Prepare query // Prepare query
@ -118,179 +120,202 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="small-card-block latest-chapters _compact <?php echo $args['classes']; ?>"> <section class="small-card-block latest-chapters _compact <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="grid-columns _collapse-on-mobile"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php <?php if ( $entries->have_posts() ) : ?>
// Setup
$post_id = $post->ID;
$story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true );
if ( get_post_status( $story_id ) !== 'publish' ) { <ul class="grid-columns _collapse-on-mobile <?php if ( $splide ) { echo 'splide__list'; } ?>">
continue; <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
}
$title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-chapters-compact' ); <?php
$story = $story_id ? fictioneer_get_story_data( $story_id, false ) : null; // Does not refresh comment count! // Setup
$text_icon = get_post_meta( $post_id, 'fictioneer_chapter_text_icon', true ); $post_id = $post->ID;
$words = fictioneer_get_word_count( $post_id ); $story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
// Extra card classes if ( get_post_status( $story_id ) !== 'publish' ) {
if ( ! empty( $post->post_password ) ) { continue;
$card_classes[] = '_password'; }
}
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) { $title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-chapters-compact' );
$card_classes[] = '_' . get_theme_mod( 'card_style' ); $story = $story_id ? fictioneer_get_story_data( $story_id, false ) : null; // Does not refresh comment count!
} $text_icon = get_post_meta( $post_id, 'fictioneer_chapter_text_icon', true );
$words = fictioneer_get_word_count( $post_id );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
if ( $args['vertical'] ) { // Extra card classes
$card_classes[] = '_vertical'; if ( ! empty( $post->post_password ) ) {
} $card_classes[] = '_password';
}
if ( $args['seamless'] ) { if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
$card_classes[] = '_seamless'; $card_classes[] = '_' . get_theme_mod( 'card_style' );
} }
if ( ! $args['footer_words'] ) { if ( $args['vertical'] ) {
$card_classes[] = '_no-footer-words'; $card_classes[] = '_vertical';
} }
if ( ! $args['footer_date'] ) { if ( $args['seamless'] ) {
$card_classes[] = '_no-footer-date'; $card_classes[] = '_seamless';
} }
// Count actually rendered cards to account for buffer if ( ! $args['footer_words'] ) {
if ( ++$card_counter > $args['count'] ) { $card_classes[] = '_no-footer-words';
break; }
}
// Card attributes if ( ! $args['footer_date'] ) {
$attributes = []; $card_classes[] = '_no-footer-date';
}
if ( $args['aspect_ratio'] ) { if ( $splide ) {
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio']; $card_classes[] = 'splide__slide';
} }
$attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-chapters-compact' ); // Count actually rendered cards to account for buffer
if ( ++$card_counter > $args['count'] ) {
break;
}
$card_attributes = ''; // Card attributes
$attributes = [];
foreach ( $attributes as $key => $value ) { if ( $args['aspect_ratio'] ) {
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" '; $attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
} }
?>
<li class="post-<?php echo $post_id; ?> card watch-last-clicked _small _info _chapter _compact _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>> $attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-chapters-compact' );
<div class="card__body polygon">
<?php if ( $words > 0 && $args['infobox'] ) : ?> $card_attributes = '';
<button class="card__info-toggle toggle-last-clicked" aria-label="<?php esc_attr_e( 'Open info box', 'fictioneer' ); ?>"><i class="fa-solid fa-chevron-down"></i></button>
<?php endif; ?>
<div class="card__main <?php echo $grid_or_vertical; ?> _small"> foreach ( $attributes as $key => $value ) {
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
<?php <li class="post-<?php echo $post_id; ?> card watch-last-clicked _small _info _chapter _compact _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
do_action( 'fictioneer_shortcode_latest_chapters_card_body', $post, $story, $args ); <div class="card__body polygon">
if ( $args['thumbnail'] ) { <?php if ( $words > 0 && $args['infobox'] ) : ?>
fictioneer_render_thumbnail( <button class="card__info-toggle toggle-last-clicked" aria-label="<?php esc_attr_e( 'Open info box', 'fictioneer' ); ?>"><i class="fa-solid fa-chevron-down"></i></button>
array( <?php endif; ?>
'post_id' => $post_id,
'title' => $title,
'classes' => 'card__image cell-img',
'permalink' => get_permalink(),
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio'],
'text_icon' => $text_icon
)
);
}
?>
<h3 class="card__title _small cell-title"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php <div class="card__main <?php echo $grid_or_vertical; ?> _small">
$list_title = get_post_meta( $post_id, 'fictioneer_chapter_list_title', true );
$list_title = trim( wp_strip_all_tags( $list_title ) );
if ( ! empty( $post->post_password ) ) { <?php
echo '<i class="fa-solid fa-lock protected-icon"></i> '; do_action( 'fictioneer_shortcode_latest_chapters_card_body', $post, $story, $args );
}
echo $list_title ? $list_title : $title; if ( $args['thumbnail'] ) {
?></a></h3> fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $title,
'classes' => 'card__image cell-img',
'permalink' => get_permalink(),
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio'],
'text_icon' => $text_icon
)
);
}
?>
<div class="card__content _small cell-desc"> <h3 class="card__title _small cell-title"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php
<div class="text-overflow-ellipsis _bottom-spacer-xs"> $list_title = get_post_meta( $post_id, 'fictioneer_chapter_list_title', true );
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] && $args['footer_author'] ) : ?> $list_title = trim( wp_strip_all_tags( $list_title ) );
<span class="card__by-author"><?php
printf( _x( 'by %s', 'Small card: by {Author}.', 'fictioneer' ), fictioneer_get_author_node() );
?></span>
<?php endif; ?>
<?php
if ( $story && $args['source'] ) {
$story_link = get_post_meta( $story_id, 'fictioneer_story_redirect_link', true )
?: get_permalink( $story_id );
printf( if ( ! empty( $post->post_password ) ) {
_x( 'in <a href="%1$s" class="bold-link">%2$s</a>', 'Small card: in {Link to Story}.', 'fictioneer' ), echo '<i class="fa-solid fa-lock protected-icon"></i> ';
$story_link, }
fictioneer_truncate( fictioneer_get_safe_title( $story_id, 'shortcode-latest-chapters-compact' ), 24 )
); echo $list_title ? $list_title : $title;
} ?></a></h3>
?>
</div> <div class="card__content _small cell-desc">
<div class="card__words-on-date text-overflow-ellipsis"> <div class="text-overflow-ellipsis _bottom-spacer-xs">
<?php <?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] && $args['footer_author'] ) : ?>
$format = $args['date_format'] ?: FICTIONEER_LATEST_CHAPTERS_FOOTER_DATE; <span class="card__by-author"><?php
printf( _x( 'by %s', 'Small card: by {Author}.', 'fictioneer' ), fictioneer_get_author_node() );
?></span>
<?php endif; ?>
<?php
if ( $story && $args['source'] ) {
$story_link = get_post_meta( $story_id, 'fictioneer_story_redirect_link', true )
?: get_permalink( $story_id );
if ( $words > 0 && $args['footer_words'] && $args['footer_date'] ) {
printf(
_x( '%1$s Words on %2$s', 'Small card: {n} Words on {Date}.', 'fictioneer' ),
fictioneer_shorten_number( fictioneer_get_word_count( $post_id ) ),
get_the_time( $format )
);
} else {
if ( $words > 0 && $args['footer_words'] ) {
printf( printf(
_x( '%1$s Words', 'Small card: {n} Words.', 'fictioneer' ), _x( 'in <a href="%1$s" class="bold-link">%2$s</a>', 'Small card: in {Link to Story}.', 'fictioneer' ),
fictioneer_shorten_number( fictioneer_get_word_count( $post_id ) ) $story_link,
fictioneer_truncate( fictioneer_get_safe_title( $story_id, 'shortcode-latest-chapters-compact' ), 24 )
); );
} elseif ( $args['footer_date'] ) {
the_time( $format );
} }
} ?>
?> </div>
<div class="card__words-on-date text-overflow-ellipsis">
<?php
$format = $args['date_format'] ?: FICTIONEER_LATEST_CHAPTERS_FOOTER_DATE;
if ( $words > 0 && $args['footer_words'] && $args['footer_date'] ) {
printf(
_x( '%1$s Words on %2$s', 'Small card: {n} Words on {Date}.', 'fictioneer' ),
fictioneer_shorten_number( fictioneer_get_word_count( $post_id ) ),
get_the_time( $format )
);
} else {
if ( $words > 0 && $args['footer_words'] ) {
printf(
_x( '%1$s Words', 'Small card: {n} Words.', 'fictioneer' ),
fictioneer_shorten_number( fictioneer_get_word_count( $post_id ) )
);
} elseif ( $args['footer_date'] ) {
the_time( $format );
}
}
?>
</div>
</div> </div>
</div> </div>
<?php if ( $words > 0 && $args['infobox'] ) : ?>
<div class="card__overlay-infobox _excerpt escape-last-click">
<div class="card__excerpt"><?php
echo fictioneer_get_forced_excerpt( $post, $args['vertical'] ? 512 : 256 );
?></div>
</div>
<?php endif; ?>
</div> </div>
</li>
<?php if ( $words > 0 && $args['infobox'] ) : ?> <?php endwhile; ?>
<div class="card__overlay-infobox _excerpt escape-last-click"> </ul>
<div class="card__excerpt"><?php
echo fictioneer_get_forced_excerpt( $post, $args['vertical'] ? 512 : 256 );
?></div>
</div>
<?php endif; ?>
</div> <?php else : ?>
</li>
<?php endwhile; ?> <div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
</ul>
<?php else : ?> <?php endif; wp_reset_postdata(); ?>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div> <?php if ( $splide ) { echo '</div>'; } ?>
<?php endif; wp_reset_postdata(); ?>
</section> </section>

View File

@ -38,6 +38,7 @@
* @internal $args['footer_status'] Whether to show the chapter story status. Default true. * @internal $args['footer_status'] Whether to show the chapter story status. Default true.
* @internal $args['footer_rating'] Whether to show the story/chapter age rating. Default true. * @internal $args['footer_rating'] Whether to show the story/chapter age rating. Default true.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -45,6 +46,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$render_count = 0; $render_count = 0;
$content_list_style = get_theme_mod( 'content_list_style', 'default' ); $content_list_style = get_theme_mod( 'content_list_style', 'default' );
@ -123,184 +125,207 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="latest-chapters _list <?php echo $args['classes']; ?>"> <section class="latest-chapters _list <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="post-list _latest-chapters"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php <?php if ( $entries->have_posts() ) : ?>
// Setup
$post_id = $post->ID;
$story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true );
$story = $story_id ? fictioneer_get_story_data( $story_id, false ) : null; // Does not refresh comment count!
if ( get_post_status( $story_id ) !== 'publish' || ! $story ) { <ul class="post-list _latest-chapters <?php if ( $splide ) { echo 'splide__list'; } ?>">
continue; <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
}
// Count actually rendered items to account for buffer
if ( ++$render_count > $args['count'] ) {
break;
}
// Continue setup
$title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-chapters-list' );
$permalink = get_permalink( $post_id );
$chapter_rating = get_post_meta( $post_id, 'fictioneer_chapter_rating', true );
$words = fictioneer_get_word_count( $post_id );
// Thumbnail
$thumbnail = null;
if ( $args['thumbnail'] ) {
$thumbnail = fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $title,
'permalink' => $permalink,
'size' => 'snippet',
'classes' => 'post-list-item__image',
'lightbox' => $args['lightbox'],
'aspect_ratio' => $args['aspect_ratio'] ?? '2/2.5'
),
false
);
}
// Extra classes
$classes = [];
if ( ! empty( $post->post_password ) ) {
$classes[] = '_password';
}
if ( $args['seamless'] ) {
$classes[] = '_seamless';
}
if ( $content_list_style !== 'default' ) {
$classes[] = "_{$content_list_style}";
}
if ( ! $thumbnail ) {
$classes[] = '_no-thumbnail';
}
if ( ! $args['footer'] ) {
$classes[] = '_no-footer';
}
if ( ! $args['footer_author'] ) {
$classes[] = '_no-footer-author';
}
if ( ! $args['footer_words'] ) {
$classes[] = '_no-footer-words';
}
if ( ! $args['footer_date'] ) {
$classes[] = '_no-footer-date';
}
if ( ! $args['footer_status'] ) {
$classes[] = '_no-footer-status';
}
if ( ! $args['footer_rating'] ) {
$classes[] = '_no-footer-rating';
}
// Meta
$meta = [];
if ( $args['source'] ) {
$meta['story'] = sprintf(
_x(
'<span class="post-list-item__meta-in-story"><span>in </span><a href="%1$s">%2$s</a></span>',
'Story in Latest * shortcode (type: list).',
'fictioneer'
),
get_the_permalink( $story_id ),
$story['title']
);
}
if ( $words > 0 && $args['footer_words'] ) {
$meta['words'] = '<span class="post-list-item__meta-words">' . sprintf(
_x( '%s&nbsp;Words', 'Word count in Latest * shortcode (type: list).', 'fictioneer' ),
number_format_i18n( $words )
) . '</span>';
}
if ( $args['footer_status'] ) {
$meta['status'] = '<span class="post-item-item__meta-status">' . fcntr( $story['status'] ) . '</span>';
}
if ( $chapter_rating && $args['footer_rating'] ) {
$meta['rating'] = '<span class="post-list-item__meta-rating">' . fcntr( $chapter_rating ) . '</span>';
}
if ( get_option( 'fictioneer_show_authors' ) && $args['footer_author'] ) {
$author = fictioneer_get_author_node( null, 'post-list-item__meta-author' );
if ( $author ) {
$meta['author'] = $author;
}
}
if ( $args['footer_date'] ) {
$meta['publish_date'] = '<span class="post-list-item__meta-publish-date _floating-right">' . get_the_date( $args['date_format'], $post ) . '</span>';
}
$meta = apply_filters( 'fictioneer_filter_shortcode_latest_chapter_list_meta', $meta, $post, $story );
// Attributes
$attributes = [];
if ( $args['aspect_ratio'] ) {
$attributes['style'] = '--post-item-image-aspect-ratio: ' . $args['aspect_ratio'];
}
$attributes = apply_filters( 'fictioneer_filter_shortcode_list_attributes', $attributes, $post, 'latest-chapters' );
$output_attributes = '';
foreach ( $attributes as $key => $value ) {
$output_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
<li class="post-<?php echo $post_id; ?> post-list-item _latest-chapters <?php echo implode( ' ', $classes ); ?>" <?php echo $output_attributes; ?>>
<?php <?php
if ( $thumbnail ) { // Setup
echo $thumbnail; $post_id = $post->ID;
$story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true );
$story = $story_id ? fictioneer_get_story_data( $story_id, false ) : null; // Does not refresh comment count!
if ( get_post_status( $story_id ) !== 'publish' || ! $story ) {
continue;
} }
// Count actually rendered items to account for buffer
if ( ++$render_count > $args['count'] ) {
break;
}
// Continue setup
$title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-chapters-list' );
$permalink = get_permalink( $post_id );
$chapter_rating = get_post_meta( $post_id, 'fictioneer_chapter_rating', true );
$words = fictioneer_get_word_count( $post_id );
// Thumbnail
$thumbnail = null;
if ( $args['thumbnail'] ) {
$thumbnail = fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $title,
'permalink' => $permalink,
'size' => 'snippet',
'classes' => 'post-list-item__image',
'lightbox' => $args['lightbox'],
'aspect_ratio' => $args['aspect_ratio'] ?? '2/2.5'
),
false
);
}
// Extra classes
$classes = [];
if ( ! empty( $post->post_password ) ) {
$classes[] = '_password';
}
if ( $args['seamless'] ) {
$classes[] = '_seamless';
}
if ( $content_list_style !== 'default' ) {
$classes[] = "_{$content_list_style}";
}
if ( ! $thumbnail ) {
$classes[] = '_no-thumbnail';
}
if ( ! $args['footer'] ) {
$classes[] = '_no-footer';
}
if ( ! $args['footer_author'] ) {
$classes[] = '_no-footer-author';
}
if ( ! $args['footer_words'] ) {
$classes[] = '_no-footer-words';
}
if ( ! $args['footer_date'] ) {
$classes[] = '_no-footer-date';
}
if ( ! $args['footer_status'] ) {
$classes[] = '_no-footer-status';
}
if ( ! $args['footer_rating'] ) {
$classes[] = '_no-footer-rating';
}
if ( $splide ) {
$classes[] = 'splide__slide';
}
// Meta
$meta = [];
if ( $args['source'] ) {
$meta['story'] = sprintf(
_x(
'<span class="post-list-item__meta-in-story"><span>in </span><a href="%1$s">%2$s</a></span>',
'Story in Latest * shortcode (type: list).',
'fictioneer'
),
get_the_permalink( $story_id ),
$story['title']
);
}
if ( $words > 0 && $args['footer_words'] ) {
$meta['words'] = '<span class="post-list-item__meta-words">' . sprintf(
_x( '%s&nbsp;Words', 'Word count in Latest * shortcode (type: list).', 'fictioneer' ),
number_format_i18n( $words )
) . '</span>';
}
if ( $args['footer_status'] ) {
$meta['status'] = '<span class="post-item-item__meta-status">' . fcntr( $story['status'] ) . '</span>';
}
if ( $chapter_rating && $args['footer_rating'] ) {
$meta['rating'] = '<span class="post-list-item__meta-rating">' . fcntr( $chapter_rating ) . '</span>';
}
if ( get_option( 'fictioneer_show_authors' ) && $args['footer_author'] ) {
$author = fictioneer_get_author_node( null, 'post-list-item__meta-author' );
if ( $author ) {
$meta['author'] = $author;
}
}
if ( $args['footer_date'] ) {
$meta['publish_date'] = '<span class="post-list-item__meta-publish-date _floating-right">' . get_the_date( $args['date_format'], $post ) . '</span>';
}
$meta = apply_filters( 'fictioneer_filter_shortcode_latest_chapter_list_meta', $meta, $post, $story );
// Attributes
$attributes = [];
if ( $args['aspect_ratio'] ) {
$attributes['style'] = '--post-item-image-aspect-ratio: ' . $args['aspect_ratio'];
}
$attributes = apply_filters( 'fictioneer_filter_shortcode_list_attributes', $attributes, $post, 'latest-chapters' );
$output_attributes = '';
foreach ( $attributes as $key => $value ) {
$output_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?> ?>
<a href="<?php echo $permalink; ?>" class="post-list-item__title _link"><?php <li class="post-<?php echo $post_id; ?> post-list-item _latest-chapters <?php echo implode( ' ', $classes ); ?>" <?php echo $output_attributes; ?>>
$html_title = empty( $post->post_password ) ? '' : '<i class="fa-solid fa-lock protected-icon"></i> ';
$html_title .= $title;
echo apply_filters( 'fictioneer_filter_shortcode_list_title', $html_title, $post, 'shortcode-latest-chapters-list' ); <?php
?></a> if ( $thumbnail ) {
echo $thumbnail;
}
?>
<?php if ( $args['footer'] ) : ?> <a href="<?php echo $permalink; ?>" class="post-list-item__title _link"><?php
<div class="post-list-item__meta pseudo-separator"><?php echo implode( ' ', $meta ); ?></div> $html_title = empty( $post->post_password ) ? '' : '<i class="fa-solid fa-lock protected-icon"></i> ';
<?php endif; ?> $html_title .= $title;
</li> echo apply_filters( 'fictioneer_filter_shortcode_list_title', $html_title, $post, 'shortcode-latest-chapters-list' );
?></a>
<?php endwhile; ?> <?php if ( $args['footer'] ) : ?>
</ul> <div class="post-list-item__meta pseudo-separator"><?php echo implode( ' ', $meta ); ?></div>
<?php endif; ?>
<?php else : ?> </li>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div> <?php endwhile; ?>
</ul>
<?php endif; wp_reset_postdata(); ?> <?php else : ?>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<?php endif; wp_reset_postdata(); ?>
<?php if ( $splide ) { echo '</div>'; } ?>
</section> </section>

View File

@ -39,6 +39,7 @@
* @internal $args['footer_status'] Whether to show the chapter story status. Default true. * @internal $args['footer_status'] Whether to show the chapter story status. Default true.
* @internal $args['footer_rating'] Whether to show the chapter age rating. Default true. * @internal $args['footer_rating'] Whether to show the chapter age rating. Default true.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -46,6 +47,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$card_counter = 0; $card_counter = 0;
// Prepare query // Prepare query
@ -123,243 +125,266 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="small-card-block latest-chapters <?php echo $args['classes']; ?>"> <section class="small-card-block latest-chapters <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="grid-columns _collapse-on-mobile"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php <?php if ( $entries->have_posts() ) : ?>
// Setup
$post_id = $post->ID;
$story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true );
if ( get_post_status( $story_id ) !== 'publish' ) { <ul class="grid-columns _collapse-on-mobile <?php if ( $splide ) { echo 'splide__list'; } ?>">
continue; <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
}
$title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-chapters' ); <?php
$chapter_rating = get_post_meta( $post_id, 'fictioneer_chapter_rating', true ); // Setup
$story = $story_id ? fictioneer_get_story_data( $story_id, false ) : null; // Does not refresh comment count! $post_id = $post->ID;
$text_icon = get_post_meta( $post_id, 'fictioneer_chapter_text_icon', true ); $story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
// Extra card classes if ( get_post_status( $story_id ) !== 'publish' ) {
if ( ! empty( $post->post_password ) ) { continue;
$card_classes[] = '_password'; }
}
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) { $title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-chapters' );
$card_classes[] = '_' . get_theme_mod( 'card_style' ); $chapter_rating = get_post_meta( $post_id, 'fictioneer_chapter_rating', true );
} $story = $story_id ? fictioneer_get_story_data( $story_id, false ) : null; // Does not refresh comment count!
$text_icon = get_post_meta( $post_id, 'fictioneer_chapter_text_icon', true );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
if ( $args['simple'] || ! $args['footer'] ) { // Extra card classes
$card_classes[] = '_no-footer'; if ( ! empty( $post->post_password ) ) {
} $card_classes[] = '_password';
}
if ( $args['vertical'] ) { if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
$card_classes[] = '_vertical'; $card_classes[] = '_' . get_theme_mod( 'card_style' );
} }
if ( $args['seamless'] ) { if ( $args['simple'] || ! $args['footer'] ) {
$card_classes[] = '_seamless'; $card_classes[] = '_no-footer';
} }
if ( ! $args['footer_words'] ) { if ( $args['vertical'] ) {
$card_classes[] = '_no-footer-words'; $card_classes[] = '_vertical';
} }
if ( ! $args['footer_date'] ) { if ( $args['seamless'] ) {
$card_classes[] = '_no-footer-date'; $card_classes[] = '_seamless';
} }
if ( ! $args['footer_comments'] ) { if ( ! $args['footer_words'] ) {
$card_classes[] = '_no-footer-comments'; $card_classes[] = '_no-footer-words';
} }
if ( ! $args['footer_status'] ) { if ( ! $args['footer_date'] ) {
$card_classes[] = '_no-footer-status'; $card_classes[] = '_no-footer-date';
} }
if ( ! $args['footer_rating'] ) { if ( ! $args['footer_comments'] ) {
$card_classes[] = '_no-footer-rating'; $card_classes[] = '_no-footer-comments';
} }
// Truncate factor if ( ! $args['footer_status'] ) {
$truncate_factor = $args['vertical'] ? '_4-4' : '_cq-3-4'; $card_classes[] = '_no-footer-status';
}
// Count actually rendered cards to account for buffer if ( ! $args['footer_rating'] ) {
if ( ++$card_counter > $args['count'] ) { $card_classes[] = '_no-footer-rating';
break; }
}
// Card attributes if ( $splide ) {
$attributes = []; $card_classes[] = 'splide__slide';
}
if ( $args['aspect_ratio'] ) { // Truncate factor
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio']; $truncate_factor = $args['vertical'] ? '_4-4' : '_cq-3-4';
}
$attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-chapters' ); // Count actually rendered cards to account for buffer
if ( ++$card_counter > $args['count'] ) {
break;
}
$card_attributes = ''; // Card attributes
$attributes = [];
foreach ( $attributes as $key => $value ) { if ( $args['aspect_ratio'] ) {
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" '; $attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
} }
?>
<li class="post-<?php echo $post_id; ?> card _small _chapter <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>> $attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-chapters' );
<div class="card__body polygon">
<div class="card__main <?php echo $grid_or_vertical; ?> _small"> $card_attributes = '';
<?php foreach ( $attributes as $key => $value ) {
do_action( 'fictioneer_shortcode_latest_chapters_card_body', $post, $story, $args ); $card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
if ( $args['thumbnail'] ) { <li class="post-<?php echo $post_id; ?> card _small _chapter <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
fictioneer_render_thumbnail( <div class="card__body polygon">
array(
'post_id' => $post_id,
'title' => $title,
'classes' => 'card__image cell-img',
'permalink' => get_permalink(),
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio'],
'text_icon' => $text_icon
)
);
}
?>
<h3 class="card__title _small cell-title"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php <div class="card__main <?php echo $grid_or_vertical; ?> _small">
$list_title = get_post_meta( $post_id, 'fictioneer_chapter_list_title', true );
$list_title = trim( wp_strip_all_tags( $list_title ) );
if ( ! empty( $post->post_password ) ) { <?php
echo '<i class="fa-solid fa-lock protected-icon"></i> '; do_action( 'fictioneer_shortcode_latest_chapters_card_body', $post, $story, $args );
}
echo $list_title ? $list_title : $title; if ( $args['thumbnail'] ) {
?></a></h3> fictioneer_render_thumbnail(
array(
<div class="card__content _small cell-desc"> 'post_id' => $post_id,
<div class="truncate <?php echo $truncate_factor; ?> <?php if ( ! $args['spoiler'] ) echo '_obfuscated'; ?>" data-obfuscation-target> 'title' => $title,
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] && $args['footer_author'] ) : ?> 'classes' => 'card__image cell-img',
<span class="card__by-author"><?php 'permalink' => get_permalink(),
printf( _x( 'by %s', 'Small card: by {Author}.', 'fictioneer' ), fictioneer_get_author_node() ); 'lightbox' => $args['lightbox'],
?></span> 'vertical' => $args['vertical'],
<?php endif; ?> 'seamless' => $args['seamless'],
<?php 'aspect_ratio' => $args['aspect_ratio'],
if ( $story && $args['source'] ) { 'text_icon' => $text_icon
$story_link = get_post_meta( $story_id, 'fictioneer_story_redirect_link', true ) )
?: get_permalink( $story_id );
printf(
_x( 'in <a href="%1$s" class="bold-link">%2$s</a>', 'Small card: in {Link to Story}.', 'fictioneer' ),
$story_link,
fictioneer_truncate( fictioneer_get_safe_title( $story_id, 'shortcode-latest-chapters' ), 24 )
);
}
$excerpt = fictioneer_get_forced_excerpt( $post );
$spoiler_note = str_repeat(
_x( '&#183; ', 'Spoiler obfuscation character.', 'fictioneer' ), intval( mb_strlen( $excerpt ) )
); );
$spoiler_note = apply_filters( 'fictioneer_filter_obfuscation_string', $spoiler_note, $post ); }
?> ?>
<?php if ( mb_strlen( str_replace( '…', '', $excerpt ) ) > 2 ) : ?>
<?php if ( ! $args['spoiler'] ) : ?> <h3 class="card__title _small cell-title"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php
<span data-click="toggle-obfuscation" tabindex="0"> $list_title = get_post_meta( $post_id, 'fictioneer_chapter_list_title', true );
<span class="obfuscated">&nbsp;<?php echo $spoiler_note; ?></span> $list_title = trim( wp_strip_all_tags( $list_title ) );
<span class="clean"><?php
if ( ! empty( $post->post_password ) ) {
echo '<i class="fa-solid fa-lock protected-icon"></i> ';
}
echo $list_title ? $list_title : $title;
?></a></h3>
<div class="card__content _small cell-desc">
<div class="truncate <?php echo $truncate_factor; ?> <?php if ( ! $args['spoiler'] ) echo '_obfuscated'; ?>" data-obfuscation-target>
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] && $args['footer_author'] ) : ?>
<span class="card__by-author"><?php
printf( _x( 'by %s', 'Small card: by {Author}.', 'fictioneer' ), fictioneer_get_author_node() );
?></span>
<?php endif; ?>
<?php
if ( $story && $args['source'] ) {
$story_link = get_post_meta( $story_id, 'fictioneer_story_redirect_link', true )
?: get_permalink( $story_id );
printf(
_x( 'in <a href="%1$s" class="bold-link">%2$s</a>', 'Small card: in {Link to Story}.', 'fictioneer' ),
$story_link,
fictioneer_truncate( fictioneer_get_safe_title( $story_id, 'shortcode-latest-chapters' ), 24 )
);
}
$excerpt = fictioneer_get_forced_excerpt( $post );
$spoiler_note = str_repeat(
_x( '&#183; ', 'Spoiler obfuscation character.', 'fictioneer' ), intval( mb_strlen( $excerpt ) )
);
$spoiler_note = apply_filters( 'fictioneer_filter_obfuscation_string', $spoiler_note, $post );
?>
<?php if ( mb_strlen( str_replace( '…', '', $excerpt ) ) > 2 ) : ?>
<?php if ( ! $args['spoiler'] ) : ?>
<span data-click="toggle-obfuscation" tabindex="0">
<span class="obfuscated">&nbsp;<?php echo $spoiler_note; ?></span>
<span class="clean"><?php
echo $args['source'] ? '— ' : '';
echo $excerpt;
?></span>
</span>
<?php else : ?>
<span><span class="clean"><?php
echo $args['source'] ? '— ' : ''; echo $args['source'] ? '— ' : '';
echo $excerpt; echo $excerpt;
?></span> ?></span></span>
</span> <?php endif; ?>
<?php else : ?>
<span><span class="clean"><?php
echo $args['source'] ? '— ' : '';
echo $excerpt;
?></span></span>
<?php endif; ?> <?php endif; ?>
<?php endif; ?> </div>
</div> </div>
<?php if ( ! $args['simple'] && $args['footer'] ) : ?>
<div class="card__footer cell-footer _small">
<div class="card__footer-box _left text-overflow-ellipsis"><?php
// Build footer items
$footer_items = [];
if ( $args['footer_words'] ) {
$words = fictioneer_get_word_count( $post_id );
if ( $words > 0 ) {
$footer_items['words'] = '<span class="card__footer-words"><i class="card-footer-icon fa-solid fa-font" title="' . esc_attr__( 'Words', 'fictioneer' ) . '"></i> ' . fictioneer_shorten_number( fictioneer_get_word_count( $post_id ) ) . '</span>';
}
}
if ( $args['footer_date'] ) {
$format = $args['date_format'] ?: FICTIONEER_LATEST_CHAPTERS_FOOTER_DATE;
if ( $args['orderby'] === 'modified' ) {
$footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>';
} else {
$footer_items['publish_date'] = '<span class="card__footer-publish-date"><i class="card-footer-icon fa-solid fa-clock" title="' . esc_attr__( 'Published', 'fictioneer' ) . '"></i> ' . get_the_date( $format, $post ) . '</span>';
}
}
if ( $args['footer_comments'] ) {
$footer_items['comments'] = '<span class="card__footer-comments"><i class="card-footer-icon fa-solid fa-message" title="' . esc_attr__( 'Comments', 'fictioneer' ) . '"></i> ' . get_comments_number() . '</span>';
}
if ( $story && $args['footer_status'] ) {
$footer_items['status'] = '<span class="card__footer-status"><i class="card-footer-icon ' . $story['icon'] . '"></i> ' . fcntr( $story['status'] ) . '</span>';
}
// Filter footer items
$footer_items = apply_filters(
'fictioneer_filter_shortcode_latest_chapters_card_footer',
$footer_items,
$post,
$args,
$story
);
// Implode and render footer items
echo implode( ' ', $footer_items );
?></div>
<?php if ( ! empty( $chapter_rating ) && $args['footer_rating'] ) : ?>
<div class="card__footer-box _right rating-letter-label tooltipped" data-tooltip="<?php echo fcntr( $chapter_rating, true ); ?>">
<?php echo fcntr( $chapter_rating[0] ); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div> </div>
<?php if ( ! $args['simple'] && $args['footer'] ) : ?>
<div class="card__footer cell-footer _small">
<div class="card__footer-box _left text-overflow-ellipsis"><?php
// Build footer items
$footer_items = [];
if ( $args['footer_words'] ) {
$words = fictioneer_get_word_count( $post_id );
if ( $words > 0 ) {
$footer_items['words'] = '<span class="card__footer-words"><i class="card-footer-icon fa-solid fa-font" title="' . esc_attr__( 'Words', 'fictioneer' ) . '"></i> ' . fictioneer_shorten_number( fictioneer_get_word_count( $post_id ) ) . '</span>';
}
}
if ( $args['footer_date'] ) {
$format = $args['date_format'] ?: FICTIONEER_LATEST_CHAPTERS_FOOTER_DATE;
if ( $args['orderby'] === 'modified' ) {
$footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>';
} else {
$footer_items['publish_date'] = '<span class="card__footer-publish-date"><i class="card-footer-icon fa-solid fa-clock" title="' . esc_attr__( 'Published', 'fictioneer' ) . '"></i> ' . get_the_date( $format, $post ) . '</span>';
}
}
if ( $args['footer_comments'] ) {
$footer_items['comments'] = '<span class="card__footer-comments"><i class="card-footer-icon fa-solid fa-message" title="' . esc_attr__( 'Comments', 'fictioneer' ) . '"></i> ' . get_comments_number() . '</span>';
}
if ( $story && $args['footer_status'] ) {
$footer_items['status'] = '<span class="card__footer-status"><i class="card-footer-icon ' . $story['icon'] . '"></i> ' . fcntr( $story['status'] ) . '</span>';
}
// Filter footer items
$footer_items = apply_filters(
'fictioneer_filter_shortcode_latest_chapters_card_footer',
$footer_items,
$post,
$args,
$story
);
// Implode and render footer items
echo implode( ' ', $footer_items );
?></div>
<?php if ( ! empty( $chapter_rating ) && $args['footer_rating'] ) : ?>
<div class="card__footer-box _right rating-letter-label tooltipped" data-tooltip="<?php echo fcntr( $chapter_rating, true ); ?>">
<?php echo fcntr( $chapter_rating[0] ); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div> </div>
</li>
</div> <?php endwhile; ?>
</li> </ul>
<?php endwhile; ?> <?php else : ?>
</ul>
<?php else : ?> <div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div> <?php endif; wp_reset_postdata(); ?>
<?php endif; wp_reset_postdata(); ?> <?php if ( $splide ) { echo '</div>'; } ?>
</section> </section>

View File

@ -26,10 +26,11 @@
* @internal $args['aspect_ratio'] Aspect ratio for the image. Only with vertical. * @internal $args['aspect_ratio'] Aspect ratio for the image. Only with vertical.
* @internal $args['lightbox'] Whether the image is opened in the lightbox. Default true. * @internal $args['lightbox'] Whether the image is opened in the lightbox. Default true.
* @internal $args['thumbnail'] Whether the image is rendered. Default true (Customizer). * @internal $args['thumbnail'] Whether the image is rendered. Default true (Customizer).
* @internal $args['terms'] Either inline, pills, none, or false. Default inline. * @internal $args['terms'] Either inline, pills, none, or false. Default inline.
* @internal $args['max_terms'] Maximum number of shown taxonomies. Default 10. * @internal $args['max_terms'] Maximum number of shown taxonomies. Default 10.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['infobox'] Whether to show the info box and toggle. * @internal $args['infobox'] Whether to show the info box and toggle.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -37,6 +38,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$show_terms = ! in_array( $args['terms'], ['none', 'false'] ) && $show_terms = ! in_array( $args['terms'], ['none', 'false'] ) &&
! get_option( 'fictioneer_hide_taxonomies_on_recommendation_cards' ); ! get_option( 'fictioneer_hide_taxonomies_on_recommendation_cards' );
@ -97,158 +99,181 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="small-card-block latest-recommendations _compact <?php echo $args['classes']; ?>"> <section class="small-card-block latest-recommendations _compact <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="grid-columns _collapse-on-mobile"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php <?php if ( $entries->have_posts() ) : ?>
// Setup
$post_id = $post->ID;
$title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-recommendations-compact' );
$one_sentence = get_post_meta( $post_id, 'fictioneer_recommendation_one_sentence', true );
$fandoms = $show_terms ? get_the_terms( $post, 'fcn_fandom' ) : [];
$characters = $show_terms ? get_the_terms( $post, 'fcn_character' ) : [];
$genres = $show_terms ? get_the_terms( $post, 'fcn_genre' ) : [];
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_recommendation_cards' ) ) ?
get_the_tags( $post ) : false;
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
// Extra classes <ul class="grid-columns _collapse-on-mobile <?php if ( $splide ) { echo 'splide__list'; } ?>">
if ( $show_terms ) { <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
$card_classes[] = '_info';
}
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) { <?php
$card_classes[] = '_' . get_theme_mod( 'card_style' ); // Setup
} $post_id = $post->ID;
$title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-recommendations-compact' );
$one_sentence = get_post_meta( $post_id, 'fictioneer_recommendation_one_sentence', true );
$fandoms = $show_terms ? get_the_terms( $post, 'fcn_fandom' ) : [];
$characters = $show_terms ? get_the_terms( $post, 'fcn_character' ) : [];
$genres = $show_terms ? get_the_terms( $post, 'fcn_genre' ) : [];
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_recommendation_cards' ) ) ?
get_the_tags( $post ) : false;
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
if ( $args['vertical'] ) { // Extra classes
$card_classes[] = '_vertical'; if ( $show_terms ) {
} $card_classes[] = '_info';
}
if ( $args['seamless'] ) { if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
$card_classes[] = '_seamless'; $card_classes[] = '_' . get_theme_mod( 'card_style' );
} }
if ( ! $show_terms ) { if ( $args['vertical'] ) {
$card_classes[] = '_no-tax'; $card_classes[] = '_vertical';
} }
// Truncate factor if ( $args['seamless'] ) {
$truncate_factor = $args['vertical'] ? '_4-4' : '_cq-3-4'; $card_classes[] = '_seamless';
}
// Card attributes if ( ! $show_terms ) {
$attributes = []; $card_classes[] = '_no-tax';
}
if ( $args['aspect_ratio'] ) { if ( $splide ) {
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio']; $card_classes[] = 'splide__slide';
} }
$attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-recommendations-compact' ); // Truncate factor
$truncate_factor = $args['vertical'] ? '_4-4' : '_cq-3-4';
$card_attributes = ''; // Card attributes
$attributes = [];
foreach ( $attributes as $key => $value ) { if ( $args['aspect_ratio'] ) {
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" '; $attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
} }
?>
<li class="post-<?php echo $post_id; ?> card watch-last-clicked _small _recommendation _compact _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>> $attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-recommendations-compact' );
<div class="card__body polygon">
<?php if ( $show_terms && $args['infobox'] ) : ?> $card_attributes = '';
<button class="card__info-toggle toggle-last-clicked" aria-label="<?php esc_attr_e( 'Open info box', 'fictioneer' ); ?>"><i class="fa-solid fa-chevron-down"></i></button>
<?php endif; ?>
<div class="card__main <?php echo $grid_or_vertical; ?> _small"> foreach ( $attributes as $key => $value ) {
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
<?php <li class="post-<?php echo $post_id; ?> card watch-last-clicked _small _recommendation _compact _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
do_action( 'fictioneer_shortcode_latest_recommendations_card_body', $post, $args ); <div class="card__body polygon">
if ( $args['thumbnail'] ) { <?php if ( $show_terms && $args['infobox'] ) : ?>
fictioneer_render_thumbnail( <button class="card__info-toggle toggle-last-clicked" aria-label="<?php esc_attr_e( 'Open info box', 'fictioneer' ); ?>"><i class="fa-solid fa-chevron-down"></i></button>
array( <?php endif; ?>
'post_id' => $post_id,
'title' => $title,
'classes' => 'card__image cell-img',
'permalink' => get_permalink(),
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio']
)
);
}
?>
<h3 class="card__title _small cell-title"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php echo $title; ?></a></h3> <div class="card__main <?php echo $grid_or_vertical; ?> _small">
<div class="card__content _small cell-desc"> <?php
<div class="truncate <?php echo $truncate_factor; ?>"> do_action( 'fictioneer_shortcode_latest_recommendations_card_body', $post, $args );
<span class="card__by-author"><?php
printf( if ( $args['thumbnail'] ) {
_x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ), fictioneer_render_thumbnail(
'<span class="author">' . get_post_meta( $post_id, 'fictioneer_recommendation_author', true ) . '</span>' array(
'post_id' => $post_id,
'title' => $title,
'classes' => 'card__image cell-img',
'permalink' => get_permalink(),
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio']
)
); );
?></span> }
<span><?php ?>
if ( ! empty( $one_sentence ) ) {
echo wp_strip_all_tags( $one_sentence, true ); <h3 class="card__title _small cell-title"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php echo $title; ?></a></h3>
} else {
echo fictioneer_first_paragraph_as_excerpt( get_the_content() ); <div class="card__content _small cell-desc">
} <div class="truncate <?php echo $truncate_factor; ?>">
?></span> <span class="card__by-author"><?php
printf(
_x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ),
'<span class="author">' . get_post_meta( $post_id, 'fictioneer_recommendation_author', true ) . '</span>'
);
?></span>
<span><?php
if ( ! empty( $one_sentence ) ) {
echo wp_strip_all_tags( $one_sentence, true );
} else {
echo fictioneer_first_paragraph_as_excerpt( get_the_content() );
}
?></span>
</div>
</div> </div>
</div> </div>
<?php if ( $show_terms && $args['infobox'] ) : ?>
<div class="card__overlay-infobox escape-last-click">
<div class="card__tag-list _small <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>">
<?php
if ( $fandoms || $genres || $tags || $characters ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
$terms = array_merge(
$fandoms ? fictioneer_get_term_nodes( $fandoms, "{$variant} _fandom" ) : [],
$genres ? fictioneer_get_term_nodes( $genres, "{$variant} _genre" ) : [],
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$characters ? fictioneer_get_term_nodes( $characters, "{$variant} _character" ) : []
);
$terms = apply_filters(
'fictioneer_filter_shortcode_latest_recommendations_terms',
$terms, $post, $args, null
);
// Implode with separator
echo implode(
fictioneer_get_bullet_separator( 'latest-recommendations-compact', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
} else {
?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php
}
?>
</div>
</div>
<?php endif; ?>
</div> </div>
</li>
<?php if ( $show_terms && $args['infobox'] ) : ?> <?php endwhile; ?>
<div class="card__overlay-infobox escape-last-click"> </ul>
<div class="card__tag-list _small <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>">
<?php
if ( $fandoms || $genres || $tags || $characters ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
$terms = array_merge( <?php else : ?>
$fandoms ? fictioneer_get_term_nodes( $fandoms, "{$variant} _fandom" ) : [],
$genres ? fictioneer_get_term_nodes( $genres, "{$variant} _genre" ) : [],
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$characters ? fictioneer_get_term_nodes( $characters, "{$variant} _character" ) : []
);
$terms = apply_filters( <div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
'fictioneer_filter_shortcode_latest_recommendations_terms',
$terms, $post, $args, null
);
// Implode with separator <?php endif; wp_reset_postdata(); ?>
echo implode(
fictioneer_get_bullet_separator( 'latest-recommendations-compact', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
} else {
?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php
}
?>
</div>
</div>
<?php endif; ?>
</div> <?php if ( $splide ) { echo '</div>'; } ?>
</li>
<?php endwhile; ?>
</ul>
<?php else : ?>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<?php endif; wp_reset_postdata(); ?>
</section> </section>

View File

@ -24,11 +24,12 @@
* @internal $args['vertical'] Whether to show the vertical variant. * @internal $args['vertical'] Whether to show the vertical variant.
* @internal $args['seamless'] Whether to render the image seamless. Default false (Customizer). * @internal $args['seamless'] Whether to render the image seamless. Default false (Customizer).
* @internal $args['aspect_ratio'] Aspect ratio for the image. Only with vertical. * @internal $args['aspect_ratio'] Aspect ratio for the image. Only with vertical.
* @internal $args['terms'] Either inline, pills, none, or false. Default inline. * @internal $args['terms'] Either inline, pills, none, or false. Default inline.
* @internal $args['max_terms'] Maximum number of shown taxonomies. Default 10. * @internal $args['max_terms'] Maximum number of shown taxonomies. Default 10.
* @internal $args['lightbox'] Whether the image is opened in the lightbox. Default true. * @internal $args['lightbox'] Whether the image is opened in the lightbox. Default true.
* @internal $args['thumbnail'] Whether the image is rendered. Default true (Customizer). * @internal $args['thumbnail'] Whether the image is rendered. Default true (Customizer).
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -36,6 +37,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$show_terms = ! get_option( 'fictioneer_hide_taxonomies_on_recommendation_cards' ); $show_terms = ! get_option( 'fictioneer_hide_taxonomies_on_recommendation_cards' );
// Prepare query // Prepare query
@ -94,178 +96,199 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="small-card-block latest-recommendations <?php echo $args['classes']; ?>"> <section class="small-card-block latest-recommendations <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<?php if ( $entries->have_posts() ) : ?> if ( $splide ) {
echo '<div class="splide__track">';
}
?>
<ul class="grid-columns _collapse-on-mobile"> <?php if ( $entries->have_posts() ) : ?>
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
<?php <ul class="grid-columns _collapse-on-mobile <?php if ( $splide ) { echo 'splide__list'; } ?>">
// Setup <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
$post_id = $post->ID;
$title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-recommendations' );
$one_sentence = get_post_meta( $post_id, 'fictioneer_recommendation_one_sentence', true );
$fandoms = $show_terms ? get_the_terms( $post, 'fcn_fandom' ) : [];
$characters = $show_terms ? get_the_terms( $post, 'fcn_character' ) : [];
$genres = $show_terms ? get_the_terms( $post, 'fcn_genre' ) : [];
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_recommendation_cards' ) ) ?
get_the_tags( $post ) : false;
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
// Extra classes <?php
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) { // Setup
$card_classes[] = '_' . get_theme_mod( 'card_style' ); $post_id = $post->ID;
} $title = fictioneer_get_safe_title( $post_id, 'shortcode-latest-recommendations' );
$one_sentence = get_post_meta( $post_id, 'fictioneer_recommendation_one_sentence', true );
$fandoms = $show_terms ? get_the_terms( $post, 'fcn_fandom' ) : [];
$characters = $show_terms ? get_the_terms( $post, 'fcn_character' ) : [];
$genres = $show_terms ? get_the_terms( $post, 'fcn_genre' ) : [];
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_recommendation_cards' ) ) ?
get_the_tags( $post ) : false;
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
if ( $args['vertical'] ) { // Extra classes
$card_classes[] = '_vertical'; if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
} $card_classes[] = '_' . get_theme_mod( 'card_style' );
}
if ( $args['seamless'] ) { if ( $args['vertical'] ) {
$card_classes[] = '_seamless'; $card_classes[] = '_vertical';
} }
if ( ! $show_terms ) { if ( $args['seamless'] ) {
$card_classes[] = '_no-tax'; $card_classes[] = '_seamless';
} }
// Truncate factor if ( ! $show_terms ) {
$truncate_factor = $args['vertical'] ? '_4-4' : '_3-3'; $card_classes[] = '_no-tax';
}
// Sources if ( $splide ) {
$urls = array_merge( $card_classes[] = 'splide__slide';
explode( "\n", get_post_meta( $post_id, 'fictioneer_recommendation_urls', true ) ), }
explode( "\n", get_post_meta( $post_id, 'fictioneer_recommendation_support', true ) )
);
// Sanitize // Truncate factor
$urls = array_map( 'wp_strip_all_tags', $urls ); $truncate_factor = $args['vertical'] ? '_4-4' : '_3-3';
// Remove empty nodes // Sources
$urls = array_filter( $urls ); $urls = array_merge(
explode( "\n", get_post_meta( $post_id, 'fictioneer_recommendation_urls', true ) ),
explode( "\n", get_post_meta( $post_id, 'fictioneer_recommendation_support', true ) )
);
// Abort if no links found // Sanitize
if ( empty( $urls ) ) { $urls = array_map( 'wp_strip_all_tags', $urls );
continue;
}
// Extract first link // Remove empty nodes
$url = $urls[0]; $urls = array_filter( $urls );
$tuple = explode( '|', $url );
$tuple = array_map( 'trim', $tuple );
// Card attributes // Abort if no links found
$attributes = []; if ( empty( $urls ) ) {
continue;
}
if ( $args['aspect_ratio'] ) { // Extract first link
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio']; $url = $urls[0];
} $tuple = explode( '|', $url );
$tuple = array_map( 'trim', $tuple );
$attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-recommendations' ); // Card attributes
$attributes = [];
$card_attributes = ''; if ( $args['aspect_ratio'] ) {
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
}
foreach ( $attributes as $key => $value ) { $attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-recommendations' );
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
<li class="post-<?php echo $post_id; ?> card _small _recommendation _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>> $card_attributes = '';
<div class="card__body polygon">
<div class="card__main <?php echo $grid_or_vertical; ?> _small"> foreach ( $attributes as $key => $value ) {
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
<?php <li class="post-<?php echo $post_id; ?> card _small _recommendation _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
do_action( 'fictioneer_shortcode_latest_recommendations_card_body', $post, $args ); <div class="card__body polygon">
if ( $args['thumbnail'] ) { <div class="card__main <?php echo $grid_or_vertical; ?> _small">
fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $title,
'classes' => 'card__image cell-img',
'permalink' => get_permalink(),
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio']
)
);
}
?>
<h3 class="card__title _small cell-title"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php echo $title; ?></a></h3> <?php
do_action( 'fictioneer_shortcode_latest_recommendations_card_body', $post, $args );
<div class="card__content _small cell-meta text-overflow-ellipsis"> if ( $args['thumbnail'] ) {
<span class="card__by-author"><?php fictioneer_render_thumbnail(
printf( array(
_x( 'by %s on', 'Small card: by {Author} on.', 'fictioneer' ), 'post_id' => $post_id,
'<span class="author">' . get_post_meta( $post_id, 'fictioneer_recommendation_author', true ) . '</span>' 'title' => $title,
); 'classes' => 'card__image cell-img',
?></span> 'permalink' => get_permalink(),
<a href="<?php echo esc_url( $tuple[1] ); ?>" rel="noopener" target="_blank" class="bold-link"><?php 'lightbox' => $args['lightbox'],
echo $tuple[0]; 'vertical' => $args['vertical'],
?></a> 'seamless' => $args['seamless'],
</div> 'aspect_ratio' => $args['aspect_ratio']
)
<div class="card__content _small cell-desc"> );
<div class="truncate <?php echo $truncate_factor; ?>"><?php
if ( ! empty( $one_sentence ) ) {
echo wp_strip_all_tags( $one_sentence, true );
} else {
echo fictioneer_first_paragraph_as_excerpt( get_the_content() );
} }
?></div> ?>
</div>
<?php if ( $show_terms ) : ?> <h3 class="card__title _small cell-title"><a href="<?php the_permalink(); ?>" class="truncate _1-1"><?php echo $title; ?></a></h3>
<div class="card__tag-list _small _scrolling cell-tax">
<div class="card__h-scroll <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>">
<?php
if ( $fandoms || $characters || $genres || $tags ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
$terms = array_merge( <div class="card__content _small cell-meta text-overflow-ellipsis">
$fandoms ? fictioneer_get_term_nodes( $fandoms, "{$variant} _fandom" ) : [], <span class="card__by-author"><?php
$genres ? fictioneer_get_term_nodes( $genres, "{$variant} _genre" ) : [], printf(
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [], _x( 'by %s on', 'Small card: by {Author} on.', 'fictioneer' ),
$characters ? fictioneer_get_term_nodes( $characters, "{$variant} _character" ) : [] '<span class="author">' . get_post_meta( $post_id, 'fictioneer_recommendation_author', true ) . '</span>'
); );
?></span>
$terms = apply_filters( <a href="<?php echo esc_url( $tuple[1] ); ?>" rel="noopener" target="_blank" class="bold-link"><?php
'fictioneer_filter_shortcode_latest_recommendations_terms', echo $tuple[0];
$terms, $post, $args, null ?></a>
);
// Implode with separator
echo implode(
fictioneer_get_bullet_separator( 'latest-recommendations', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
} else {
?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php
}
?>
</div>
</div> </div>
<?php endif; ?>
<div class="card__content _small cell-desc">
<div class="truncate <?php echo $truncate_factor; ?>"><?php
if ( ! empty( $one_sentence ) ) {
echo wp_strip_all_tags( $one_sentence, true );
} else {
echo fictioneer_first_paragraph_as_excerpt( get_the_content() );
}
?></div>
</div>
<?php if ( $show_terms ) : ?>
<div class="card__tag-list _small _scrolling cell-tax">
<div class="card__h-scroll <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>">
<?php
if ( $fandoms || $characters || $genres || $tags ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
$terms = array_merge(
$fandoms ? fictioneer_get_term_nodes( $fandoms, "{$variant} _fandom" ) : [],
$genres ? fictioneer_get_term_nodes( $genres, "{$variant} _genre" ) : [],
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$characters ? fictioneer_get_term_nodes( $characters, "{$variant} _character" ) : []
);
$terms = apply_filters(
'fictioneer_filter_shortcode_latest_recommendations_terms',
$terms, $post, $args, null
);
// Implode with separator
echo implode(
fictioneer_get_bullet_separator( 'latest-recommendations', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
} else {
?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php
}
?>
</div>
</div>
<?php endif; ?>
</div>
</div> </div>
</li>
</div> <?php endwhile; ?>
</li> </ul>
<?php endwhile; ?> <?php else : ?>
</ul>
<?php else : ?> <div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div> <?php endif; wp_reset_postdata(); ?>
<?php endif; wp_reset_postdata(); ?>
<?php if ( $splide ) { echo '</div>'; } ?>
</section> </section>

View File

@ -30,13 +30,14 @@
* @internal $args['thumbnail'] Whether the image is rendered. Default true (Customizer). * @internal $args['thumbnail'] Whether the image is rendered. Default true (Customizer).
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['infobox'] Whether to show the info box and toggle. Default true. * @internal $args['infobox'] Whether to show the info box and toggle. Default true.
* @internal $args['terms'] Either inline, pills, none, or false. Default inline. * @internal $args['terms'] Either inline, pills, none, or false. Default inline.
* @internal $args['max_terms'] Maximum number of shown taxonomies. Default 10. * @internal $args['max_terms'] Maximum number of shown taxonomies. Default 10.
* @internal $args['footer_chapters'] Whether to show the chapter count. Default true. * @internal $args['footer_chapters'] Whether to show the chapter count. Default true.
* @internal $args['footer_words'] Whether to show the word count. Default true. * @internal $args['footer_words'] Whether to show the word count. Default true.
* @internal $args['footer_date'] Whether to show the date. Default true. * @internal $args['footer_date'] Whether to show the date. Default true.
* @internal $args['footer_status'] Whether to show the status. Default true. * @internal $args['footer_status'] Whether to show the status. Default true.
* @internal $args['footer_rating'] Whether to show the age rating. Default true. * @internal $args['footer_rating'] Whether to show the age rating. Default true.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -44,6 +45,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$show_terms = ! in_array( $args['terms'], ['none', 'false'] ) && $show_terms = ! in_array( $args['terms'], ['none', 'false'] ) &&
! get_option( 'fictioneer_hide_taxonomies_on_story_cards' ); ! get_option( 'fictioneer_hide_taxonomies_on_story_cards' );
@ -122,251 +124,273 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="small-card-block latest-stories _compact <?php echo $args['classes']; ?>"> <section class="small-card-block latest-stories _compact <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="grid-columns _collapse-on-mobile"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php if ( $entries->have_posts() ) : ?>
<?php <ul class="grid-columns _collapse-on-mobile <?php if ( $splide ) { echo 'splide__list'; } ?>">
// Setup <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
$post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$story_link = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$is_sticky = FICTIONEER_ENABLE_STICKY_CARDS && get_post_meta( $post_id, 'fictioneer_story_sticky', true );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
// Description <?php
$short_description = fictioneer_first_paragraph_as_excerpt( // Setup
fictioneer_get_content_field( 'fictioneer_story_short_description', $post_id ) $post_id = $post->ID;
); $story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$short_description = mb_strlen( $short_description, 'UTF-8' ) < 30 ? get_the_excerpt() : $short_description; $story_link = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$is_sticky = FICTIONEER_ENABLE_STICKY_CARDS && get_post_meta( $post_id, 'fictioneer_story_sticky', true );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
// Extra classes // Description
if ( $show_terms ) { $short_description = fictioneer_first_paragraph_as_excerpt(
$card_classes[] = '_info'; fictioneer_get_content_field( 'fictioneer_story_short_description', $post_id )
} );
$short_description = mb_strlen( $short_description, 'UTF-8' ) < 30 ? get_the_excerpt() : $short_description;
if ( $is_sticky ) { // Extra classes
$card_classes[] = '_sticky'; if ( $show_terms ) {
} $card_classes[] = '_info';
}
if ( ! empty( $post->post_password ) ) { if ( $is_sticky ) {
$card_classes[] = '_password'; $card_classes[] = '_sticky';
} }
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) { if ( ! empty( $post->post_password ) ) {
$card_classes[] = '_' . get_theme_mod( 'card_style' ); $card_classes[] = '_password';
} }
if ( $args['vertical'] ) { if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
$card_classes[] = '_vertical'; $card_classes[] = '_' . get_theme_mod( 'card_style' );
} }
if ( $args['seamless'] ) { if ( $args['vertical'] ) {
$card_classes[] = '_seamless'; $card_classes[] = '_vertical';
} }
if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) { if ( $args['seamless'] ) {
$card_classes[] = '_no-tax'; $card_classes[] = '_seamless';
} }
if ( ! $args['footer'] ) { if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) {
$card_classes[] = '_no-footer'; $card_classes[] = '_no-tax';
} }
if ( ! $args['footer_chapters'] ) { if ( ! $args['footer'] ) {
$card_classes[] = '_no-footer-chapters'; $card_classes[] = '_no-footer';
} }
if ( ! $args['footer_words'] ) { if ( ! $args['footer_chapters'] ) {
$card_classes[] = '_no-footer-words'; $card_classes[] = '_no-footer-chapters';
} }
if ( ! $args['footer_date'] ) { if ( ! $args['footer_words'] ) {
$card_classes[] = '_no-footer-date'; $card_classes[] = '_no-footer-words';
} }
if ( ! $args['footer_status'] ) { if ( ! $args['footer_date'] ) {
$card_classes[] = '_no-footer-status'; $card_classes[] = '_no-footer-date';
} }
if ( ! $args['footer_rating'] ) { if ( ! $args['footer_status'] ) {
$card_classes[] = '_no-footer-rating'; $card_classes[] = '_no-footer-status';
} }
// Truncate factor if ( ! $args['footer_rating'] ) {
$truncate_factor = $args['vertical'] ? '_4-4' : '_cq-3-4'; $card_classes[] = '_no-footer-rating';
}
// Card attributes if ( $splide ) {
$attributes = []; $card_classes[] = 'splide__slide';
}
if ( $args['aspect_ratio'] ) { // Truncate factor
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio']; $truncate_factor = $args['vertical'] ? '_4-4' : '_cq-3-4';
}
$attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-stories-compact' ); // Card attributes
$attributes = [];
$card_attributes = ''; if ( $args['aspect_ratio'] ) {
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
}
foreach ( $attributes as $key => $value ) { $attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-stories-compact' );
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
<li class="post-<?php echo $post_id; ?> card watch-last-clicked _small _story _compact _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>> $card_attributes = '';
<div class="card__body polygon">
<?php if ( $args['infobox'] ) : ?> foreach ( $attributes as $key => $value ) {
<button class="card__info-toggle toggle-last-clicked" aria-label="<?php esc_attr_e( 'Open info box', 'fictioneer' ); ?>"><i class="fa-solid fa-chevron-down"></i></button> $card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
<?php endif; ?> }
?>
<div class="card__main <?php echo $grid_or_vertical; ?> _small"> <li class="post-<?php echo $post_id; ?> card watch-last-clicked _small _story _compact _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
<div class="card__body polygon">
<?php <?php if ( $args['infobox'] ) : ?>
do_action( 'fictioneer_shortcode_latest_stories_card_body', $post, $story, $args ); <button class="card__info-toggle toggle-last-clicked" aria-label="<?php esc_attr_e( 'Open info box', 'fictioneer' ); ?>"><i class="fa-solid fa-chevron-down"></i></button>
<?php endif; ?>
if ( $args['thumbnail'] ) { <div class="card__main <?php echo $grid_or_vertical; ?> _small">
fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $story['title'],
'classes' => 'card__image cell-img',
'permalink' => $story_link,
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio']
)
);
}
?>
<h3 class="card__title _small cell-title"><a href="<?php echo $story_link; ?>" class="truncate _1-1"><?php <?php
if ( ! empty( $post->post_password ) ) { do_action( 'fictioneer_shortcode_latest_stories_card_body', $post, $story, $args );
echo '<i class="fa-solid fa-lock protected-icon"></i> ';
}
echo $story['title']; if ( $args['thumbnail'] ) {
?></a></h3> fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $story['title'],
'classes' => 'card__image cell-img',
'permalink' => $story_link,
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio']
)
);
}
?>
<div class="card__content _small cell-desc"> <h3 class="card__title _small cell-title"><a href="<?php echo $story_link; ?>" class="truncate _1-1"><?php
<div class="truncate <?php echo $truncate_factor; ?>"> if ( ! empty( $post->post_password ) ) {
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] ) : ?> echo '<i class="fa-solid fa-lock protected-icon"></i> ';
<span class="card__by-author"><?php }
printf( _x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ), fictioneer_get_author_node() );
?></span> echo $story['title'];
<?php endif; ?> ?></a></h3>
<span><?php echo $short_description; ?></span>
<div class="card__content _small cell-desc">
<div class="truncate <?php echo $truncate_factor; ?>">
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] ) : ?>
<span class="card__by-author"><?php
printf( _x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ), fictioneer_get_author_node() );
?></span>
<?php endif; ?>
<span><?php echo $short_description; ?></span>
</div>
</div> </div>
</div> </div>
<?php if ( $args['infobox'] ) : ?>
<div class="card__overlay-infobox escape-last-click">
<?php if ( $show_terms ) : ?>
<div class="card__tag-list _small <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>">
<?php
if ( $story['has_taxonomies'] || $tags ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
$terms = array_merge(
$story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [],
$story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [],
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
);
$terms = apply_filters(
'fictioneer_filter_shortcode_latest_stories_terms',
$terms, $post, $args, $story
);
// Implode with separator
echo implode(
fictioneer_get_bullet_separator( 'latest-stories-compact', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
} else {
?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php
}
?>
</div>
<?php else : ?>
<div class="card__overlay-description _small"><?php echo $short_description; ?></div>
<?php endif; ?>
<?php if ( $args['footer'] ) : ?>
<div class="card__footer _small">
<div class="card__footer-box _left text-overflow-ellipsis"><?php
// Build footer items
$footer_items = [];
if ( $args['footer_chapters'] && ( $story['status'] !== 'Oneshot' || $story['chapter_count'] > 1 ) ) {
$footer_items['chapters'] = '<span class="card__footer-chapters"><i class="card-footer-icon fa-solid fa-list" title="' . esc_attr__( 'Chapters', 'fictioneer' ) . '"></i> ' . $story['chapter_count'] . '</span>';
}
if ( $args['footer_words'] && ( $story['word_count'] > 2000 || $story['status'] === 'Oneshot' ) ) {
$footer_items['words'] = '<span class="card__footer-words"><i class="card-footer-icon fa-solid fa-font" title="' . esc_attr__( 'Total Words', 'fictioneer' ) . '"></i> ' . $story['word_count_short'] . '</span>';
}
if ( $args['footer_date'] ) {
$format = $args['date_format'] ?: FICTIONEER_LATEST_STORIES_FOOTER_DATE;
if ( $args['orderby'] === 'modified' ) {
$footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>';
} else {
$footer_items['publish_date'] = '<span class="card__footer-publish-date"><i class="card-footer-icon fa-solid fa-clock" title="' . esc_attr__( 'Published', 'fictioneer' ) . '"></i> ' . get_the_date( $format, $post ) . '</span>';
}
}
if ( $args['footer_status'] ) {
$footer_items['status'] = '<span class="card__footer-status"><i class="card-footer-icon ' . $story['icon'] . '"></i> ' . fcntr( $story['status'] ) . '</span>';
}
// Filter footer items
$footer_items = apply_filters(
'fictioneer_filter_shortcode_latest_stories_card_footer',
$footer_items,
$post,
$story,
$args
);
// Implode and render footer items
echo implode( ' ', $footer_items );
?></div>
<?php if ( $args['footer_rating'] ) : ?>
<div class="card__footer-box _right rating-letter-label tooltipped" data-tooltip="<?php echo fcntr( $story['rating'], true ); ?>">
<?php echo fcntr( $story['rating_letter'] ); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div> </div>
</li>
<?php if ( $args['infobox'] ) : ?> <?php endwhile; ?>
<div class="card__overlay-infobox escape-last-click"> </ul>
<?php if ( $show_terms ) : ?> <?php else : ?>
<div class="card__tag-list _small <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>">
<?php
if ( $story['has_taxonomies'] || $tags ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
$terms = array_merge( <div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
$story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [],
$story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [],
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
);
$terms = apply_filters( <?php endif; wp_reset_postdata(); ?>
'fictioneer_filter_shortcode_latest_stories_terms',
$terms, $post, $args, $story
);
// Implode with separator <?php if ( $splide ) { echo '</div>'; } ?>
echo implode(
fictioneer_get_bullet_separator( 'latest-stories-compact', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
} else {
?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php
}
?>
</div>
<?php else : ?>
<div class="card__overlay-description _small"><?php echo $short_description; ?></div>
<?php endif; ?>
<?php if ( $args['footer'] ) : ?>
<div class="card__footer _small">
<div class="card__footer-box _left text-overflow-ellipsis"><?php
// Build footer items
$footer_items = [];
if ( $args['footer_chapters'] && ( $story['status'] !== 'Oneshot' || $story['chapter_count'] > 1 ) ) {
$footer_items['chapters'] = '<span class="card__footer-chapters"><i class="card-footer-icon fa-solid fa-list" title="' . esc_attr__( 'Chapters', 'fictioneer' ) . '"></i> ' . $story['chapter_count'] . '</span>';
}
if ( $args['footer_words'] && ( $story['word_count'] > 2000 || $story['status'] === 'Oneshot' ) ) {
$footer_items['words'] = '<span class="card__footer-words"><i class="card-footer-icon fa-solid fa-font" title="' . esc_attr__( 'Total Words', 'fictioneer' ) . '"></i> ' . $story['word_count_short'] . '</span>';
}
if ( $args['footer_date'] ) {
$format = $args['date_format'] ?: FICTIONEER_LATEST_STORIES_FOOTER_DATE;
if ( $args['orderby'] === 'modified' ) {
$footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>';
} else {
$footer_items['publish_date'] = '<span class="card__footer-publish-date"><i class="card-footer-icon fa-solid fa-clock" title="' . esc_attr__( 'Published', 'fictioneer' ) . '"></i> ' . get_the_date( $format, $post ) . '</span>';
}
}
if ( $args['footer_status'] ) {
$footer_items['status'] = '<span class="card__footer-status"><i class="card-footer-icon ' . $story['icon'] . '"></i> ' . fcntr( $story['status'] ) . '</span>';
}
// Filter footer items
$footer_items = apply_filters(
'fictioneer_filter_shortcode_latest_stories_card_footer',
$footer_items,
$post,
$story,
$args
);
// Implode and render footer items
echo implode( ' ', $footer_items );
?></div>
<?php if ( $args['footer_rating'] ) : ?>
<div class="card__footer-box _right rating-letter-label tooltipped" data-tooltip="<?php echo fcntr( $story['rating'], true ); ?>">
<?php echo fcntr( $story['rating_letter'] ); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</li>
<?php endwhile; ?>
</ul>
<?php else : ?>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<?php endif; wp_reset_postdata(); ?>
</section> </section>

View File

@ -38,6 +38,7 @@
* @internal $args['footer_status'] Whether to show the story status. Default true. * @internal $args['footer_status'] Whether to show the story status. Default true.
* @internal $args['footer_rating'] Whether to show the story age rating. Default true. * @internal $args['footer_rating'] Whether to show the story age rating. Default true.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -45,6 +46,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$show_terms = ! in_array( $args['terms'], ['none', 'false'] ); $show_terms = ! in_array( $args['terms'], ['none', 'false'] );
$content_list_style = get_theme_mod( 'content_list_style', 'default' ); $content_list_style = get_theme_mod( 'content_list_style', 'default' );
@ -123,191 +125,214 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="latest-stories _list <?php echo $args['classes']; ?>"> <section class="latest-stories _list <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="post-list _latest-stories"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php <?php if ( $entries->have_posts() ) : ?>
// Setup
$post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$permalink = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$is_sticky = FICTIONEER_ENABLE_STICKY_CARDS && get_post_meta( $post_id, 'fictioneer_story_sticky', true );
// Thumbnail <ul class="post-list _latest-stories <?php if ( $splide ) { echo 'splide__list'; } ?>">
$thumbnail = null; <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
if ( $args['thumbnail'] ) {
$thumbnail = fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $story['title'],
'permalink' => $permalink,
'size' => 'snippet',
'classes' => 'post-list-item__image',
'lightbox' => $args['lightbox'],
'aspect_ratio' => $args['aspect_ratio'] ?? '2/2.5'
),
false
);
}
// Extra classes
$classes = [];
if ( $is_sticky ) {
$classes[] = '_sticky';
}
if ( ! empty( $post->post_password ) ) {
$classes[] = '_password';
}
if ( $args['seamless'] ) {
$classes[] = '_seamless';
}
if ( $content_list_style !== 'default' ) {
$classes[] = "_{$content_list_style}";
}
if ( ! $thumbnail ) {
$classes[] = '_no-thumbnail';
}
if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) {
$classes[] = '_no-tax';
}
if ( ! $args['footer'] ) {
$classes[] = '_no-footer';
}
if ( ! $args['footer_author'] && ! $args['source'] ) {
$classes[] = '_no-footer-author';
}
if ( ! $args['footer_words'] ) {
$classes[] = '_no-footer-words';
}
if ( ! $args['footer_date'] ) {
$classes[] = '_no-footer-date';
}
if ( ! $args['footer_status'] ) {
$classes[] = '_no-footer-status';
}
if ( ! $args['footer_rating'] ) {
$classes[] = '_no-footer-rating';
}
// Meta
$meta = [];
if ( $story['word_count'] > 0 && $args['footer_words'] ) {
$meta['words'] = '<span class="post-item-item__meta-words">' . sprintf(
_x( '%s&nbsp;Words', 'Word count in Latest * shortcode (type: list).', 'fictioneer' ),
number_format_i18n( $story['word_count'] )
) . '</span>';
}
if ( $args['footer_status'] ) {
$meta['status'] = '<span class="post-item-item__meta-status">' . fcntr( $story['status'] ) . '</span>';
}
if ( $story['rating'] && $args['footer_rating'] ) {
$meta['rating'] = '<span class="post-item-item__meta-rating">' . fcntr( $story['rating'] ) . '</span>';
}
if ( get_option( 'fictioneer_show_authors' ) && $args['footer_author'] && $args['source'] ) {
$author = fictioneer_get_author_node( $post->post_author, 'post-item-item__meta-author' );
if ( $author ) {
$meta['author'] = $author;
}
}
if ( $args['footer_date'] ) {
$meta['publish_date'] = '<span class="post-item-item__meta-publish-date _floating-right">' . get_the_date( $args['date_format'], $post ) . '</span>';
}
$meta = apply_filters( 'fictioneer_filter_shortcode_latest_stories_list_meta', $meta, $post, $story );
// Attributes
$attributes = [];
if ( $args['aspect_ratio'] ) {
$attributes['style'] = '--post-item-image-aspect-ratio: ' . $args['aspect_ratio'];
}
$attributes = apply_filters( 'fictioneer_filter_shortcode_list_attributes', $attributes, $post, 'latest-stories' );
$output_attributes = '';
foreach ( $attributes as $key => $value ) {
$output_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
<li class="post-<?php echo $post_id; ?> post-list-item _latest-stories <?php echo implode( ' ', $classes ); ?>" <?php echo $output_attributes; ?>>
<?php <?php
if ( $thumbnail ) { // Setup
echo $thumbnail; $post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$permalink = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$is_sticky = FICTIONEER_ENABLE_STICKY_CARDS && get_post_meta( $post_id, 'fictioneer_story_sticky', true );
// Thumbnail
$thumbnail = null;
if ( $args['thumbnail'] ) {
$thumbnail = fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $story['title'],
'permalink' => $permalink,
'size' => 'snippet',
'classes' => 'post-list-item__image',
'lightbox' => $args['lightbox'],
'aspect_ratio' => $args['aspect_ratio'] ?? '2/2.5'
),
false
);
} }
// Extra classes
$classes = [];
if ( $is_sticky ) {
$classes[] = '_sticky';
}
if ( ! empty( $post->post_password ) ) {
$classes[] = '_password';
}
if ( $args['seamless'] ) {
$classes[] = '_seamless';
}
if ( $content_list_style !== 'default' ) {
$classes[] = "_{$content_list_style}";
}
if ( ! $thumbnail ) {
$classes[] = '_no-thumbnail';
}
if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) {
$classes[] = '_no-tax';
}
if ( ! $args['footer'] ) {
$classes[] = '_no-footer';
}
if ( ! $args['footer_author'] && ! $args['source'] ) {
$classes[] = '_no-footer-author';
}
if ( ! $args['footer_words'] ) {
$classes[] = '_no-footer-words';
}
if ( ! $args['footer_date'] ) {
$classes[] = '_no-footer-date';
}
if ( ! $args['footer_status'] ) {
$classes[] = '_no-footer-status';
}
if ( ! $args['footer_rating'] ) {
$classes[] = '_no-footer-rating';
}
if ( $splide ) {
$classes[] = 'splide__slide';
}
// Meta
$meta = [];
if ( $story['word_count'] > 0 && $args['footer_words'] ) {
$meta['words'] = '<span class="post-item-item__meta-words">' . sprintf(
_x( '%s&nbsp;Words', 'Word count in Latest * shortcode (type: list).', 'fictioneer' ),
number_format_i18n( $story['word_count'] )
) . '</span>';
}
if ( $args['footer_status'] ) {
$meta['status'] = '<span class="post-item-item__meta-status">' . fcntr( $story['status'] ) . '</span>';
}
if ( $story['rating'] && $args['footer_rating'] ) {
$meta['rating'] = '<span class="post-item-item__meta-rating">' . fcntr( $story['rating'] ) . '</span>';
}
if ( get_option( 'fictioneer_show_authors' ) && $args['footer_author'] && $args['source'] ) {
$author = fictioneer_get_author_node( $post->post_author, 'post-item-item__meta-author' );
if ( $author ) {
$meta['author'] = $author;
}
}
if ( $args['footer_date'] ) {
$meta['publish_date'] = '<span class="post-item-item__meta-publish-date _floating-right">' . get_the_date( $args['date_format'], $post ) . '</span>';
}
$meta = apply_filters( 'fictioneer_filter_shortcode_latest_stories_list_meta', $meta, $post, $story );
// Attributes
$attributes = [];
if ( $args['aspect_ratio'] ) {
$attributes['style'] = '--post-item-image-aspect-ratio: ' . $args['aspect_ratio'];
}
$attributes = apply_filters( 'fictioneer_filter_shortcode_list_attributes', $attributes, $post, 'latest-stories' );
$output_attributes = '';
foreach ( $attributes as $key => $value ) {
$output_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?> ?>
<a href="<?php echo $permalink; ?>" class="post-list-item__title _link"><?php <li class="post-<?php echo $post_id; ?> post-list-item _latest-stories <?php echo implode( ' ', $classes ); ?>" <?php echo $output_attributes; ?>>
$html_title = empty( $post->post_password ) ? '' : '<i class="fa-solid fa-lock protected-icon"></i> ';
$html_title .= $story['title'];
echo apply_filters( 'fictioneer_filter_shortcode_list_title', $story['title'], $post, 'shortcode-latest-updates-list' ); <?php
?></a> if ( $thumbnail ) {
echo $thumbnail;
}
?>
<?php if ( $args['footer'] ) : ?> <a href="<?php echo $permalink; ?>" class="post-list-item__title _link"><?php
<div class="post-list-item__meta pseudo-separator"><?php echo implode( ' ', $meta ); ?></div> $html_title = empty( $post->post_password ) ? '' : '<i class="fa-solid fa-lock protected-icon"></i> ';
<?php endif; ?> $html_title .= $story['title'];
<?php if ( $show_terms && ( $story['has_taxonomies'] || $tags ) ) : ?> echo apply_filters( 'fictioneer_filter_shortcode_list_title', $story['title'], $post, 'shortcode-latest-updates-list' );
<div class="post-list-item__tax <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>"><?php ?></a>
$variant = $args['terms'] === 'pills' ? '' : '_inline';
$terms = array_merge( <?php if ( $args['footer'] ) : ?>
$story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [], <div class="post-list-item__meta pseudo-separator"><?php echo implode( ' ', $meta ); ?></div>
$story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [], <?php endif; ?>
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
);
$terms = apply_filters( <?php if ( $show_terms && ( $story['has_taxonomies'] || $tags ) ) : ?>
'fictioneer_filter_shortcode_latest_stories_terms', <div class="post-list-item__tax <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>"><?php
$terms, $post, $args, $story $variant = $args['terms'] === 'pills' ? '' : '_inline';
);
// Implode with separator $terms = array_merge(
echo implode( $story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [],
fictioneer_get_bullet_separator( 'latest-stories-list', $args['terms'] === 'pills' ), $story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [],
array_slice( $terms, 0, $args['max_terms'] ) $tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
); $story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
?></div> );
<?php endif; ?>
</li> $terms = apply_filters(
'fictioneer_filter_shortcode_latest_stories_terms',
$terms, $post, $args, $story
);
<?php endwhile; ?> // Implode with separator
</ul> echo implode(
fictioneer_get_bullet_separator( 'latest-stories-list', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
?></div>
<?php endif; ?>
<?php else : ?> </li>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div> <?php endwhile; ?>
</ul>
<?php endif; wp_reset_postdata(); ?> <?php else : ?>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<?php endif; wp_reset_postdata(); ?>
<?php if ( $splide ) { echo '</div>'; } ?>
</section> </section>

View File

@ -38,6 +38,7 @@
* @internal $args['footer_status'] Whether to show the status. Default true. * @internal $args['footer_status'] Whether to show the status. Default true.
* @internal $args['footer_rating'] Whether to show the age rating. Default true. * @internal $args['footer_rating'] Whether to show the age rating. Default true.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -45,6 +46,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$show_terms = ! in_array( $args['terms'], ['none', 'false'] ) && $show_terms = ! in_array( $args['terms'], ['none', 'false'] ) &&
! get_option( 'fictioneer_hide_taxonomies_on_story_cards' ); ! get_option( 'fictioneer_hide_taxonomies_on_story_cards' );
@ -123,235 +125,258 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="small-card-block latest-stories <?php echo $args['classes']; ?>"> <section class="small-card-block latest-stories <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="grid-columns _collapse-on-mobile"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php <?php if ( $entries->have_posts() ) : ?>
// Setup
$post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$story_link = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$is_sticky = FICTIONEER_ENABLE_STICKY_CARDS && get_post_meta( $post_id, 'fictioneer_story_sticky', true );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
if ( $is_sticky ) { <ul class="grid-columns _collapse-on-mobile <?php if ( $splide ) { echo 'splide__list'; } ?>">
$card_classes[] = '_sticky'; <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
}
if ( ! empty( $post->post_password ) ) { <?php
$card_classes[] = '_password'; // Setup
} $post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$story_link = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$is_sticky = FICTIONEER_ENABLE_STICKY_CARDS && get_post_meta( $post_id, 'fictioneer_story_sticky', true );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$card_classes = [];
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) { if ( $is_sticky ) {
$card_classes[] = '_' . get_theme_mod( 'card_style' ); $card_classes[] = '_sticky';
} }
if ( $args['vertical'] ) { if ( ! empty( $post->post_password ) ) {
$card_classes[] = '_vertical'; $card_classes[] = '_password';
} }
if ( $args['seamless'] ) { if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
$card_classes[] = '_seamless'; $card_classes[] = '_' . get_theme_mod( 'card_style' );
} }
if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) { if ( $args['vertical'] ) {
$classes[] = '_no-tax'; $card_classes[] = '_vertical';
} }
if ( ! $args['footer'] ) { if ( $args['seamless'] ) {
$card_classes[] = '_no-footer'; $card_classes[] = '_seamless';
} }
if ( ! $args['footer_chapters'] ) { if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) {
$card_classes[] = '_no-footer-chapters'; $classes[] = '_no-tax';
} }
if ( ! $args['footer_words'] ) { if ( ! $args['footer'] ) {
$card_classes[] = '_no-footer-words'; $card_classes[] = '_no-footer';
} }
if ( ! $args['footer_date'] ) { if ( ! $args['footer_chapters'] ) {
$card_classes[] = '_no-footer-date'; $card_classes[] = '_no-footer-chapters';
} }
if ( ! $args['footer_status'] ) { if ( ! $args['footer_words'] ) {
$card_classes[] = '_no-footer-status'; $card_classes[] = '_no-footer-words';
} }
if ( ! $args['footer_rating'] ) { if ( ! $args['footer_date'] ) {
$card_classes[] = '_no-footer-rating'; $card_classes[] = '_no-footer-date';
} }
// Truncate factor if ( ! $args['footer_status'] ) {
$truncate_factor = $args['vertical'] ? '_4-4' : '_cq-3-4'; $card_classes[] = '_no-footer-status';
}
// Card attributes if ( ! $args['footer_rating'] ) {
$attributes = []; $card_classes[] = '_no-footer-rating';
}
if ( $args['aspect_ratio'] ) { if ( $splide ) {
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio']; $card_classes[] = 'splide__slide';
} }
$attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-stories' ); // Truncate factor
$truncate_factor = $args['vertical'] ? '_4-4' : '_cq-3-4';
$card_attributes = ''; // Card attributes
$attributes = [];
foreach ( $attributes as $key => $value ) { if ( $args['aspect_ratio'] ) {
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" '; $attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
} }
?>
<li class="post-<?php echo $post_id; ?> card _small _story <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>> $attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-stories' );
<div class="card__body polygon">
<div class="card__main <?php echo $grid_or_vertical; ?> _small"> $card_attributes = '';
<?php foreach ( $attributes as $key => $value ) {
do_action( 'fictioneer_shortcode_latest_stories_card_body', $post, $story, $args ); $card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
if ( $args['thumbnail'] ) { <li class="post-<?php echo $post_id; ?> card _small _story <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
fictioneer_render_thumbnail( <div class="card__body polygon">
array(
'post_id' => $post_id,
'title' => $story['title'],
'classes' => 'card__image cell-img',
'permalink' => $story_link,
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio']
)
);
}
?>
<h3 class="card__title _small cell-title"><a href="<?php echo $story_link; ?>" class="truncate _1-1"><?php <div class="card__main <?php echo $grid_or_vertical; ?> _small">
if ( ! empty( $post->post_password ) ) {
echo '<i class="fa-solid fa-lock protected-icon"></i> ';
}
echo $story['title']; <?php
?></a></h3> do_action( 'fictioneer_shortcode_latest_stories_card_body', $post, $story, $args );
<div class="card__content _small cell-desc"> if ( $args['thumbnail'] ) {
<div class="truncate <?php echo $truncate_factor; ?>"> fictioneer_render_thumbnail(
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] ) : ?> array(
<span class="card__by-author"><?php 'post_id' => $post_id,
printf( _x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ), fictioneer_get_author_node() ); 'title' => $story['title'],
?></span> 'classes' => 'card__image cell-img',
<?php endif; ?> 'permalink' => $story_link,
<span><?php 'lightbox' => $args['lightbox'],
$short_description = fictioneer_first_paragraph_as_excerpt( 'vertical' => $args['vertical'],
fictioneer_get_content_field( 'fictioneer_story_short_description', $post_id ) 'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio']
)
); );
echo mb_strlen( $short_description, 'UTF-8' ) < 30 ? get_the_excerpt() : $short_description; }
?></span> ?>
</div>
</div>
<?php if ( $show_terms ) : ?> <h3 class="card__title _small cell-title"><a href="<?php echo $story_link; ?>" class="truncate _1-1"><?php
<div class="card__tag-list _small _scrolling cell-tax"> if ( ! empty( $post->post_password ) ) {
<div class="card__h-scroll <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>"> echo '<i class="fa-solid fa-lock protected-icon"></i> ';
<?php }
if ( $story['has_taxonomies'] || $tags ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
$terms = array_merge( echo $story['title'];
$story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [], ?></a></h3>
$story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [],
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
);
$terms = apply_filters( <div class="card__content _small cell-desc">
'fictioneer_filter_shortcode_latest_stories_terms', <div class="truncate <?php echo $truncate_factor; ?>">
$terms, $post, $args, $story <?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] ) : ?>
); <span class="card__by-author"><?php
printf( _x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ), fictioneer_get_author_node() );
// Implode with separator ?></span>
echo implode( <?php endif; ?>
fictioneer_get_bullet_separator( 'latest-stories', $args['terms'] === 'pills' ), <span><?php
array_slice( $terms, 0, $args['max_terms'] ) $short_description = fictioneer_first_paragraph_as_excerpt(
); fictioneer_get_content_field( 'fictioneer_story_short_description', $post_id )
} else { );
?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php echo mb_strlen( $short_description, 'UTF-8' ) < 30 ? get_the_excerpt() : $short_description;
} ?></span>
?>
</div> </div>
</div> </div>
<?php endif; ?>
<?php if ( $args['footer'] ) : ?> <?php if ( $show_terms ) : ?>
<div class="card__footer cell-footer _small"> <div class="card__tag-list _small _scrolling cell-tax">
<div class="card__h-scroll <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>">
<?php
if ( $story['has_taxonomies'] || $tags ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
<div class="card__footer-box _left text-overflow-ellipsis"><?php $terms = array_merge(
$story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [],
$story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [],
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
);
// Build footer items $terms = apply_filters(
$footer_items = []; 'fictioneer_filter_shortcode_latest_stories_terms',
$terms, $post, $args, $story
);
if ( $args['footer_chapters'] && ( $story['status'] !== 'Oneshot' || $story['chapter_count'] > 1 ) ) { // Implode with separator
$footer_items['chapters'] = '<span class="card__footer-chapters"><i class="card-footer-icon fa-solid fa-list" title="' . esc_attr__( 'Chapters', 'fictioneer' ) . '"></i> ' . $story['chapter_count'] . '</span>'; echo implode(
} fictioneer_get_bullet_separator( 'latest-stories', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
if ( $args['footer_words'] && ( $story['word_count'] > 2000 || $story['status'] === 'Oneshot' ) ) { );
$footer_items['words'] = '<span class="card__footer-words"><i class="card-footer-icon fa-solid fa-font" title="' . esc_attr__( 'Total Words', 'fictioneer' ) . '"></i> ' . $story['word_count_short'] . '</span>'; } else {
} ?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php
}
if ( $args['footer_date'] ) { ?>
$format = $args['date_format'] ?: FICTIONEER_LATEST_STORIES_FOOTER_DATE;
if ( $args['orderby'] === 'modified' ) {
$footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>';
} else {
$footer_items['publish_date'] = '<span class="card__footer-publish-date"><i class="card-footer-icon fa-solid fa-clock" title="' . esc_attr__( 'Published', 'fictioneer' ) . '"></i> ' . get_the_date( $format, $post ) . '</span>';
}
}
if ( $args['footer_status'] ) {
$footer_items['status'] = '<span class="card__footer-status"><i class="card-footer-icon ' . $story['icon'] . '"></i> ' . fcntr( $story['status'] ) . '</span>';
}
// Filter footer items
$footer_items = apply_filters(
'fictioneer_filter_shortcode_latest_stories_card_footer',
$footer_items,
$post,
$story,
$args
);
// Implode and render footer items
echo implode( ' ', $footer_items );
?></div>
<?php if ( $args['footer_rating'] ) : ?>
<div class="card__footer-box _right rating-letter-label tooltipped" data-tooltip="<?php echo fcntr( $story['rating'], true ); ?>">
<?php echo fcntr( $story['rating_letter'] ); ?>
</div> </div>
<?php endif; ?> </div>
<?php endif; ?>
</div> <?php if ( $args['footer'] ) : ?>
<?php endif; ?> <div class="card__footer cell-footer _small">
<div class="card__footer-box _left text-overflow-ellipsis"><?php
// Build footer items
$footer_items = [];
if ( $args['footer_chapters'] && ( $story['status'] !== 'Oneshot' || $story['chapter_count'] > 1 ) ) {
$footer_items['chapters'] = '<span class="card__footer-chapters"><i class="card-footer-icon fa-solid fa-list" title="' . esc_attr__( 'Chapters', 'fictioneer' ) . '"></i> ' . $story['chapter_count'] . '</span>';
}
if ( $args['footer_words'] && ( $story['word_count'] > 2000 || $story['status'] === 'Oneshot' ) ) {
$footer_items['words'] = '<span class="card__footer-words"><i class="card-footer-icon fa-solid fa-font" title="' . esc_attr__( 'Total Words', 'fictioneer' ) . '"></i> ' . $story['word_count_short'] . '</span>';
}
if ( $args['footer_date'] ) {
$format = $args['date_format'] ?: FICTIONEER_LATEST_STORIES_FOOTER_DATE;
if ( $args['orderby'] === 'modified' ) {
$footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>';
} else {
$footer_items['publish_date'] = '<span class="card__footer-publish-date"><i class="card-footer-icon fa-solid fa-clock" title="' . esc_attr__( 'Published', 'fictioneer' ) . '"></i> ' . get_the_date( $format, $post ) . '</span>';
}
}
if ( $args['footer_status'] ) {
$footer_items['status'] = '<span class="card__footer-status"><i class="card-footer-icon ' . $story['icon'] . '"></i> ' . fcntr( $story['status'] ) . '</span>';
}
// Filter footer items
$footer_items = apply_filters(
'fictioneer_filter_shortcode_latest_stories_card_footer',
$footer_items,
$post,
$story,
$args
);
// Implode and render footer items
echo implode( ' ', $footer_items );
?></div>
<?php if ( $args['footer_rating'] ) : ?>
<div class="card__footer-box _right rating-letter-label tooltipped" data-tooltip="<?php echo fcntr( $story['rating'], true ); ?>">
<?php echo fcntr( $story['rating_letter'] ); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div> </div>
</li>
</div> <?php endwhile; ?>
</li> </ul>
<?php endwhile; ?> <?php else : ?>
</ul>
<?php else : ?> <div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div> <?php endif; wp_reset_postdata(); ?>
<?php endif; wp_reset_postdata(); ?> <?php if ( $splide ) { echo '</div>'; } ?>
</section> </section>

View File

@ -41,6 +41,7 @@
* @internal $args['footer_rating'] Whether to show the story age rating. Default true. * @internal $args['footer_rating'] Whether to show the story age rating. Default true.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['infobox'] Whether to show the info box and toggle. * @internal $args['infobox'] Whether to show the info box and toggle.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -48,6 +49,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$card_counter = 0; $card_counter = 0;
// Prepare query // Prepare query
@ -131,225 +133,248 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="small-card-block latest-updates <?php echo $args['classes']; ?>"> <section class="small-card-block latest-updates <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="grid-columns _collapse-on-mobile"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php <?php if ( $entries->have_posts() ) : ?>
// Setup
$post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$story_link = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$chapter_list = [];
$chapter_excerpt; // Set inside inner loop
$chapter_title; // Set inside inner loop
$card_classes = [];
// Skip if no chapters <ul class="grid-columns _collapse-on-mobile <?php if ( $splide ) { echo 'splide__list'; } ?>">
if ( $story['chapter_count'] < 1 ) { <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
continue;
}
// Extra classes <?php
if ( ! empty( $post->post_password ) ) { // Setup
$card_classes[] = '_password'; $post_id = $post->ID;
} $story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$story_link = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$chapter_list = [];
$chapter_excerpt; // Set inside inner loop
$chapter_title; // Set inside inner loop
$card_classes = [];
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) { // Skip if no chapters
$card_classes[] = '_' . get_theme_mod( 'card_style' ); if ( $story['chapter_count'] < 1 ) {
}
if ( $args['vertical'] ) {
$card_classes[] = '_vertical';
}
if ( $args['seamless'] ) {
$card_classes[] = '_seamless';
}
if ( ! $args['words'] ) {
$card_classes[] = '_no-chapter-words';
}
if ( ! $args['date'] ) {
$card_classes[] = '_no-chapter-dates';
}
// Search for viable chapters...
$search_list = array_reverse( $story['chapter_ids'] );
foreach ( $search_list as $chapter_id ) {
$chapter_post = get_post( $chapter_id );
if ( get_post_meta( $chapter_id, 'fictioneer_chapter_hidden', true ) ) {
continue; continue;
} }
if ( $args['ignore_protected'] && $chapter_post->post_password ) { // Extra classes
if ( ! empty( $post->post_password ) ) {
$card_classes[] = '_password';
}
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
$card_classes[] = '_' . get_theme_mod( 'card_style' );
}
if ( $args['vertical'] ) {
$card_classes[] = '_vertical';
}
if ( $args['seamless'] ) {
$card_classes[] = '_seamless';
}
if ( ! $args['words'] ) {
$card_classes[] = '_no-chapter-words';
}
if ( ! $args['date'] ) {
$card_classes[] = '_no-chapter-dates';
}
if ( $splide ) {
$card_classes[] = 'splide__slide';
}
// Search for viable chapters...
$search_list = array_reverse( $story['chapter_ids'] );
foreach ( $search_list as $chapter_id ) {
$chapter_post = get_post( $chapter_id );
if ( get_post_meta( $chapter_id, 'fictioneer_chapter_hidden', true ) ) {
continue;
}
if ( $args['ignore_protected'] && $chapter_post->post_password ) {
continue;
}
$chapter_list[] = $chapter_post;
break; // Only one needed
}
// No viable chapters
if ( count( $chapter_list ) < 1 ) {
continue; continue;
} }
$chapter_list[] = $chapter_post; // Count actually rendered cards to account for buffer
break; // Only one needed if ( ++$card_counter > $args['count'] ) {
} break;
}
// No viable chapters // Chapter excerpt
if ( count( $chapter_list ) < 1 ) { $chapter_excerpt = fictioneer_get_forced_excerpt( $chapter_list[0]->ID, 768 );
continue; $show_excerpt = mb_strlen( str_replace( '…', '', $chapter_excerpt ) ) > 2;
}
// Count actually rendered cards to account for buffer // Truncate factor
if ( ++$card_counter > $args['count'] ) { $truncate_factor = $args['vertical'] ? '_2-2' : '_cq-1-2';
break;
}
// Chapter excerpt // Card attributes
$chapter_excerpt = fictioneer_get_forced_excerpt( $chapter_list[0]->ID, 768 ); $attributes = [];
$show_excerpt = mb_strlen( str_replace( '…', '', $chapter_excerpt ) ) > 2;
// Truncate factor if ( $args['aspect_ratio'] ) {
$truncate_factor = $args['vertical'] ? '_2-2' : '_cq-1-2'; $attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
}
// Card attributes $attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-updates-compact' );
$attributes = [];
if ( $args['aspect_ratio'] ) { $card_attributes = '';
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
}
$attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-updates-compact' ); foreach ( $attributes as $key => $value ) {
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
$card_attributes = ''; <li class="post-<?php echo $post_id; ?> card watch-last-clicked _small _info _story-update _compact _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
<div class="card__body polygon">
foreach ( $attributes as $key => $value ) { <?php if ( $show_excerpt && $args['infobox'] ) : ?>
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" '; <button class="card__info-toggle toggle-last-clicked" aria-label="<?php esc_attr_e( 'Open info box', 'fictioneer' ); ?>"><i class="fa-solid fa-chevron-down"></i></button>
} <?php endif; ?>
?>
<li class="post-<?php echo $post_id; ?> card watch-last-clicked _small _info _story-update _compact _no-footer <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>> <div class="card__main <?php echo $grid_or_vertical; ?> _small">
<div class="card__body polygon">
<?php if ( $show_excerpt && $args['infobox'] ) : ?> <?php
<button class="card__info-toggle toggle-last-clicked" aria-label="<?php esc_attr_e( 'Open info box', 'fictioneer' ); ?>"><i class="fa-solid fa-chevron-down"></i></button> do_action( 'fictioneer_shortcode_latest_updates_card_body', $post, $story, $args );
<?php endif; ?>
<div class="card__main <?php echo $grid_or_vertical; ?> _small"> if ( $args['thumbnail'] ) {
fictioneer_render_thumbnail(
<?php array(
do_action( 'fictioneer_shortcode_latest_updates_card_body', $post, $story, $args ); 'post_id' => $post_id,
'title' => $story['title'],
if ( $args['thumbnail'] ) { 'classes' => 'card__image cell-img',
fictioneer_render_thumbnail( 'permalink' => $story_link,
array( 'lightbox' => $args['lightbox'],
'post_id' => $post_id, 'vertical' => $args['vertical'],
'title' => $story['title'], 'seamless' => $args['seamless'],
'classes' => 'card__image cell-img', 'aspect_ratio' => $args['aspect_ratio']
'permalink' => $story_link, )
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio']
)
);
}
?>
<h3 class="card__title _small cell-title"><a href="<?php echo $story_link; ?>" class="truncate _1-1"><?php
if ( ! empty( $post->post_password ) ) {
echo '<i class="fa-solid fa-lock protected-icon"></i> ';
}
echo $story['title'];
?></a></h3>
<div class="card__content _small cell-desc">
<div class="truncate <?php echo $truncate_factor; ?>">
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] ) : ?>
<span class="card__by-author"><?php
printf( _x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ), fictioneer_get_author_node() );
?></span>
<?php endif; ?>
<span><?php
$short_description = fictioneer_first_paragraph_as_excerpt(
fictioneer_get_content_field( 'fictioneer_story_short_description', $post_id )
); );
echo mb_strlen( $short_description, 'UTF-8' ) < 30 ? get_the_excerpt() : $short_description; }
?></span> ?>
<h3 class="card__title _small cell-title"><a href="<?php echo $story_link; ?>" class="truncate _1-1"><?php
if ( ! empty( $post->post_password ) ) {
echo '<i class="fa-solid fa-lock protected-icon"></i> ';
}
echo $story['title'];
?></a></h3>
<div class="card__content _small cell-desc">
<div class="truncate <?php echo $truncate_factor; ?>">
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] ) : ?>
<span class="card__by-author"><?php
printf( _x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ), fictioneer_get_author_node() );
?></span>
<?php endif; ?>
<span><?php
$short_description = fictioneer_first_paragraph_as_excerpt(
fictioneer_get_content_field( 'fictioneer_story_short_description', $post_id )
);
echo mb_strlen( $short_description, 'UTF-8' ) < 30 ? get_the_excerpt() : $short_description;
?></span>
</div>
</div> </div>
<ol class="card__link-list _small cell-list">
<?php foreach ( $chapter_list as $chapter ) : ?>
<?php
// Chapter title
$list_title = get_post_meta( $chapter->ID, 'fictioneer_chapter_list_title', true );
$list_title = trim( wp_strip_all_tags( $list_title ) );
if ( empty( $list_title ) ) {
$chapter_title = fictioneer_get_safe_title( $chapter->ID, 'shortcode-latest-updates-compact' );
} else {
$chapter_title = $list_title;
}
// Extra classes
$list_item_classes = [];
if ( ! empty( $chapter->post_password ) ) {
$list_item_classes[] = '_password';
}
?>
<li class="card__link-list-item <?php echo implode( ' ', $list_item_classes ); ?>">
<div class="card__left text-overflow-ellipsis">
<i class="fa-solid fa-caret-right"></i>
<a href="<?php the_permalink( $chapter->ID ); ?>" class="card__link-list-link"><?php
echo $chapter_title;
?></a>
</div>
<div class="card__right">
<?php
$words = $args['words'] ? fictioneer_get_word_count( $chapter->ID ) : 0;
if ( $words ) {
echo '<span class="words _words-' . $words . '">' . fictioneer_shorten_number( $words ) . '</span>';
}
if ( $words && $args['date'] ) {
echo fictioneer_get_bullet_separator( 'latest-updates-compact' );
}
if ( $args['date'] ) {
echo '<span class="date">' . get_the_date(
$args['nested_date_format'] ?: FICTIONEER_LATEST_UPDATES_LI_DATE, $chapter->ID
) . '</span>';
}
?>
</div>
</li>
<?php endforeach; ?>
</ol>
</div> </div>
<ol class="card__link-list _small cell-list"> <?php if ( $show_excerpt && $args['infobox'] ) : ?>
<?php foreach ( $chapter_list as $chapter ) : ?> <div class="card__overlay-infobox _excerpt escape-last-click">
<?php <div class="card__excerpt"><strong><?php echo $chapter_title; ?>:</strong> <?php echo $chapter_excerpt; ?></div>
// Chapter title </div>
$list_title = get_post_meta( $chapter->ID, 'fictioneer_chapter_list_title', true ); <?php endif; ?>
$list_title = trim( wp_strip_all_tags( $list_title ) );
if ( empty( $list_title ) ) {
$chapter_title = fictioneer_get_safe_title( $chapter->ID, 'shortcode-latest-updates-compact' );
} else {
$chapter_title = $list_title;
}
// Extra classes
$list_item_classes = [];
if ( ! empty( $chapter->post_password ) ) {
$list_item_classes[] = '_password';
}
?>
<li class="card__link-list-item <?php echo implode( ' ', $list_item_classes ); ?>">
<div class="card__left text-overflow-ellipsis">
<i class="fa-solid fa-caret-right"></i>
<a href="<?php the_permalink( $chapter->ID ); ?>" class="card__link-list-link"><?php
echo $chapter_title;
?></a>
</div>
<div class="card__right">
<?php
$words = $args['words'] ? fictioneer_get_word_count( $chapter->ID ) : 0;
if ( $words ) {
echo '<span class="words _words-' . $words . '">' . fictioneer_shorten_number( $words ) . '</span>';
}
if ( $words && $args['date'] ) {
echo fictioneer_get_bullet_separator( 'latest-updates-compact' );
}
if ( $args['date'] ) {
echo '<span class="date">' . get_the_date(
$args['nested_date_format'] ?: FICTIONEER_LATEST_UPDATES_LI_DATE, $chapter->ID
) . '</span>';
}
?>
</div>
</li>
<?php endforeach; ?>
</ol>
</div> </div>
</li>
<?php if ( $show_excerpt && $args['infobox'] ) : ?> <?php endwhile; ?>
<div class="card__overlay-infobox _excerpt escape-last-click"> </ul>
<div class="card__excerpt"><strong><?php echo $chapter_title; ?>:</strong> <?php echo $chapter_excerpt; ?></div>
</div>
<?php endif; ?>
</div> <?php else : ?>
</li>
<?php endwhile; ?> <div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
</ul>
<?php else : ?> <?php endif; wp_reset_postdata(); ?>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div> <?php if ( $splide ) { echo '</div>'; } ?>
<?php endif; wp_reset_postdata(); ?>
</section> </section>

View File

@ -42,6 +42,7 @@
* @internal $args['footer_date'] Whether to show the chapter date. Default true. * @internal $args['footer_date'] Whether to show the chapter date. Default true.
* @internal $args['footer_rating'] Whether to show the story/chapter age rating. Default true. * @internal $args['footer_rating'] Whether to show the story/chapter age rating. Default true.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -49,6 +50,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$render_count = 0; $render_count = 0;
$show_terms = ! in_array( $args['terms'], ['none', 'false'] ); $show_terms = ! in_array( $args['terms'], ['none', 'false'] );
$content_list_style = get_theme_mod( 'content_list_style', 'default' ); $content_list_style = get_theme_mod( 'content_list_style', 'default' );
@ -134,216 +136,239 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="latest-updates _list <?php echo $args['classes']; ?>"> <section class="latest-updates _list <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="post-list _latest-updates"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php <?php if ( $entries->have_posts() ) : ?>
// Setup
$post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$permalink = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$chapter_list = [];
// Skip if no chapters <ul class="post-list _latest-updates <?php if ( $splide ) { echo 'splide__list'; } ?>">
if ( $story['chapter_count'] < 1 ) { <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
continue;
}
// Count actually rendered items to account for buffer
if ( ++$render_count > $args['count'] ) {
break;
}
// Thumbnail
$thumbnail = null;
if ( $args['thumbnail'] ) {
$thumbnail = fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $story['title'],
'permalink' => $permalink,
'size' => 'snippet',
'classes' => 'post-list-item__image',
'lightbox' => $args['lightbox'],
'aspect_ratio' => $args['aspect_ratio'] ?? '2/2.5'
),
false
);
}
// Extra classes
$classes = [];
if ( ! empty( $post->post_password ) ) {
$classes[] = '_password';
}
if ( $args['seamless'] ) {
$classes[] = '_seamless';
}
if ( $content_list_style !== 'default' ) {
$classes[] = "_{$content_list_style}";
}
if ( ! $thumbnail ) {
$classes[] = '_no-thumbnail';
}
if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) {
$classes[] = '_no-tax';
}
if ( ! $args['footer'] ) {
$classes[] = '_no-footer';
}
if ( ! $args['footer_author'] ) {
$classes[] = '_no-footer-author';
}
if ( ! $args['words'] || ! $args['footer_words'] ) {
$classes[] = '_no-chapter-words';
}
if ( ! $args['date'] || ! $args['footer_date'] ) {
$classes[] = '_no-chapter-dates';
}
if ( ! $args['footer_rating'] ) {
$classes[] = '_no-footer-rating';
}
// Search for viable chapters...
$search_list = array_reverse( $story['chapter_ids'] );
$chapter_post = null;
foreach ( $search_list as $chapter_id ) {
$chapter_post = get_post( $chapter_id );
if ( ! $chapter_post || get_post_meta( $chapter_id, 'fictioneer_chapter_hidden', true ) ) {
continue;
}
if ( $args['ignore_protected'] && $chapter_post->post_password ) {
continue;
}
break;
}
// No viable chapter
if ( ! $chapter_post ) {
continue;
}
// Meta
$meta = [];
$meta['chapter'] = '<a href="' . get_the_permalink( $chapter_post ) . '" class="post-list-item__meta-chapter"><i class="fa-solid fa-caret-right chapter-link-icon"></i> ' . fictioneer_get_safe_title( $chapter_post, 'shortcode-latest-updates-list-chapter' ) . '</a>';
if ( $args['words'] && $args['footer_words'] ) {
$meta['words'] = '<span class="post-item-item__meta-words">' . sprintf(
_x( '%s&nbsp;Words', 'Word count in Latest * shortcode (type: list).', 'fictioneer' ),
number_format_i18n( fictioneer_get_word_count( $chapter_post->ID ) )
) . '</span>';
}
if ( $story['rating'] && $args['footer_rating'] ) {
$meta['rating'] = '<span class="post-item-item__meta-rating">' . fcntr( $story['rating'] ) . '</span>';
}
if ( get_option( 'fictioneer_show_authors' ) && $args['footer_author'] ) {
$author = fictioneer_get_author_node( $chapter_post->post_author, 'post-item-item__meta-author' );
if ( $author ) {
$meta['author'] = $author;
}
}
if ( $args['date'] && $args['footer_date'] ) {
$format = $args['date_format'] ?: $args['nested_date_format'] ?: '';
$meta['date'] = '<span class="post-item-item__meta-publish-date _floating-right">' . get_the_date( $format, $chapter_post->ID ) . '</span>';
}
$meta = apply_filters( 'fictioneer_filter_shortcode_latest_updates_list_meta', $meta, $post, $story, $chapter_post );
// Attributes
$attributes = [];
if ( $args['aspect_ratio'] ) {
$attributes['style'] = '--post-item-image-aspect-ratio: ' . $args['aspect_ratio'];
}
$attributes = apply_filters( 'fictioneer_filter_shortcode_list_attributes', $attributes, $post, 'latest-updates' );
$output_attributes = '';
foreach ( $attributes as $key => $value ) {
$output_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?>
<li class="post-<?php echo $post_id; ?> post-list-item _latest-updates <?php echo implode( ' ', $classes ); ?>" <?php echo $output_attributes; ?>>
<?php <?php
if ( $thumbnail ) { // Setup
echo $thumbnail; $post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$permalink = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$chapter_list = [];
// Skip if no chapters
if ( $story['chapter_count'] < 1 ) {
continue;
} }
// Count actually rendered items to account for buffer
if ( ++$render_count > $args['count'] ) {
break;
}
// Thumbnail
$thumbnail = null;
if ( $args['thumbnail'] ) {
$thumbnail = fictioneer_render_thumbnail(
array(
'post_id' => $post_id,
'title' => $story['title'],
'permalink' => $permalink,
'size' => 'snippet',
'classes' => 'post-list-item__image',
'lightbox' => $args['lightbox'],
'aspect_ratio' => $args['aspect_ratio'] ?? '2/2.5'
),
false
);
}
// Extra classes
$classes = [];
if ( ! empty( $post->post_password ) ) {
$classes[] = '_password';
}
if ( $args['seamless'] ) {
$classes[] = '_seamless';
}
if ( $content_list_style !== 'default' ) {
$classes[] = "_{$content_list_style}";
}
if ( ! $thumbnail ) {
$classes[] = '_no-thumbnail';
}
if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) {
$classes[] = '_no-tax';
}
if ( ! $args['footer'] ) {
$classes[] = '_no-footer';
}
if ( ! $args['footer_author'] ) {
$classes[] = '_no-footer-author';
}
if ( ! $args['words'] || ! $args['footer_words'] ) {
$classes[] = '_no-chapter-words';
}
if ( ! $args['date'] || ! $args['footer_date'] ) {
$classes[] = '_no-chapter-dates';
}
if ( ! $args['footer_rating'] ) {
$classes[] = '_no-footer-rating';
}
if ( $splide ) {
$classes[] = 'splide__slide';
}
// Search for viable chapters...
$search_list = array_reverse( $story['chapter_ids'] );
$chapter_post = null;
foreach ( $search_list as $chapter_id ) {
$chapter_post = get_post( $chapter_id );
if ( ! $chapter_post || get_post_meta( $chapter_id, 'fictioneer_chapter_hidden', true ) ) {
continue;
}
if ( $args['ignore_protected'] && $chapter_post->post_password ) {
continue;
}
break;
}
// No viable chapter
if ( ! $chapter_post ) {
continue;
}
// Meta
$meta = [];
$meta['chapter'] = '<a href="' . get_the_permalink( $chapter_post ) . '" class="post-list-item__meta-chapter"><i class="fa-solid fa-caret-right chapter-link-icon"></i> ' . fictioneer_get_safe_title( $chapter_post, 'shortcode-latest-updates-list-chapter' ) . '</a>';
if ( $args['words'] && $args['footer_words'] ) {
$meta['words'] = '<span class="post-item-item__meta-words">' . sprintf(
_x( '%s&nbsp;Words', 'Word count in Latest * shortcode (type: list).', 'fictioneer' ),
number_format_i18n( fictioneer_get_word_count( $chapter_post->ID ) )
) . '</span>';
}
if ( $story['rating'] && $args['footer_rating'] ) {
$meta['rating'] = '<span class="post-item-item__meta-rating">' . fcntr( $story['rating'] ) . '</span>';
}
if ( get_option( 'fictioneer_show_authors' ) && $args['footer_author'] ) {
$author = fictioneer_get_author_node( $chapter_post->post_author, 'post-item-item__meta-author' );
if ( $author ) {
$meta['author'] = $author;
}
}
if ( $args['date'] && $args['footer_date'] ) {
$format = $args['date_format'] ?: $args['nested_date_format'] ?: '';
$meta['date'] = '<span class="post-item-item__meta-publish-date _floating-right">' . get_the_date( $format, $chapter_post->ID ) . '</span>';
}
$meta = apply_filters( 'fictioneer_filter_shortcode_latest_updates_list_meta', $meta, $post, $story, $chapter_post );
// Attributes
$attributes = [];
if ( $args['aspect_ratio'] ) {
$attributes['style'] = '--post-item-image-aspect-ratio: ' . $args['aspect_ratio'];
}
$attributes = apply_filters( 'fictioneer_filter_shortcode_list_attributes', $attributes, $post, 'latest-updates' );
$output_attributes = '';
foreach ( $attributes as $key => $value ) {
$output_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
}
?> ?>
<a href="<?php echo $permalink; ?>" class="post-list-item__title _link"><?php <li class="post-<?php echo $post_id; ?> post-list-item _latest-updates <?php echo implode( ' ', $classes ); ?>" <?php echo $output_attributes; ?>>
$html_title = empty( $post->post_password ) ? '' : '<i class="fa-solid fa-lock protected-icon"></i> ';
$html_title .= $story['title'];
echo apply_filters( 'fictioneer_filter_shortcode_list_title', $story['title'], $post, 'shortcode-latest-updates-list' ); <?php
?></a> if ( $thumbnail ) {
echo $thumbnail;
}
?>
<?php if ( $args['footer'] ) : ?> <a href="<?php echo $permalink; ?>" class="post-list-item__title _link"><?php
<div class="post-list-item__meta pseudo-separator"><?php echo implode( ' ', $meta ); ?></div> $html_title = empty( $post->post_password ) ? '' : '<i class="fa-solid fa-lock protected-icon"></i> ';
<?php endif; ?> $html_title .= $story['title'];
<?php if ( $show_terms && ( $story['has_taxonomies'] || $tags ) ) : ?> echo apply_filters( 'fictioneer_filter_shortcode_list_title', $story['title'], $post, 'shortcode-latest-updates-list' );
<div class="post-list-item__tax <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>"><?php ?></a>
$variant = $args['terms'] === 'pills' ? '' : '_inline';
$terms = array_merge( <?php if ( $args['footer'] ) : ?>
$story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [], <div class="post-list-item__meta pseudo-separator"><?php echo implode( ' ', $meta ); ?></div>
$story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [], <?php endif; ?>
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
);
$terms = apply_filters( <?php if ( $show_terms && ( $story['has_taxonomies'] || $tags ) ) : ?>
'fictioneer_filter_shortcode_latest_updates_terms', <div class="post-list-item__tax <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>"><?php
$terms, $post, $args, $story $variant = $args['terms'] === 'pills' ? '' : '_inline';
);
// Implode with separator $terms = array_merge(
echo implode( $story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [],
fictioneer_get_bullet_separator( 'latest-stories-list', $args['terms'] === 'pills' ), $story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [],
array_slice( $terms, 0, $args['max_terms'] ) $tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
); $story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
?></div> );
<?php endif; ?>
</li> $terms = apply_filters(
'fictioneer_filter_shortcode_latest_updates_terms',
$terms, $post, $args, $story
);
<?php endwhile; ?> // Implode with separator
</ul> echo implode(
fictioneer_get_bullet_separator( 'latest-stories-list', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
?></div>
<?php endif; ?>
<?php else : ?> </li>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div> <?php endwhile; ?>
</ul>
<?php endif; wp_reset_postdata(); ?> <?php else : ?>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<?php endif; wp_reset_postdata(); ?>
<?php if ( $splide ) { echo '</div>'; } ?>
</section> </section>

View File

@ -44,6 +44,7 @@
* @internal $args['footer_status'] Whether to show the story status. Default true. * @internal $args['footer_status'] Whether to show the story status. Default true.
* @internal $args['footer_rating'] Whether to show the story age rating. Default true. * @internal $args['footer_rating'] Whether to show the story age rating. Default true.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
*/ */
@ -51,6 +52,7 @@
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup // Setup
$splide = $args['splide'] ?? 0;
$card_counter = 0; $card_counter = 0;
$show_terms = ! get_option( 'fictioneer_hide_taxonomies_on_story_cards' ) && $show_terms = ! get_option( 'fictioneer_hide_taxonomies_on_story_cards' ) &&
! in_array( $args['type'], ['simple', 'single'] ) && ! in_array( $args['type'], ['simple', 'single'] ) &&
@ -137,333 +139,356 @@ $entries = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<section class="small-card-block latest-updates <?php echo $args['classes']; ?>"> <section class="small-card-block latest-updates <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<?php if ( $entries->have_posts() ) : ?> <?php
if ( $args['splide'] === false ) {
echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
}
<ul class="grid-columns _collapse-on-mobile"> if ( $splide ) {
<?php while ( $entries->have_posts() ) : $entries->the_post(); ?> echo '<div class="splide__track">';
}
?>
<?php <?php if ( $entries->have_posts() ) : ?>
// Setup
$post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$story_link = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$chapter_list = [];
$card_classes = [];
// Skip if no chapters <ul class="grid-columns _collapse-on-mobile <?php if ( $splide ) { echo 'splide__list'; } ?>">
if ( $story['chapter_count'] < 1 ) { <?php while ( $entries->have_posts() ) : $entries->the_post(); ?>
continue;
}
// Extra classes <?php
if ( ! empty( $post->post_password ) ) { // Setup
$card_classes[] = '_password'; $post_id = $post->ID;
} $story = fictioneer_get_story_data( $post_id, false ); // Does not refresh comment count!
$story_link = get_post_meta( $post_id, 'fictioneer_story_redirect_link', true ) ?: get_permalink( $post_id );
$tags = ( $show_terms && get_option( 'fictioneer_show_tags_on_story_cards' ) ) ? get_the_tags( $post ) : false;
$grid_or_vertical = $args['vertical'] ? '_vertical' : '_grid';
$chapter_list = [];
$card_classes = [];
if ( ! $args['footer'] || in_array( $args['type'], ['simple', 'single'] ) ) { // Skip if no chapters
$card_classes[] = '_no-footer'; if ( $story['chapter_count'] < 1 ) {
}
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
$card_classes[] = '_' . get_theme_mod( 'card_style' );
}
if ( $args['vertical'] ) {
$card_classes[] = '_vertical';
}
if ( $args['seamless'] ) {
$card_classes[] = '_seamless';
}
if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) {
$card_classes[] = '_no-tax';
}
if ( ! $args['words'] ) {
$card_classes[] = '_no-chapter-words';
}
if ( ! $args['date'] ) {
$card_classes[] = '_no-chapter-dates';
}
if ( ! $args['footer_chapters'] ) {
$card_classes[] = '_no-footer-chapters';
}
if ( ! $args['footer_words'] ) {
$card_classes[] = '_no-footer-words';
}
if ( ! $args['footer_date'] ) {
$card_classes[] = '_no-footer-date';
}
if ( ! $args['footer_status'] ) {
$card_classes[] = '_no-footer-status';
}
if ( ! $args['footer_rating'] ) {
$card_classes[] = '_no-footer-rating';
}
// Search for viable chapters...
$search_list = array_reverse( $story['chapter_ids'] );
foreach ( $search_list as $chapter_id ) {
$chapter_post = get_post( $chapter_id );
if ( get_post_meta( $chapter_id, 'fictioneer_chapter_hidden', true ) ) {
continue; continue;
} }
if ( $args['ignore_protected'] && $chapter_post->post_password ) { // Extra classes
if ( ! empty( $post->post_password ) ) {
$card_classes[] = '_password';
}
if ( ! $args['footer'] || in_array( $args['type'], ['simple', 'single'] ) ) {
$card_classes[] = '_no-footer';
}
if ( get_theme_mod( 'card_style', 'default' ) !== 'default' ) {
$card_classes[] = '_' . get_theme_mod( 'card_style' );
}
if ( $args['vertical'] ) {
$card_classes[] = '_vertical';
}
if ( $args['seamless'] ) {
$card_classes[] = '_seamless';
}
if ( ! $show_terms || ! ( $story['has_taxonomies'] || $tags ) ) {
$card_classes[] = '_no-tax';
}
if ( ! $args['words'] ) {
$card_classes[] = '_no-chapter-words';
}
if ( ! $args['date'] ) {
$card_classes[] = '_no-chapter-dates';
}
if ( ! $args['footer_chapters'] ) {
$card_classes[] = '_no-footer-chapters';
}
if ( ! $args['footer_words'] ) {
$card_classes[] = '_no-footer-words';
}
if ( ! $args['footer_date'] ) {
$card_classes[] = '_no-footer-date';
}
if ( ! $args['footer_status'] ) {
$card_classes[] = '_no-footer-status';
}
if ( ! $args['footer_rating'] ) {
$card_classes[] = '_no-footer-rating';
}
if ( $splide ) {
$card_classes[] = 'splide__slide';
}
// Search for viable chapters...
$search_list = array_reverse( $story['chapter_ids'] );
foreach ( $search_list as $chapter_id ) {
$chapter_post = get_post( $chapter_id );
if ( get_post_meta( $chapter_id, 'fictioneer_chapter_hidden', true ) ) {
continue;
}
if ( $args['ignore_protected'] && $chapter_post->post_password ) {
continue;
}
$chapter_list[] = $chapter_post;
if ( count( $chapter_list ) > 1 ) {
break; // Max one or two
}
}
// Slice chapter list down
$chapter_list = array_slice( $chapter_list, 0, ( $args['single'] || $args['type'] === 'single' ) ? 1 : 2 );
// No viable chapters
if ( count( $chapter_list ) < 1 ) {
continue; continue;
} }
$chapter_list[] = $chapter_post; // Count actually rendered cards to account for buffer
if ( ++$card_counter > $args['count'] ) {
if ( count( $chapter_list ) > 1 ) { break;
break; // Max one or two
} }
}
// Slice chapter list down // Truncate factor
$chapter_list = array_slice( $chapter_list, 0, ( $args['single'] || $args['type'] === 'single' ) ? 1 : 2 ); $truncate_factor = '_1-1';
// No viable chapters if ( $args['vertical'] ) {
if ( count( $chapter_list ) < 1 ) { $truncate_factor = count( $chapter_list ) > 1 ? '_2-2' : '_3-3';
continue; } else {
} $truncate_factor = count( $chapter_list ) > 1 ? '_1-1' : '_2-2';
}
// Count actually rendered cards to account for buffer // Card attributes
if ( ++$card_counter > $args['count'] ) { $attributes = [];
break;
}
// Truncate factor if ( $args['aspect_ratio'] ) {
$truncate_factor = '_1-1'; $attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio'];
}
if ( $args['vertical'] ) { $attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-updates' );
$truncate_factor = count( $chapter_list ) > 1 ? '_2-2' : '_3-3';
} else {
$truncate_factor = count( $chapter_list ) > 1 ? '_1-1' : '_2-2';
}
// Card attributes $card_attributes = '';
$attributes = [];
if ( $args['aspect_ratio'] ) { foreach ( $attributes as $key => $value ) {
$attributes['style'] = '--card-image-aspect-ratio: ' . $args['aspect_ratio']; $card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
} }
?>
$attributes = apply_filters( 'fictioneer_filter_card_attributes', $attributes, $post, 'shortcode-latest-updates' ); <li class="post-<?php echo $post_id; ?> card _small _story-update <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>>
<div class="card__body polygon">
$card_attributes = ''; <div class="card__main <?php echo $grid_or_vertical; ?> _small">
foreach ( $attributes as $key => $value ) { <?php
$card_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" '; do_action( 'fictioneer_shortcode_latest_updates_card_body', $post, $story, $args );
}
?>
<li class="post-<?php echo $post_id; ?> card _small _story-update <?php echo implode( ' ', $card_classes ); ?>" <?php echo $card_attributes; ?>> if ( $args['thumbnail'] ) {
<div class="card__body polygon"> fictioneer_render_thumbnail(
array(
<div class="card__main <?php echo $grid_or_vertical; ?> _small"> 'post_id' => $post_id,
'title' => $story['title'],
<?php 'classes' => 'card__image cell-img',
do_action( 'fictioneer_shortcode_latest_updates_card_body', $post, $story, $args ); 'permalink' => $story_link,
'lightbox' => $args['lightbox'],
if ( $args['thumbnail'] ) { 'vertical' => $args['vertical'],
fictioneer_render_thumbnail( 'seamless' => $args['seamless'],
array( 'aspect_ratio' => $args['aspect_ratio']
'post_id' => $post_id, )
'title' => $story['title'],
'classes' => 'card__image cell-img',
'permalink' => $story_link,
'lightbox' => $args['lightbox'],
'vertical' => $args['vertical'],
'seamless' => $args['seamless'],
'aspect_ratio' => $args['aspect_ratio']
)
);
}
?>
<h3 class="card__title _small cell-title"><a href="<?php echo $story_link; ?>" class="truncate _1-1"><?php
if ( ! empty( $post->post_password ) ) {
echo '<i class="fa-solid fa-lock protected-icon"></i> ';
}
echo $story['title'];
?></a></h3>
<div class="card__content _small cell-desc">
<div class="truncate <?php echo $truncate_factor; ?>">
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] ) : ?>
<span class="card__by-author"><?php
printf( _x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ), fictioneer_get_author_node() );
?></span>
<?php endif; ?>
<span><?php
$short_description = fictioneer_first_paragraph_as_excerpt(
fictioneer_get_content_field( 'fictioneer_story_short_description', $post_id )
); );
echo mb_strlen( $short_description, 'UTF-8' ) < 30 ? get_the_excerpt() : $short_description; }
?></span> ?>
</div>
</div>
<ol class="card__link-list _small cell-list"> <h3 class="card__title _small cell-title"><a href="<?php echo $story_link; ?>" class="truncate _1-1"><?php
<?php foreach ( $chapter_list as $chapter ) : ?> if ( ! empty( $post->post_password ) ) {
<?php echo '<i class="fa-solid fa-lock protected-icon"></i> ';
// Chapter title }
$list_title = get_post_meta( $chapter->ID, 'fictioneer_chapter_list_title', true );
$list_title = trim( wp_strip_all_tags( $list_title ) );
if ( empty( $list_title ) ) { echo $story['title'];
$chapter_title = fictioneer_get_safe_title( $chapter->ID, 'shortcode-latest-updates' ); ?></a></h3>
} else {
$chapter_title = $list_title;
}
// Extra classes <div class="card__content _small cell-desc">
$list_item_classes = []; <div class="truncate <?php echo $truncate_factor; ?>">
<?php if ( get_option( 'fictioneer_show_authors' ) && $args['source'] ) : ?>
if ( ! empty( $chapter->post_password ) ) { <span class="card__by-author"><?php
$list_item_classes[] = '_password'; printf( _x( 'by %s —', 'Small card: by {Author} —.', 'fictioneer' ), fictioneer_get_author_node() );
} ?></span>
?>
<li class="card__link-list-item <?php echo implode( ' ', $list_item_classes ); ?>">
<div class="card__left text-overflow-ellipsis">
<i class="fa-solid fa-caret-right"></i>
<a href="<?php the_permalink( $chapter->ID ); ?>" class="card__link-list-link"><?php
echo $chapter_title;
?></a>
</div>
<?php if ( $args['words'] || $args['date'] ) : ?>
<div class="card__right">
<?php
$words = $args['words'] ? fictioneer_get_word_count( $chapter->ID ) : 0;
if ( $words ) {
echo '<span class="words _words-' . $words . '">' . fictioneer_shorten_number( $words ) . '</span>';
}
if ( $words && $args['date'] ) {
echo '<span class="separator-dot">&#8196;&bull;&#8196;</span>';
}
if ( $args['date'] ) {
echo '<span class="date">' . get_the_date(
$args['nested_date_format'] ?: FICTIONEER_LATEST_UPDATES_LI_DATE, $chapter->ID
) . '</span>';
}
?>
</div>
<?php endif; ?> <?php endif; ?>
</li> <span><?php
<?php endforeach; ?> $short_description = fictioneer_first_paragraph_as_excerpt(
</ol> fictioneer_get_content_field( 'fictioneer_story_short_description', $post_id )
);
<?php if ( $show_terms ) : ?> echo mb_strlen( $short_description, 'UTF-8' ) < 30 ? get_the_excerpt() : $short_description;
<div class="card__tag-list _small _scrolling cell-tax"> ?></span>
<div class="card__h-scroll <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>">
<?php
if ( $story['has_taxonomies'] || $tags ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
$terms = array_merge(
$story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [],
$story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [],
$tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
);
$terms = apply_filters(
'fictioneer_filter_shortcode_latest_updates_terms',
$terms, $post, $args, $story
);
// Implode with separator
echo implode(
fictioneer_get_bullet_separator( 'latest-updates', $args['terms'] === 'pills' ),
array_slice( $terms, 0, $args['max_terms'] )
);
} else {
?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php
}
?>
</div> </div>
</div> </div>
<?php endif; ?>
<?php if ( $args['footer'] && ! in_array( $args['type'], ['simple', 'single'] ) ) : ?> <ol class="card__link-list _small cell-list">
<div class="card__footer cell-footer _small"> <?php foreach ( $chapter_list as $chapter ) : ?>
<?php
// Chapter title
$list_title = get_post_meta( $chapter->ID, 'fictioneer_chapter_list_title', true );
$list_title = trim( wp_strip_all_tags( $list_title ) );
<div class="card__footer-box _left text-overflow-ellipsis"><?php if ( empty( $list_title ) ) {
$chapter_title = fictioneer_get_safe_title( $chapter->ID, 'shortcode-latest-updates' );
} else {
$chapter_title = $list_title;
}
// Build footer items // Extra classes
$footer_items = []; $list_item_classes = [];
if ( $args['footer_chapters'] ) { if ( ! empty( $chapter->post_password ) ) {
$footer_items['chapters'] = '<span class="card__footer-chapters"><i class="card-footer-icon fa-solid fa-list" title="' . esc_attr__( 'Chapters', 'fictioneer' ) . '"></i> ' . $story['chapter_count'] . '</span>'; $list_item_classes[] = '_password';
} }
?>
<li class="card__link-list-item <?php echo implode( ' ', $list_item_classes ); ?>">
<div class="card__left text-overflow-ellipsis">
<i class="fa-solid fa-caret-right"></i>
<a href="<?php the_permalink( $chapter->ID ); ?>" class="card__link-list-link"><?php
echo $chapter_title;
?></a>
</div>
<?php if ( $args['words'] || $args['date'] ) : ?>
<div class="card__right">
<?php
$words = $args['words'] ? fictioneer_get_word_count( $chapter->ID ) : 0;
if ( $story['word_count'] > 0 && $args['footer_words'] ) { if ( $words ) {
$footer_items['words'] = '<span class="card__footer-words"><i class="card-footer-icon fa-solid fa-font" title="' . esc_attr__( 'Total Words', 'fictioneer' ) . '"></i> ' . $story['word_count_short'] . '</span>'; echo '<span class="words _words-' . $words . '">' . fictioneer_shorten_number( $words ) . '</span>';
} }
if ( $args['footer_date'] ) { if ( $words && $args['date'] ) {
$format = $args['date_format'] ?: FICTIONEER_LATEST_UPDATES_FOOTER_DATE; echo '<span class="separator-dot">&#8196;&bull;&#8196;</span>';
}
$footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>'; if ( $args['date'] ) {
} echo '<span class="date">' . get_the_date(
$args['nested_date_format'] ?: FICTIONEER_LATEST_UPDATES_LI_DATE, $chapter->ID
) . '</span>';
}
?>
</div>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ol>
if ( $args['footer_status'] ) { <?php if ( $show_terms ) : ?>
$footer_items['status'] = '<span class="card__footer-status"><i class="card-footer-icon ' . $story['icon'] . '"></i> ' . fcntr( $story['status'] ) . '</span>'; <div class="card__tag-list _small _scrolling cell-tax">
} <div class="card__h-scroll <?php echo $args['terms'] === 'pills' ? '_pills' : ''; ?>">
<?php
if ( $story['has_taxonomies'] || $tags ) {
$variant = $args['terms'] === 'pills' ? '_pill' : '_inline';
// Filter footer items $terms = array_merge(
$footer_items = apply_filters( $story['fandoms'] ? fictioneer_get_term_nodes( $story['fandoms'], "{$variant} _fandom" ) : [],
'fictioneer_filter_shortcode_latest_updates_card_footer', $story['genres'] ? fictioneer_get_term_nodes( $story['genres'], "{$variant} _genre" ) : [],
$footer_items, $tags ? fictioneer_get_term_nodes( $tags, "{$variant} _tag" ) : [],
$post, $story['characters'] ? fictioneer_get_term_nodes( $story['characters'], "{$variant} _character" ) : []
$story, );
$args
);
// Implode and render footer items $terms = apply_filters(
echo implode( ' ', $footer_items ); 'fictioneer_filter_shortcode_latest_updates_terms',
$terms, $post, $args, $story
);
?></div> // Implode with separator
echo implode(
<?php if ( $args['footer_rating'] ) : ?> fictioneer_get_bullet_separator( 'latest-updates', $args['terms'] === 'pills' ),
<div class="card__footer-box _right rating-letter-label tooltipped" data-tooltip="<?php echo fcntr( $story['rating'], true ); ?>"> array_slice( $terms, 0, $args['max_terms'] )
<?php echo fcntr( $story['rating_letter'] ); ?> );
} else {
?><span class="card__no-taxonomies"><?php _e( 'No taxonomies specified yet.', 'fictioneer' ); ?></span><?php
}
?>
</div> </div>
<?php endif; ?> </div>
<?php endif; ?>
</div> <?php if ( $args['footer'] && ! in_array( $args['type'], ['simple', 'single'] ) ) : ?>
<?php endif; ?> <div class="card__footer cell-footer _small">
<div class="card__footer-box _left text-overflow-ellipsis"><?php
// Build footer items
$footer_items = [];
if ( $args['footer_chapters'] ) {
$footer_items['chapters'] = '<span class="card__footer-chapters"><i class="card-footer-icon fa-solid fa-list" title="' . esc_attr__( 'Chapters', 'fictioneer' ) . '"></i> ' . $story['chapter_count'] . '</span>';
}
if ( $story['word_count'] > 0 && $args['footer_words'] ) {
$footer_items['words'] = '<span class="card__footer-words"><i class="card-footer-icon fa-solid fa-font" title="' . esc_attr__( 'Total Words', 'fictioneer' ) . '"></i> ' . $story['word_count_short'] . '</span>';
}
if ( $args['footer_date'] ) {
$format = $args['date_format'] ?: FICTIONEER_LATEST_UPDATES_FOOTER_DATE;
$footer_items['modified_date'] = '<span class="card__footer-modified-date"><i class="card-footer-icon fa-regular fa-clock" title="' . esc_attr__( 'Last Updated', 'fictioneer' ) . '"></i> ' . get_the_modified_date( $format, $post ) . '</span>';
}
if ( $args['footer_status'] ) {
$footer_items['status'] = '<span class="card__footer-status"><i class="card-footer-icon ' . $story['icon'] . '"></i> ' . fcntr( $story['status'] ) . '</span>';
}
// Filter footer items
$footer_items = apply_filters(
'fictioneer_filter_shortcode_latest_updates_card_footer',
$footer_items,
$post,
$story,
$args
);
// Implode and render footer items
echo implode( ' ', $footer_items );
?></div>
<?php if ( $args['footer_rating'] ) : ?>
<div class="card__footer-box _right rating-letter-label tooltipped" data-tooltip="<?php echo fcntr( $story['rating'], true ); ?>">
<?php echo fcntr( $story['rating_letter'] ); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div> </div>
</li>
</div> <?php endwhile; ?>
</li> </ul>
<?php endwhile; ?> <?php else : ?>
</ul>
<?php else : ?> <div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div>
<div class="no-results"><?php _e( 'Nothing to show.', 'fictioneer' ); ?></div> <?php endif; wp_reset_postdata(); ?>
<?php endif; wp_reset_postdata(); ?> <?php if ( $splide ) { echo '</div>'; } ?>
</section> </section>

View File

@ -25,12 +25,18 @@
* @internal $args['taxonomies'] Array of taxonomy arrays. Default empty. * @internal $args['taxonomies'] Array of taxonomy arrays. Default empty.
* @internal $args['relation'] Relationship between taxonomies. * @internal $args['relation'] Relationship between taxonomies.
* @internal $args['classes'] String of additional CSS classes. Default empty. * @internal $args['classes'] String of additional CSS classes. Default empty.
* @internal $args['splide'] Configuration JSON for the Splide slider. Default empty.
* @internal $args['height'] Override the item height. Default empty.
* @internal $args['aspect_ratio'] Aspect ratio of the item. Superseded by height. Default empty.
*/ */
// No direct access! // No direct access!
defined( 'ABSPATH' ) OR exit; defined( 'ABSPATH' ) OR exit;
// Setup
$splide = $args['splide'] ?? 0;
// Prepare query // Prepare query
$query_args = array ( $query_args = array (
'fictioneer_query_name' => 'showcase', 'fictioneer_query_name' => 'showcase',
@ -88,66 +94,119 @@ $query = fictioneer_shortcode_query( $query_args );
// Remove temporary filters // Remove temporary filters
remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' ); remove_filter( 'posts_where', 'fictioneer_exclude_protected_posts' );
// Extra attributes
$attributes = [];
if ( $splide ) {
$attributes[] = "data-splide='{$splide}'";
}
?> ?>
<?php if ( $query->have_posts() ) : ?> <?php if ( $query->have_posts() ) : ?>
<section class="showcase container-inline-size <?php echo $args['classes']; ?>"> <section class="showcase container-inline-size <?php echo $args['classes']; ?>" <?php echo implode( ' ', $attributes ); ?>>
<ul class="showcase__list"> <?php
<?php while ( $query->have_posts() ) : $query->the_post(); ?> if ( $args['splide'] === false ) {
<li class="showcase__list-item"> echo '<div class="shortcode-json-invalid">' . __( 'Splide JSON is invalid and has been ignored.', 'fictioneer' ) . '</div>';
<a class="showcase__list-item-link polygon" href="<?php the_permalink(); ?>"> }
<figure class="showcase__list-item-figure">
<?php
// Setup
$post_id = $post->ID;
$list_title = '';
$story_id = null;
$landscape_image_id = get_post_meta( $post_id, 'fictioneer_landscape_image', true );
// Get list title and story ID (if any) if ( $splide ) {
switch ( $args['post_type'] ) { echo '<div class="splide__track">';
case 'fcn_collection': }
$list_title = get_post_meta( $post_id, 'fictioneer_collection_list_title', true );
break;
case 'fcn_chapter':
$list_title = get_post_meta( $post_id, 'fictioneer_chapter_list_title', true );
$story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true );
if ( empty( $landscape_image_id ) ) { // Item classes
$landscape_image_id = get_post_meta( $story_id, 'fictioneer_landscape_image', true ); $item_classes = '';
}
break; if ( $args['aspect_ratio'] ) {
} $item_classes .= ' _aspect-ratio';
}
// Prepare titles if ( $args['height'] ) {
$list_title = trim( wp_strip_all_tags( $list_title ) ); $item_classes .= ' _custom-height';
$title = empty( $list_title ) ? fictioneer_get_safe_title( $post_id, 'shortcode-showcase' ) : $list_title; }
// Prepare image arguments if ( $splide ) {
$image_args = array( $item_classes .= ' splide__slide';
'alt' => sprintf( __( '%s Cover', 'fictioneer' ), $title ), }
'class' => 'no-auto-lightbox showcase__image'
);
// Output image or placeholder // Item attributes
if ( ! empty( $landscape_image_id ) ) { $attributes = array(
echo wp_get_attachment_image( $landscape_image_id, 'medium', false, $image_args ); 'style' => ''
} elseif ( has_post_thumbnail() ) { );
the_post_thumbnail( 'medium', $image_args );
} elseif ( $story_id && has_post_thumbnail( $story_id ) ) { if ( $args['aspect_ratio'] ) {
echo get_the_post_thumbnail( $story_id, 'medium', $image_args ); $attributes['style'] .= "--showcase-item-aspect-ratio: {$args['aspect_ratio']};";
} else { }
echo '<div class="showcase__image _no-cover"></div>';
} if ( $args['height'] ) {
?> $attributes['style'] .= "--showcase-item-height: {$args['height']};";
<?php if ( ! $args['no_cap'] ) : ?> }
<figcaption class="showcase__list-item-figcaption"><?php echo $title; ?></figcaption>
<?php endif; ?> $item_attributes = '';
</figure>
</a> foreach ( $attributes as $key => $value ) {
</li> $item_attributes .= esc_attr( $key ) . '="' . esc_attr( $value ) . '" ';
<?php endwhile; ?> }
</ul> ?>
<ul class="showcase__list <?php if ( $splide ) { echo 'splide__list'; } ?>">
<?php while ( $query->have_posts() ) : $query->the_post(); ?>
<li class="showcase__list-item <?php echo $item_classes; ?>" <?php echo $item_attributes; ?>>
<a class="showcase__list-item-link polygon" href="<?php the_permalink(); ?>">
<figure class="showcase__list-item-figure">
<?php
// Setup
$post_id = $post->ID;
$list_title = '';
$story_id = null;
$landscape_image_id = get_post_meta( $post_id, 'fictioneer_landscape_image', true );
// Get list title and story ID (if any)
switch ( $args['post_type'] ) {
case 'fcn_collection':
$list_title = get_post_meta( $post_id, 'fictioneer_collection_list_title', true );
break;
case 'fcn_chapter':
$list_title = get_post_meta( $post_id, 'fictioneer_chapter_list_title', true );
$story_id = get_post_meta( $post_id, 'fictioneer_chapter_story', true );
if ( empty( $landscape_image_id ) ) {
$landscape_image_id = get_post_meta( $story_id, 'fictioneer_landscape_image', true );
}
break;
}
// Prepare titles
$list_title = trim( wp_strip_all_tags( $list_title ) );
$title = empty( $list_title ) ? fictioneer_get_safe_title( $post_id, 'shortcode-showcase' ) : $list_title;
// Prepare image arguments
$image_args = array(
'alt' => sprintf( __( '%s Cover', 'fictioneer' ), $title ),
'class' => 'no-auto-lightbox showcase__image'
);
// Output image or placeholder
if ( ! empty( $landscape_image_id ) ) {
echo wp_get_attachment_image( $landscape_image_id, 'medium', false, $image_args );
} elseif ( has_post_thumbnail() ) {
the_post_thumbnail( 'medium', $image_args );
} elseif ( $story_id && has_post_thumbnail( $story_id ) ) {
echo get_the_post_thumbnail( $story_id, 'medium', $image_args );
} else {
echo '<div class="showcase__image _no-cover"></div>';
}
?>
<?php if ( ! $args['no_cap'] ) : ?>
<figcaption class="showcase__list-item-figcaption"><?php echo $title; ?></figcaption>
<?php endif; ?>
</figure>
</a>
</li>
<?php endwhile; ?>
</ul>
<?php if ( $splide ) { echo '</div>'; } ?>
</section> </section>
<?php endif; wp_reset_postdata(); ?> <?php endif; wp_reset_postdata(); ?>

View File

@ -2262,3 +2262,15 @@ function fcn_showAgeConfirmationModal() {
localStorage.removeItem('fcnAgeConfirmation'); localStorage.removeItem('fcnAgeConfirmation');
}); });
} }
// =============================================================================
// SPLIDE SLIDER (IF ANY)
// =============================================================================
document.addEventListener('DOMContentLoaded', () => {
_$$('.splide:not(.no-auto-splide, .is-initialized)').forEach(slider => {
if (typeof Splide !== 'undefined') {
new Splide(slider).mount();
}
});
});

View File

@ -21,7 +21,7 @@
} }
} }
&__list { &__list:where(:not(.splide__list)) {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(#{get_clamp(160, 175, 375, 480)}, 1fr)); grid-template-columns: repeat(auto-fit, minmax(#{get_clamp(160, 175, 375, 480)}, 1fr));
gap: var(--showcase-row-gap, max(3cqw, 1.5rem)) var(--showcase-row-col, max(3cqw, 1.5rem)); gap: var(--showcase-row-gap, max(3cqw, 1.5rem)) var(--showcase-row-col, max(3cqw, 1.5rem));
@ -35,13 +35,21 @@
&__list-item { &__list-item {
--focus-offset: 2px; --focus-offset: 2px;
position: relative; position: relative;
aspect-ratio: var(--showcase-item-aspect-ratio);
border-radius: var(--layout-border-radius-small); border-radius: var(--layout-border-radius-small);
box-shadow: var(--showcase-box-shadow, var(--box-shadow-m)); box-shadow: var(--showcase-box-shadow, var(--box-shadow-m));
overflow: hidden; overflow: hidden;
max-width: max(420px, 100%); max-width: max(420px, 100%);
height: max(64px, #{get_clamp(64, 96, 375, 768)});
contain: layout paint style; // Improve performance contain: layout paint style; // Improve performance
&:where(:not(._aspect-ratio, ._custom-height)) {
height: max(64px, #{get_clamp(64, 96, 375, 768)});
}
&._custom-height {
height: var(--showcase-item-height);
}
@include bp(mobile-l) { @include bp(mobile-l) {
&:first-child:last-child { &:first-child:last-child {
width: max(360px, 50%); width: max(360px, 50%);

View File

@ -605,11 +605,16 @@ button[type=submit]:where(:not(._inline)),
opacity: 0.5; opacity: 0.5;
} }
.no-results { .no-results,
.shortcode-json-invalid {
color: var(--fg-950); color: var(--fg-950);
padding: 0 2px; padding: 0 2px;
} }
.shortcode-json-invalid {
margin: 2rem 0;
}
html:not(.logged-in) body:not(.logged-in) { html:not(.logged-in) body:not(.logged-in) {
.checkmark { .checkmark {
display: none; display: none;

View File

@ -59,6 +59,15 @@
--strong-filter: none; --strong-filter: none;
--invert-filter: invert(1); // Checkbox check --invert-filter: invert(1); // Checkbox check
// === SPLIDE ================================================================
--splide-arrow: var(--bg-400);
--splide-arrow-hover: var(--bg-700);
--splide-arrow-active: var(--bg-700);
--splide-pagination: var(--bg-400);
--splide-pagination-hover: var(--bg-500);
--splide-pagination-active: var(--bg-700);
// === LITRPG ================================================================ // === LITRPG ================================================================
--litrpg-background: var(--bg-50); --litrpg-background: var(--bg-50);

View File

@ -157,6 +157,15 @@
--strong-filter: brightness(1.05); --strong-filter: brightness(1.05);
--invert-filter: invert(0); // Checkbox check --invert-filter: invert(0); // Checkbox check
// === SPLIDE ================================================================
--splide-arrow: var(--bg-400);
--splide-arrow-hover: var(--bg-100);
--splide-arrow-active: var(--bg-100);
--splide-pagination: var(--bg-400);
--splide-pagination-hover: var(--bg-300);
--splide-pagination-active: var(--bg-100);
// === LITRPG ================================================================ // === LITRPG ================================================================
--litrpg-background: var(--bg-50); --litrpg-background: var(--bg-50);

View File

@ -53,7 +53,7 @@ body:not(.is-editor):not(.is-admin) {
} }
} }
.grid-columns { .grid-columns:where(:not(.splide__list)) {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--grid-columns-min, 308px), 1fr)); grid-template-columns: repeat(auto-fill, minmax(var(--grid-columns-min, 308px), 1fr));
gap: gap:

172
src/scss/splide.scss Normal file
View File

@ -0,0 +1,172 @@
@import 'common/_functions';
@import 'common/_mixins';
@import 'common/_variables';
// Core
@keyframes splide-loading{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.splide__track--draggable{-webkit-touch-callout:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.splide__track--fade>.splide__list>.splide__slide{margin:0!important;opacity:0;z-index:0}.splide__track--fade>.splide__list>.splide__slide.is-active{opacity:1;z-index:1}.splide--rtl{direction:rtl}.splide__track--ttb>.splide__list{display:block}.splide__container{box-sizing:border-box;position:relative}.splide__list{backface-visibility:hidden;display:-ms-flexbox;display:flex;height:100%;margin:0!important;padding:0!important}.splide.is-initialized:not(.is-active) .splide__list{display:block}.splide__pagination{-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:center;justify-content:center;margin:0;pointer-events:none}.splide__pagination li{display:inline-block;line-height:1;list-style-type:none;margin:0;pointer-events:auto}.splide:not(.is-overflow) .splide__pagination{display:none}.splide__progress__bar{width:0}.splide{position:relative;visibility:hidden}.splide.is-initialized,.splide.is-rendered{visibility:visible}.splide__slide{backface-visibility:hidden;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0;list-style-type:none!important;margin:0;position:relative}.splide__slide img{vertical-align:bottom}.splide__spinner{animation:splide-loading 1s linear infinite;border:2px solid #999;border-left-color:transparent;border-radius:50%;bottom:0;contain:strict;display:inline-block;height:20px;left:0;margin:auto;position:absolute;right:0;top:0;width:20px}.splide__sr{clip:rect(0 0 0 0);border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.splide__toggle.is-active .splide__toggle__play,.splide__toggle__pause{display:none}.splide__toggle.is-active .splide__toggle__pause{display:inline}.splide__track{overflow:hidden;position:relative;z-index:0}
.splide {
--this-arrow-size: #{get_clamp(20, 40, 320, 640)};
--this-outset: 6px;
width: calc(100% + 2 * var(--this-outset));
max-width: 100vw;
transform: translateX(calc(-1 * var(--this-outset)));
padding-top: var(--this-arrow-vertical-padding, 0px);
padding-left: calc(var(--this-arrow-horizontal-padding, 0px));
padding-bottom: var(--this-arrow-vertical-padding, 0px);
padding-right: calc(var(--this-arrow-horizontal-padding, 0px) + var(--this-pagination-side-padding, 0px));
&:not(.is-initialized) {
visibility: visible;
.splide__slide:first-child {
width: 100%;
}
}
&.splide--ttb {
--this-outset: 0px;
.splide__slide {
height: auto !important;
}
.splide__track {
padding: 0 !important;
}
}
&:has(.splide__pagination) {
--this-arrow-offset: 9px; // Offset bottom pagination
}
&:has(.splide__arrows) {
--this-arrow-horizontal-padding: var(--this-arrow-size);
}
&:has(.splide__arrows--ttb) {
--this-arrow-offset: 0px; // No bottom pagination
--this-arrow-horizontal-padding: 0px;
--this-arrow-vertical-padding: var(--this-arrow-size);
}
&:has(.splide__pagination--ttb) {
--this-pagination-side-padding: 24px;
.splide__arrow--next {
right: 16px;
}
}
&__track {
padding: var(--this-outset) var(--this-outset) var(--this-pagination-vertical-padding, 16px) !important;
}
&.article-card-block .splide__track {
--this-pagination-vertical-padding: 0px;
.splide__pagination:not(.splide__pagination--ttb) {
margin-top: 16px;
}
}
&__arrow {
position: absolute;
z-index: 10;
top: calc(50% - var(--this-arrow-offset, 0px));
display: grid;
place-content: center;
line-height: 1;
height: calc(var(--this-arrow-size) * 2);
width: var(--this-arrow-size);
&--prev {
left: 0;
transform: translateY(-50%) scale(-1, 1);
}
&--next {
right: 0;
transform: translateY(-50%);
}
svg {
height: calc(var(--this-arrow-size) * .675);
width: calc(var(--this-arrow-size) * .675);
fill: var(--splide-arrow);
transition: fill .15s;
}
&:hover svg {
fill: var(--splide-arrow-hover);
}
&:active svg {
fill: var(--splide-arrow-active);
}
}
&__arrows--ttb {
.splide__arrow {
top: unset;
left: 50%;
right: unset;
transform: translateX(-50%) rotate(-90deg);
height: var(--this-arrow-size);
&--prev {
top: 0;
}
&--next {
bottom: 0;
transform: translateX(-50%) rotate(90deg);
}
}
}
&__pagination {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
li {
display: block;
}
button {
--this-background: var(--splide-pagination);
cursor: pointer;
position: relative;
display: block;
padding: 8px;
transition: background-color .15s;
contain: strict;
&::after {
content: '';
position: absolute;
inset: 4px;
display: block;
background: var(--this-background);
border-radius: 50%;
}
&.is-active {
--this-background: var(--splide-pagination-active);
}
&:where(:not(.is-active)):hover {
--this-background: var(--splide-pagination-hover, var(--splide-pagination));
}
}
&--ttb {
position: absolute;
top: 50%;
right: 0;
flex-direction: column;
transform: translateY(-50%);
}
}
}