From 894ec8a6f2ac62edbac7b3b6a88e3666f335c673 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 15 May 2026 15:08:42 +0000
Subject: [PATCH] =Refactor of CustomBlocks.php to move a majority of the logic to the pre_render_field instead of render_field, and adding support for more blocks
---
/dev/null | 128 ------
inc/managers/LoginManager.php | 97 +++-
inc/managers/DashboardManager.php | 12
inc/managers/SEO/render/Thing/Place/AdministrativeArea/State.php | 2
inc/blocks/CustomBlocks.php | 890 +++++++++++++++++++++++++++++---------------
inc/managers/SEO/BreadcrumbManager.php | 13
6 files changed, 676 insertions(+), 466 deletions(-)
diff --git a/cleanup2.php b/cleanup2.php
deleted file mode 100644
index fa493f4..0000000
--- a/cleanup2.php
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-if (!defined('ABSPATH')) {
- exit; // Exit if accessed directly
-}
-
-function jvbRemoveBlockStyles():void
-{
- if (!is_admin()) {
- wp_dequeue_style('global-styles');
- wp_dequeue_style('dashicons');
- //remove default styles
- global $wp_styles;
-
- foreach ($wp_styles->queue as $handle) {
- if (str_starts_with($handle, 'wp-block-')) {
- wp_dequeue_style($handle);
- }
- }
- wp_deregister_script('heartbeat');
- wp_dequeue_style('core-block-supports');
- wp_dequeue_script('wp-block-template-skip-link');
-
- // Remove Akismet widget style
- wp_deregister_style('akismet-widget-style-inline-css');
-// wp_dequeue_script('wp-block-template-skip-link');
-
-// wp_dequeue_style('buttons');
-// wp_dequeue_style('admin-bar');
-// wp_dequeue_style( 'classic-theme-styles' );
-
- wp_dequeue_style('wp-block-site-title');
- wp_dequeue_style('wp-block-navigation');
- wp_dequeue_style('wp-block-navigation-link');
- wp_dequeue_style('wp-block-query-title');
- wp_dequeue_style('wp-block-group');
- }
- // wp_enqueue_script('legacy-scripts', get_template_directory_uri() . '/src/js/legacy.js', false, false, array( 'in_footer' => true));
-}
-add_action('wp_enqueue_scripts', 'jvbRemoveBlockStyles', 100);
-
-//Remove generator
-remove_action('wp_head', 'wp_generator');//Remove RSD Link
-remove_action('wp_head', 'rsd_link');//Remove WLW Manifest
-remove_action('wp_head', 'wlwmanifest_link');//Remove shortlink
-remove_action('wp_head', 'wp_shortlink_wp_head');
-
-//remove emoji support
-remove_action('wp_head', 'print_emoji_detection_script', 7);
-remove_action('wp_print_styles', 'print_emoji_styles');
-
-
-add_filter('password_reset_expiration', function ($expiration) {
- return 900; //15 minutes
-});
-
-
-add_action('before_delete_post', function ($id) {
- $attachments = get_attached_media('', $id);
- foreach ($attachments as $attachment) {
- wp_delete_attachment($attachment->ID, 'true');
- }
- $meta = [
- BASE.'image',
- BASE.'gallery',
- ];
- foreach ($meta as $m) {
- if ($ID = get_post_meta($id, $m, true)!== '') {
- $IDs = (str_contains($ID, ',')) ? explode(',', $ID) : [$ID];
- foreach ($IDs as $ID) {
- wp_delete_attachment($ID, 'true');
- }
- }
- }
-});
-
-
-/**
- * Clean up the output classes
- */
-add_filter('body_class', 'jvbBodyClasses');
-function jvbBodyClasses(array $classes):array
-{
- $classes = [];
- if (is_front_page()) {
- $classes[] = 'home';
- }
- if (jvbIsDirectory()) {
- $classes[] = 'is-directory';
- } elseif (is_tax()) {
- $classes[] = str_replace(BASE, '', get_queried_object()->taxonomy);
- } elseif (is_singular() && !is_singular('page')) {
- $classes[] = str_replace(BASE, '', get_queried_object()->post_type);
- } elseif (is_post_type_archive()) {
- $classes[] = str_replace(BASE, '', get_queried_object()->name);
- }
- return $classes;
-}
diff --git a/inc/blocks/CustomBlocks.php b/inc/blocks/CustomBlocks.php
index 174e1a7..0b16235 100644
--- a/inc/blocks/CustomBlocks.php
+++ b/inc/blocks/CustomBlocks.php
@@ -4,6 +4,7 @@
use DateTime;
use DOMDocument;
use JVBase\managers\Cache;
+use JVBase\managers\LoginManager;
use JVBase\managers\SEO\BreadcrumbManager;
use WP_Block;
use WP_Query;
@@ -15,12 +16,14 @@
class CustomBlocks
{
protected Cache $cache;
+ protected array $shouldRender = ['core/query'];
public function __construct()
{
$this->cache = Cache::for('blocks', WEEK_IN_SECONDS);
$this->cache->connect('post')->connect('taxonomy');
$this->cache->flush();
- add_filter('render_block', [$this, 'render'], 900, 2);
+ add_filter('pre_render_block', [$this, 'prerender'], 10, 3);
+ add_filter('render_block', [$this, 'render'], 10, 2);
add_action('init', [$this, 'registerBlockStyles']);
}
@@ -65,51 +68,59 @@
]
);
}
-
- public function render(string $content, array $block)
- {
+ protected function checkMethods(?string $content, array $block, ?WP_Block $parent = null, bool $isPrerender = false):?string
+ {
$blockName = $this->sanitizeBlockName($block);
- $method = 'render_'.$blockName;
+
+ $base = ($isPrerender) ? 'prerender_' : 'render_';
+ $method = $base.$blockName;
$function = BASE.$method;
if (function_exists($function)) {
- return $function($block, $content);
-// return $this->cache->remember(
-// get_the_ID(),
-// function () use ($function, $block, $content) {
-// return $function($block, $content);
-// }
-// );
+ return $function($block, $content, $parent);
} else if (method_exists($this, $method)) {
- return $this->$method($block, $content);
-//
-// return $this->cache->remember(
-// get_the_ID(),
-// function () use ($method, $block, $content) {
-// return $this->$method($block, $content);
-// }
-// );
- } else if (!empty($block['blockName'])){
- //TESTING
- $ignore = [
- 'core/null',
- 'core/post-title',
- 'core/list-item',
- 'core/site-title',
- 'jvb/forms'
- ];
-// if (!in_array($block['blockName'], $ignore)) {
-// jvbDump('No method found for '.print_r($block['blockName'], true));
-// }
+ return $this->$method($block, $content, $parent);
+ } elseif (!empty($blockName) && JVB_TESTING) {
+ if (!in_array($block['blockName'], $this->getIgnore($isPrerender))) {
+ jvbDump('No method found for '.print_r($block['blockName'], true));
+ }
}
- if ($block['blockName'] === 'jvb/feed') {
- // Enqueue the feed block script (it will automatically load dependencies)
- $this->localize_feedblock();
- }
+ return $content;
+ }
+ protected function getIgnore(bool $isPrerender):array
+ {
+ //Ignore for both
+ $base = [
+ 'core/null'
+ ];
+ if ($isPrerender) {
+ $base = array_merge($base, [
+
+ ]);
+ } else {
+ $base = array_merge($base, [
+
+ ]);
+ }
+ return $base;
+ }
+ public function prerender(?string $content, array $block, ?WP_Block $parent = null):?string
+ {
+ $result = $this->checkMethods($content, $block, $parent, true);
+ return $result;
+ }
+
+ public function render(string $content, array $block):string
+ {
+ if ($block['blockName'] === 'jvb/feed') {
+ // Enqueue the feed block script (it will automatically load dependencies)
+ $this->localize_feedblock();
+ }
if ($block['blockName'] === 'jvb/forms') {
wp_enqueue_style('jvb-form');
}
- return $content;
+
+ return $this->checkMethods($content, $block);
}
/***********************************
@@ -128,8 +139,10 @@
*/
- public function render_core_button(array $block):string
+ public function prerender_core_button(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'Button');
+// jvbDump($parent, 'Parent');
preg_match('/href="([^"]*)"/', $block['innerHTML'], $url);
preg_match('/>([^<]*)<\/a>/', $block['innerHTML'], $label);
@@ -147,7 +160,7 @@
if ($icon !== '') {
return sprintf(
'<li%s><a href="%s" title="Find Us On %s">%s Maps</a></li>',
- $this->getClassesAndStyles($block['attrs']),
+ $this->getClassesAndStyles($block['attrs']??[]),
esc_url($url[1]),
esc_html($label[1]),
jvbIcon($icon)
@@ -156,58 +169,79 @@
return sprintf(
'<li%s><a href="%s">%s</a></li>',
- $this->getClassesAndStyles($block['attrs']),
+ $this->getClassesAndStyles($block['attrs']??[]),
esc_url($url[1]),
esc_html($label[1])
);
}
- public function render_core_buttons(array $block, string $content):string
+ public function prerender_core_buttons(array $block, ?string $content, ?WP_Block $parent):?string
{
- return '<ul'.$this->getClassesAndStyles($block['attrs'], ['buttons','row']).'>'.
- $this->inside($block, false, $content).'</ul>';
+// jvbDump($block, 'buttons');
+// jvbDump($parent, 'Parent');
+ return '<ul'.$this->getClassesAndStyles($block['attrs']??[], ['buttons','row']).'>'.
+ $this->innerBlocks($block).'</ul>';
}
- public function render_core_column(array $block, string $content):string
+ public function prerender_core_column(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'column');
+// jvbDump($parent, 'Parent');
$styles = (array_key_exists('attrs', $block) &&
array_key_exists('width', $block['attrs'])) ?
['flex-basis:'.$block['attrs']['width']]
: [];
return '<div'.
- $this->getClassesAndStyles($block['attrs'], ['col'], $styles).'>'.
- $this->inside($block, false, $content).'</div>';
+ $this->getClassesAndStyles($block['attrs']??[], ['col'], $styles).'>'.
+ $this->innerBlocks($block).'</div>';
}
- public function render_core_columns(array $block, string $content):string
+ public function prerender_core_columns(array $block, ?string $content, ?WP_Block $parent):?string
{
- return '<section'.
- $this->getClassesAndStyles($block['attrs'], ['columns']).'>'.
- $this->inside($block, false, $content).'</section>';
+ $tagName = array_key_exists('tagName', $block['attrs']) ? $block['attrs']['tagName'] : 'section';
+
+ return sprintf(
+ '<%s%s>%s</%s>',
+ $tagName,
+ $this->getClassesAndStyles($block['attrs']??[], ['row nowrap']),
+ $this->innerBlocks($block).'</section>',
+ $tagName
+ );
}
//core_comment_template
- public function render_core_group(array $block, string $content):string
+ public function prerender_core_group(array $block, ?string $content, ?WP_Block $parent):?string
{
- $tag = (array_key_exists('tagName', $block['attrs'])) ? $block['attrs']['tagName'] : 'div';
+// jvbDump($block, 'group');
+// jvbDump($parent, 'Parent');
+ $tag = (array_key_exists('tagName', $block['attrs']??[])) ? $block['attrs']['tagName'] : 'div';
- $classes = ($tag === 'main') ?
- '' :
- $this->getClassesAndStyles($block['attrs'], ['group']);
- return '<'.$tag.$classes.'>'.$this->inside($block, false, $content).'</'.$tag.'>';
+
+ return sprintf(
+ '<%s%s>%s</%s>',
+ $tag,
+ $tag === 'main' ? '' : $this->getClassesAndStyles($block['attrs']??[], ['group']),
+ $this->innerBlocks($block),
+ $tag
+ );
}
//core_home_link
//core_more
//core_nextpage
- public function render_core_separator(array $block):string
+ public function prerender_core_separator(array $block, ?string $content, ?WP_Block $parent):?string
{
- return '<hr'.$this->getClassesAndStyles($block['attrs']).'>';
+// jvbDump($block, 'separator');
+// jvbDump($parent, 'Parent');
+ return '<hr'.$this->getClassesAndStyles($block['attrs']??[]).'>';
}
- public function render_core_spacer(array $block):string
+ public function prerender_core_spacer(array $block, ?string $content, ?WP_Block $parent):?string
{
- return '<div'.$this->getClassesAndStyles($block['attrs'], ['spacer'], ['height:2rem']).
+
+// jvbDump($block, 'spsacer');
+// jvbDump($parent, 'Parent');
+ return '<div'.$this->getClassesAndStyles($block['attrs']??[], ['spacer'], ['height:2rem']).
' aria-hidden="true"></div>';
}
//core_table_of_contents
@@ -221,12 +255,13 @@
* Media Blocks
*/
//core_audio
- public function render_core_cover(array $block, string $content):string
+ public function prerender_core_cover(array $block, ?string $content, ?WP_Block $parent):?string
{
-
+// jvbDump($block, 'cover');
+// jvbDump($parent, 'Parent');
// Extract block attributes
- $attrs = $block['attrs'] ?? [];
- $innerContent = $this->inside($block, false, $content);
+ $attrs = $block['attrs'] ?:[];
+ $innerContent = $this->innerBlocks($block);
$position = 'object-position: center;';
if (array_key_exists('focalPoint', $attrs)) {
@@ -268,15 +303,19 @@
//core_file
- public function render_core_gallery(array $block, string $content):string
+ public function prerender_core_gallery(array $block, ?string $content, ?WP_Block $parent):?string
{
- return '<ul'.$this->getClassesAndStyles($block['attrs'], ['gallery']).'>'.
+// jvbDump($block, 'gallery');
+// jvbDump($parent, 'Parent');
+ return '<ul'.$this->getClassesAndStyles($block['attrs']??[], ['gallery']).'>'.
$this->innerBlocks($block,'<li>', '</li>').
'</ul>';
}
- public function render_core_image(array $block):string
+ public function prerender_core_image(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'image');
+// jvbDump($parent, 'Parent');
$ID = $this->imageID('', $block);
if (!$ID) {
return '';
@@ -289,32 +328,34 @@
wp_get_attachment_caption($ID) .
'</figcaption>' :
'<figcaption>' . $title . '</figcaption>';
- $size = array_key_exists('sizeSlug', $block['attrs']) ? $block['attrs']['sizeSlug'] : 'large';
+ $size = array_key_exists('sizeSlug', $block['attrs']??[]) ? $block['attrs']['sizeSlug'] : 'large';
return '<figure'.
- $this->getClassesAndStyles($block['attrs']).'>'.
+ $this->getClassesAndStyles($block['attrs']??[]).'>'.
$this->imageLink(true, $ID, 'tiny', $size) .
$caption.'</figure>';
}
- public function render_core_media_text(array $block, string $content):string
+ public function prerender_core_media_text(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'media text');
+// jvbDump($parent, 'Parent');
$ID = $this->imageID('', $block);
- $size = array_key_exists('mediaSizeSlug', $block['attrs']) ? $block['attrs']['mediaSizeSlug'] : 'large';
+ $size = array_key_exists('mediaSizeSlug', $block['attrs']??[]) ? $block['attrs']['mediaSizeSlug'] : 'large';
$imgLink = ($ID) ? $this->imageLink(true, $ID, 'tiny', $size) : '';
$inner = $this->innerBlocks($block);
$classes = ['media-text', 'row'];
- if (array_key_exists('isStackedOnMobile', $block['attrs'])) {
+ if (array_key_exists('isStackedOnMobile', $block['attrs']??[])) {
$classes[] = 'nowrap';
}
- $content = '<div'.$this->getClassesAndStyles($block['attrs'], $classes).'>';
+ $content = '<div'.$this->getClassesAndStyles($block['attrs']??[], $classes).'>';
$content .= (array_key_exists(
'mediaPosition',
- $block['attrs']
+ $block['attrs']??[]
) && $block['attrs']['mediaPosition'] == 'right') ?
'<div>'.$inner.'</div><figure>'.$imgLink.'</figure>' :
'<figure>'.$imgLink.'</figure><div>'.$inner.'</div>';
@@ -332,41 +373,49 @@
/**
* Text Blocks
*/
- //render_core_code
- //render_core_details
- //render_core_footnotes
- //render_core_classic
- public function render_core_heading(array $block):string
+ //prerender_core_code
+ //prerender_core_details
+ //prerender_core_footnotes
+ //prerender_core_classic
+ public function prerender_core_heading(array $block, ?string $content, ?WP_Block $parent):?string
{
- $level = (array_key_exists('level', $block['attrs'])) ? $block['attrs']['level'] : '2';
- $content = $this->inside($block);
+// jvbDump($block, 'heading');
+// jvbDump($parent, 'Parent');
+ $level = (array_key_exists('level', $block['attrs']??[])) ? $block['attrs']['level'] : '2';
+ $content = $this->innerBlocks($block);
$id = sanitize_title(wp_strip_all_tags($this->stripTagContents('small', $content)));
- return '<h'.$level.' id="'.$id.'"'.$this->getClassesAndStyles($block['attrs']).'>'.
+ return '<h'.$level.' id="'.$id.'"'.$this->getClassesAndStyles($block['attrs']??[]).'>'.
$content.
'</h'.$level.'>';
}
- public function render_core_list(array $block, string $content):string
+ public function prerender_core_list(array $block, ?string $content, ?WP_Block $parent):?string
{
- $tag = (array_key_exists('ordered', $block['attrs'])) ? 'ol' : 'ul';
- $output = '<'.$tag.$this->getClassesAndStyles($block['attrs']).'>'.$this->inside($block, false, $content).'</'.$tag.'>';
+// jvbDump($block, 'list');
+// jvbDump($parent, 'Parent');
+ $tag = (array_key_exists('ordered', $block['attrs']??[])) ? 'ol' : 'ul';
+ $output = '<'.$tag.$this->getClassesAndStyles($block['attrs']??[]).'>'.$this->innerBlocks($block).'</'.$tag.'>';
return $output;
}
-// public function render_core_list_item(array $block):string
+// public function prerender_core_list_item(array $block):string
// {
// return '<li'.$this->getClassesAndStyles($block['attrs']).'>'.$this->inside($block).'</li>';
// }
- //render_core_missing
+ //prerender_core_missing
- public function render_core_paragraph(array $block):string
+ public function prerender_core_paragraph(array $block, ?string $content, ?WP_Block $parent):?string
{
- return '<p'.$this->getClassesAndStyles($block['attrs']).'>'.
- $this->inside($block, 'p').
+// jvbDump($block, 'paragraph');
+// jvbDump($parent, 'Parent');
+ return '<p'.$this->getClassesAndStyles($block['attrs']??[]).'>'.
+ $this->innerBlocks($block).
'</p>';
}
- public function render_core_quote(array $block): string
+ public function prerender_core_quote(array $block, ?string $content, ?WP_Block $parent): ?string
{
+// jvbDump($block, 'quote');
+// jvbDump($parent, 'Parent');
$innerHTML = $block['innerHTML'];
// Extract cite content first
@@ -374,20 +423,22 @@
$citeHtml = ($cite === '') ? '' : '<cite>— '.$cite.'</cite>';
// Get the blockquote content
- $content = $this->inside($block, 'blockquote');
+ $content = $this->innerBlocks($block);
// Remove the cite element from content if it exists
if ($cite !== '') {
$content = $this->stripTagContents('cite', $content);
}
- return '<blockquote'.$this->getClassesAndStyles($block['attrs']).'>
+ return '<blockquote'.$this->getClassesAndStyles($block['attrs']??[]).'>
<div class="content">'.$content.'</div>'.
$citeHtml.
'</blockquote>';
}
- public function render_core_pullquote(array $block): string
+ public function prerender_core_pullquote(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'pullquote');
+// jvbDump($parent, 'Parent');
$innerHTML = $block['innerHTML'];
// Extract cite content first
@@ -403,23 +454,48 @@
}
$content = jvb_filter_content( $content);
- return '<blockquote'.$this->getClassesAndStyles($block['attrs'], ['pull']).'>'.
+ return '<blockquote'.$this->getClassesAndStyles($block['attrs']??[], ['pull']).'>'.
$content.
$citeHtml.
'</blockquote>';
}
- //render_core_table
- //render_core_verse
+ //prerender_core_table
+ //prerender_core_verse
/**
* Theme Blocks
*/
//core_avatar
//core_loginout
+ public function prerender_core_loginout(array $block, ?string $content, ?WP_Block $parent):?string
+ {
+ $action = is_user_logged_in() ? 'logout' : 'login';
+ $attrs = $block['attrs'];
+ $redirect = '';
+ if (array_key_exists('redirectToCurrent', $attrs) && $attrs['redirectToCurrent']) {
+ global $wp;
+ $redirect = get_home_url(null, $wp->request);
+ }
+
+ if (array_key_exists('displayLoginAsForm', $attrs) && $attrs['displayLoginAsForm']) {
+ LoginManager::getInstance()->setAction($action);
+ return LoginManager::getInstance()->renderLoginForm($action, $redirect, '<h2>Login</h2>');
+
+ }
+
+ return sprintf(
+ '<a href="%s"%s>%s</a>',
+ wp_login_url($redirect),
+ $this->getClassesAndStyles($attrs),
+ $action === 'login' ? jvbIcon('sign-in').'<span>Log in</span>' : jvbIcon('sign-out').'<span>Logout</span>'
+ );
+ }
//core_pattern
- public function render_core_site_logo(array $block, string $content):string
+ public function prerender_core_site_logo(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'site logo');
+// jvbDump($parent, 'Parent');
$open = $close = '';
if (!is_home() && !is_front_page()) {
@@ -428,14 +504,16 @@
}
$img = get_theme_mod('custom_logo');
$img = $this->image($img, 'tiny', 'thumbnail');
- $img = str_replace('<img', '<img'.$this->getClassesAndStyles($block['attrs']), $img);
+ $img = str_replace('<img', '<img'.$this->getClassesAndStyles($block['attrs']??[]), $img);
return $open.$img.$close;
}
//core_site_title_tagline
- public function render_core_site_title(array $block, string $content):string
+ public function prerender_core_site_title(array $block, ?string $content, ?WP_Block $parent):?string
{
- $tag = (array_key_exists('level', $block['attrs'])) ? $block['attrs']['level'] : 1;
+// jvbDump($block, 'site title');
+// jvbDump($parent, 'Parent');
+ $tag = (array_key_exists('level', $block['attrs']??[])) ? $block['attrs']['level'] : 1;
$tag = ($tag == 0) ? 'p' : 'h'.$tag;
$open = $close = '';
@@ -444,8 +522,8 @@
$close = '</a>';
}
$class = ($tag === 'p') ?
- $this->getClassesAndStyles($block['attrs'], ['title']) :
- $this->getClassesAndStyles($block['attrs']);
+ $this->getClassesAndStyles($block['attrs']??[], ['title']) :
+ $this->getClassesAndStyles($block['attrs']??[]);
return '<'.$tag.$class.'>'.
@@ -475,15 +553,17 @@
/**
* Theme Navigation Blocks
*/
- public function render_core_navigation(array $block, string $content):string
+ public function prerender_core_navigation(array $block, ?string $content, ?WP_Block $parent):?string
{
- $ID = (array_key_exists('ref', $block['attrs'])) ? $block['attrs']['ref'] : false;
+// jvbDump($block, 'navigation');
+// jvbDump($parent, 'Parent');
+ $ID = (array_key_exists('ref', $block['attrs']??[])) ? $block['attrs']['ref'] : false;
if (empty($block['innerBlocks']) && $ID && get_post($ID)) {
$block['innerBlocks'] = parse_blocks(get_post($ID)->post_content);
}
- $toggle = (array_key_exists('overlayMenu', $block['attrs'])
+ $toggle = (array_key_exists('overlayMenu', $block['attrs']??[])
&& $block['attrs']['overlayMenu'] == 'never') ?
'':
'<button class="toggle main"
@@ -495,8 +575,8 @@
jvbIcon('x', ['title'=>'Toggle Menu']).
'</button>';
$class = ($toggle === '') ?
- $this->getClassesAndStyles($block['attrs'], ['mobile']) :
- $this->getClassesAndStyles($block['attrs']);
+ $this->getClassesAndStyles($block['attrs']??[], ['mobile']) :
+ $this->getClassesAndStyles($block['attrs']??[]);
$helpmenu = (get_the_title($ID) === 'Main') ?
'<nav><ul>'.jvbNotificationMenu().jvbHelpMenu().'</ul></nav>' :
'';
@@ -517,14 +597,19 @@
'</nav>'.$helpmenu;
}
- public function render_core_navigation_link(array $block):string
+ public function prerender_core_navigation_link(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'navigation link');
+// jvbDump($parent, 'Parent');
global $wp;
+ if (!array_key_exists('attrs', $block)) {
+ return '';
+ }
$url = (str_starts_with($block['attrs']['url'],'/')) ?
home_url($block['attrs']['url']) :
$block['attrs']['url'];
$current = (home_url($wp->request.'/') == $url);
- $temp = $block['attrs'];
+ $temp = $block['attrs']??[];
unset($temp['url']);
$classes = ($current) ?
$this->getClassesAndStyles($temp, ['current']):
@@ -533,21 +618,23 @@
if ($current) {
$aria = ' aria-current="page"';
}
- $linkOpen = $this->build_navigation_link($block['attrs'], $aria);
+ $linkOpen = $this->build_navigation_link($block['attrs']??[], $aria);
return '<li'.$classes.'>'.$linkOpen.$block['attrs']['label'].'</a></li>';
}
- public function render_core_navigation_submenu(array $block, string $content):string
+ public function prerender_core_navigation_submenu(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'navigation submenu');
+// jvbDump($parent, 'Parent');
global $wp;
$url = (str_starts_with($block['attrs']['url'],'/')) ?
home_url($block['attrs']['url']) :
$block['attrs']['url'];
$current = (home_url($wp->request) == $url);
- $temp = $block['attrs'];
+ $temp = $block['attrs']??[];
unset($temp['url']);
$classes = ($current) ?
$this->getClassesAndStyles($temp, ['has-submenu', 'current']):
@@ -609,10 +696,12 @@
//core_post_author
//core_post_author_biography
//core_post_author_name
- public function render_core_post_content(array $block, string $content = ''):string
+ public function prerender_core_post_content(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'post content');
+// jvbDump($parent, 'Parent');
- $tag = (array_key_exists('tagName', $block['attrs'])) ?
+ $tag = (array_key_exists('tagName', $block['attrs']??[])) ?
$block['attrs']['tagName'] :
'main';
@@ -626,20 +715,28 @@
$result = '';
}
} else {
- $result = $this->inside($block, false, $content);
+ $result = $this->innerBlocks($block);
}
return apply_filters('jvb_post_content_output', $result, $block);
}
//core_post_date
- public function render_core_post_date(array $block):string
+ public function prerender_core_post_date(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'post date');
+// jvbDump($parent, 'Parent');
$postDate = get_the_date('c');
- return '<time datetime="'.$postDate.'" itemprop="datePublished"'.$this->getClassesAndStyles($block['attrs']).'>'.get_the_date().'</time>';
+ return '<time datetime="'.$postDate.'" itemprop="datePublished"'.$this->getClassesAndStyles($block['attrs']??[]).'>'.get_the_date().'</time>';
}
//core_post_excerpt
- public function render_core_post_featured_image(array $block):string
+ public function prerender_core_post_excerpt(array $block, ?string $content, ?WP_Block $parent):?string
+ {
+ return wpautop(get_the_excerpt());
+ }
+ public function prerender_core_post_featured_image(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'featured image');
+// jvbDump($parent, 'Parent');
global $post;
$ID = get_post_thumbnail_id($post->ID);
$aOpen = $aClose = '';
@@ -648,41 +745,129 @@
$aClose = '</a>';
}
- return '<figure'.$this->getClassesAndStyles($block['attrs']).'>'.$aOpen.
+ return '<figure'.$this->getClassesAndStyles($block['attrs']??[]).'>'.$aOpen.
apply_filters('jvbCoreFeaturedImage', $this->image($ID), $post->post_type).
$aClose.'</figure>';
}
//core_post_navigation_link
- //core_post_template
- //core_post_terms
- public function render_core_post_terms(array $block):string
+ public function prerender_core_post_navigation_link(array $block, ?string $content, ?WP_Block $parent):?string
{
+ $attr = $block['attrs'];
+ $isPrevious = $attr['type']==='previous';
+ $title = array_key_exists('showTitle', $attr)&&$attr['showTitle'];
+ $linkLabel = array_key_exists('linkLabel', $attr)&&$attr['linkLabel'];
+ $label = array_key_exists('label', $attr) ? $attr['label'] : '';
+ $arrow = '';
+ if (array_key_exists('arrow', $attr)) {
+ $dir = $isPrevious ? 'left' : 'right';
+ $icon = match($attr['arrow']) {
+ 'arrow' => 'arrow-square-',
+ 'chevron' => 'caret-circle-'
+ };
+ if ($icon) {
+ $arrow = jvbIcon($icon.$dir);
+ }
+ }
+// return $content;
+ $linkedLabel = $unlinkedLabel = '';
+ if (!empty($label)) {
+ $linkedLabel = $linkLabel ? $label : '';
+ $unlinkedLabel = $linkLabel ? '' : $label;
+ }
+ if ($title) {
+ $linkedLabel .=' %title';
+ } elseif (!empty($label)) {
+ $linkedLabel = $label;
+ $unlinkedLabel = '';
+ } else {
+ $linkedLabel = $isPrevious ? 'Previous' : 'Next';
+ $unlinkedLabel = '';
+ }
+
+ $result = $isPrevious ?
+ get_previous_post_link(
+ $arrow.$unlinkedLabel.' %link',
+ $linkedLabel
+ ) :
+ get_next_post_link(
+ '%link '.$unlinkedLabel.$arrow,
+ $linkedLabel
+ );
+
+
+ return sprintf('<div%s>%s</div>',
+ $this->getClassesAndStyles($attr,['row', 'nowrap']),
+ $result
+ );
+ }
+ //core_post_template
+ public function render_core_post_template(array $block, string $content):string
+ {
+ global $wp_query;
+
+ $inner = '';
+ $block['innerBlocks'][0]['attrs']['tagName'] = 'li';
+ if ($wp_query->have_posts()) {
+ while ($wp_query->have_posts()) {
+ $wp_query->the_post();
+
+ $inner .= $this->innerBlocks($block);
+ }
+ wp_reset_postdata();
+ }
+
+ return sprintf(
+ '<ul class="loop">%s</ul>',
+ $inner
+ );
+ }
+ //core_post_terms
+ public function prerender_core_post_terms(array $block, ?string $content, ?WP_Block $parent):?string
+ {
+ if (!array_key_exists('attrs', $block)) {
+ return '';
+ }
$terms = get_the_terms(get_the_ID(), $block['attrs']['term']);
$out = '';
if ($terms && !is_wp_error($terms)) {
- $out = '<ul class="term-list">';
- if (array_key_exists('prefix', $block['attrs'])) {
- $out .= '<li>'.$block['attrs']['prefix'].'</li>';
+ $out = sprintf(
+ '<ul%s>',
+ $this->getClassesAndStyles($block['attrs'], ['term-list', 'row', 'start'])
+ );
+ if (array_key_exists('prefix', $block['attrs']??[])) {
+ $out .= sprintf(
+ '<li class="prefix">%s</li>',
+ $block['attrs']['prefix']
+ );
}
foreach($terms as $term) {
- $out .= '<li><a href="'.get_term_link($term).'" rel="tag">'.html_entity_decode($term->name).'</a></li>';
+ $out .= sprintf(
+ '<li><a href="%s" rel="tag">%s</a></li>',
+ get_term_link($term),
+ html_entity_decode($term->name)
+ );
}
if (array_key_exists('suffix', $block['attrs'])) {
- $out .= '<li>'.$block['attrs']['suffix'].'</li>';
+ $out .= sprintf(
+ '<li class="suffix">%s</li>',
+ $block['attrs']['suffix']
+ );
}
$out .= '</ul>';
}
return $out;
}
//core_post_time_to_read
- public function render_core_post_title(array $block):string
+ public function prerender_core_post_title(array $block, ?string $content, ?WP_Block $parent):?string
{
+// jvbDump($block, 'post content');
+// jvbDump($parent, 'Parent');
$open = $close = '';
- if (array_key_exists('isLink', $block['attrs'])) {
- $rel = (array_key_exists('rel', $block['attrs'])) ?
+ if (array_key_exists('isLink', $block['attrs']??[])) {
+ $rel = (array_key_exists('rel', $block['attrs']??[])) ?
' rel="'.$block['attrs']['rel'].'"' :
'';
- $target = (array_key_exists('linkTarget', $block['attrs'])) ?
+ $target = (array_key_exists('linkTarget', $block['attrs']??[])) ?
' target="'.$block['attrs']['linkTarget'].'"' :
'';
$open = '<a href="' . get_the_permalink() . '"' . $rel . $target . '>';
@@ -695,110 +880,114 @@
array_key_exists('level', $block['attrs'])) ?
$block['attrs']['level'] :
2;
- return '<h'.$level.$this->getClassesAndStyles($block['attrs']).'>'.
+ return '<h'.$level.$this->getClassesAndStyles($block['attrs']??[]).'>'.
$open.get_the_title().$close.
'</h'.$level.'>';
}
- public function render_core_query(array $block, string $content):string
+ public function render_core_query(array $block, string $content): string
{
- $queryID = $block['attrs']['queryId'];
- $args = [];
- $inherit = $block['attrs']['inherit']??false;
- if ($inherit) {
- global $wp_query;
- $loop = $wp_query;
- } else {
- foreach ($block['attrs']['query'] as $key => $value) {
- if (empty($value)) {
- continue;
- }
- switch ($key) {
- case 'postType':
- if ($value === BASE.'progress'){
- $args['post_parent'] = 0;
- }
- $args['post_type'] = $value;
- break;
- case 'perPage':
- $args['posts_per_page'] = $value;
- break;
- case 'orderBy':
- $args['orderby'] = $value;
- break;
- case 'taxQuery':
- $taxQuery = [];
- foreach ($value as $tax => $terms) {
- $taxQuery[] = [
- 'taxonomy' => $tax,
- 'terms' => $terms
- ];
- }
- if (!empty($taxQuery)) {
- $args['tax_query'] = $taxQuery;
- if (count($taxQuery) > 1) {
- $args['tax_query']['relation'] = 'OR';
- }
- }
- break;
- case 'sticky':
- if ($value === 'ignore') {
- $args['ignore_sticky_posts'] = true;
- } else if ($value === 'exclude'){
- $args['post__not_in'] = get_option('sticky_posts');
- } else if ($value === 'only') {
- $args['include'] = get_option('sticky_posts');
- }
- break;
- case 'search':
- $args['s'] = $value;
- break;
- default:
- $args[$key] = $value;
- break;
- }
- }
- //Add in any args from the query string
- $search = 'query-'.$queryID;
- foreach ($_GET as $key => $value) {
- if (str_contains($key, $search)) {
- $key = str_replace($search, '', $key);
- if ($key === 'page') {
- $args['paged'] = (int)$value;
- }
- }
- }
- $loop = new WP_Query($args);
- }
+// $queryID = $block['attrs']['queryId'] ?? null;
+// $inherit = $block['attrs']['inherit'] ?? false;
+//
+// if ($inherit) {
+// global $wp_query;
+// $loop = $wp_query;
+// } else {
+// $args = [];
+// foreach (($block['attrs']['query'] ?? []) as $key => $value) {
+// if (empty($value)) {
+// continue;
+// }
+// switch ($key) {
+// case 'postType':
+// if ($value === BASE.'progress'){
+// $args['post_parent'] = 0;
+// }
+// $args['post_type'] = $value;
+// break;
+// case 'perPage':
+// $args['posts_per_page'] = $value;
+// break;
+// case 'orderBy':
+// $args['orderby'] = $value;
+// break;
+// case 'taxQuery':
+// $taxQuery = [];
+// foreach ($value as $tax => $terms) {
+// $taxQuery[] = [
+// 'taxonomy' => $tax,
+// 'terms' => $terms
+// ];
+// }
+// if (!empty($taxQuery)) {
+// $args['tax_query'] = $taxQuery;
+// if (count($taxQuery) > 1) {
+// $args['tax_query']['relation'] = 'OR';
+// }
+// }
+// break;
+// case 'sticky':
+// if ($value === 'ignore') {
+// $args['ignore_sticky_posts'] = true;
+// } else if ($value === 'exclude'){
+// $args['post__not_in'] = get_option('sticky_posts');
+// } else if ($value === 'only') {
+// $args['include'] = get_option('sticky_posts');
+// }
+// break;
+// case 'search':
+// $args['s'] = $value;
+// break;
+// default:
+// $args[$key] = $value;
+// break;
+//
+// }
+// }
+// $search = 'query-' . $queryID;
+// foreach ($_GET as $key => $value) {
+// if (str_contains($key, $search)) {
+// $key = str_replace($search, '', $key);
+// if ($key === 'page') {
+// $args['paged'] = (int)$value;
+// }
+// }
+// }
+// $loop = new WP_Query($args);
+// }
+//
+// $inner = '';
+// foreach ($block['innerBlocks'] as $innerBlock) {
+// switch ($innerBlock['blockName']) {
+// case 'core/post-template':
+// $inner .= '<section class="item-grid">';
+// if ($loop->have_posts()) {
+// while ($loop->have_posts()) {
+// $loop->the_post();
+// $postType = get_post_type();
+// $inner .= '<div class="item ' . jvbNoBase($postType) . '">' . $this->innerBlocks($innerBlock) . '</div>';
+// }
+// }
+// $inner .= '</section>';
+// break;
+// }
+// }
+//
+// // Reset only after a custom query, not the main query
+// if (!$inherit) {
+// wp_reset_postdata();
+// }
- $inner = '';
-
- foreach ($block['innerBlocks'] as $innerBlock) {
- switch ($innerBlock['blockName']) {
- case 'core/post-template':
- $inner .= '<section class="item-grid">';
- if ($loop->have_posts()) {
- while($loop->have_posts()) {
- $loop->the_post();
- $postType = get_post_type();
- $inner .= '<div class="item '.jvbNoBase($postType).'">'.$this->innerBlocks($innerBlock).'</div>';
- }
- }
- $inner .= '</section>';
- break;
-
- }
- }
-
-
-
- $tagName = (array_key_exists('tagName', $block['attrs'])) ? $block['attrs']['tagName'] : 'div';
- $out = '<'.$tagName.' class="loop">'.$inner.'</'.$tagName.'>';
- if ($inherit) {
- wp_reset_postdata();
- }
- return $out;
+ $tagName = $block['attrs']['tagName'] ?? 'div';
+// return sprintf(
+// '<%s class="loop">%s</%s>',
+// $tagName,
+// $this->innerBlocks($block),
+// $tagName
+// );
+ return $this->innerBlocks($block);
}
//core_query_no_results
@@ -806,59 +995,153 @@
//core_query_pagination_next
//core_query_pagination_numbers
//core_query_pagination_previous
- //core_query_title
+
+ public function prerender_core_query_title(array $block, ?string $content, ?WP_Block $parent):?string
+ {
+ jvbDump($block);
+ $attr = $block['attrs'];
+ $name = '';
+ $showPrefix = $attr['showPrefix']??false;
+ $obj = get_queried_object();
+ if (is_tax()) {
+ $name = $showPrefix ? $obj->label.':' : '';
+ $name .= $obj->name;
+ } else if (is_post_type_archive()) {
+ $name = $showPrefix ? 'Archive: ' : '';
+ $name .= $obj->label;
+ } elseif (is_search()) {
+ $name = '<small>Search results for:</small> '.get_search_query();
+ } elseif (is_singular()) {
+ $name = $obj->post_title;
+ }
+ $level = array_key_exists('level', $attr) ? 'h'.$attr['level'] : 'h1';
+
+ return sprintf(
+ '<%s id="%s">%s</%s>',
+ $level,
+ sanitize_title($name),
+ $name,
+ $level
+ );
+ }
//core_read_more
- public function render_core_template_part(array $block, string $content):string
+ public function prerender_core_template_part(array $block, ?string $content, ?WP_Block $parent):?string
{
-
- $isHeaderTemplate = (
- (array_key_exists('slug', $block['attrs']) && str_contains(strtolower($block['attrs']['slug']), 'header')) ||
- (array_key_exists('tagName', $block['attrs']) && str_contains(strtolower($block['attrs']['tagName']), 'header'))
- ) ? 'header' : false;
- $isFooterTemplate = (
- (array_key_exists('slug', $block['attrs']) && str_contains(strtolower($block['attrs']['slug']), 'footer')) ||
- (array_key_exists('tagName', $block['attrs']) && str_contains(strtolower($block['attrs']['tagName']), 'footer'))
- ) ? 'footer' : false;
+// jvbDump($block, 'template part');
+// jvbDump($parent, 'Parent');
- if (($isHeaderTemplate || $isFooterTemplate)) {
- $innerContent = $content;
+ $slug = $block['attrs']['slug'] ?? null;
+ $theme = $block['attrs']['theme'] ?? get_stylesheet();
+ $tag = $block['attrs']['tagName'] ?? 'div';
+ if (!$slug) {
+ return $content;
+ }
- $tag = $isHeaderTemplate ?: $isFooterTemplate ?: 'div';
+ // Try to get the template part post (customized via FSE)
+ $template_part = get_block_template( "$theme//$slug", 'wp_template_part' );
- $breadcrumbs = $themeSwitch = $afterHeader = $beforeHeader = $footerText= '';
- if ($isHeaderTemplate) {
+ if ( $template_part && ! empty( $template_part->content ) ) {
+ $block['innerBlocks'] = parse_blocks( $template_part->content );
- $beforeHeader = apply_filters('jvbAboveHeader', $beforeHeader);
- if ($beforeHeader !== '') {
- $beforeHeader = '<aside class="pre header row btw">'.$beforeHeader.'</aside>';
- }
- $themeSwitch = jvbDarkModeToggle();
- $breadcrumbs = BreadcrumbManager::getInstance()->renderNavigation();
- $afterHeader = apply_filters('jvbBelowHeader', $afterHeader);
+ $before = $themeSwitch = $after = $beforeClose = '';
+ switch ($tag) {
+ case 'header':
+ $before = apply_filters('jvbAboveHeader', '');
+ if (!empty($before)) {
+ $before = sprintf(
+ '<aside class="pre header row btw">%s</aside>',
+ $before
+ );
+ }
+ $themeSwitch = jvbDarkModeToggle();
- if ($afterHeader !== '') {
- $afterHeader = '<aside class="sub header row btw">'.$afterHeader.'</aside>';
- }
- $footerText = '<div class="scroll-progress"><div class="bar"></div>
-</div>';
- } elseif ($isFooterTemplate) {
- $beforeHeader = apply_filters('jvbBeforeFooter', '');
- if ($beforeHeader !== '') {
- $beforeHeader = '<aside class="footer">'.$beforeHeader.'</aside>';
- }
- $footerText = jvbRandomFooterText();
+ $after = apply_filters('jvbBelowHeader', $after);
+ if (!empty($after)) {
+ $after = sprintf(
+ '<aside class="sub header row btw">%s</aside>',
+ $after
+ );
+ }
+ $after .= BreadcrumbManager::getInstance()->renderNavigation();
+ $beforeClose = '<div class="scroll-progress"><div class="bar"></div></div>';
+ break;
+ case 'footer':
+ $before = apply_filters('jvbBeforeFooter', $before);
+ if (!empty($before)) {
+ $before = sprintf(
+ '<aside class="pre footer">%s</aside>',
+ $before
+ );
+ }
+ $beforeClose = jvbRandomFooterText();
+ break;
}
- $content = $beforeHeader.'<'.$tag.$this->getClassesAndStyles($block['attrs']).'>'.
- $themeSwitch .
- $this->inside($block, false, $innerContent).
-// $this->innerBlocks($block).
-// $innerContent.
- $footerText.'</'.$tag.'>'.$afterHeader.$breadcrumbs;
- }
+ return sprintf(
+ '%s<%s%s>%s%s%s</%s>%s',
+ $before,
+ $tag,
+ $this->getClassesAndStyles($block['attrs']??[]),
+ $themeSwitch,
+ $this->innerBlocks($block),
+ $beforeClose,
+ $tag,
+ $after
+ );
- return $content;
+ }
+ if (JVB_TESTING) {
+ jvbDump('Could not create template part block for '.$block['blockName']);
+ }
+ return $content;
+
+// $isHeaderTemplate = (
+// (array_key_exists('slug', $block['attrs']??[]) && str_contains(strtolower($block['attrs']['slug']), 'header')) ||
+// (array_key_exists('tagName', $block['attrs']??[]) && str_contains(strtolower($block['attrs']['tagName']), 'header'))
+// ) ? 'header' : false;
+// $isFooterTemplate = (
+// (array_key_exists('slug', $block['attrs']??[]) && str_contains(strtolower($block['attrs']['slug']), 'footer')) ||
+// (array_key_exists('tagName', $block['attrs']??[]) && str_contains(strtolower($block['attrs']['tagName']), 'footer'))
+// ) ? 'footer' : false;
+// if (($isHeaderTemplate || $isFooterTemplate)) {
+// $innerContent = $content;
+//
+// $tag = $isHeaderTemplate ?: $isFooterTemplate ?: 'div';
+//
+// $breadcrumbs = $themeSwitch = $afterHeader = $beforeHeader = $footerText= '';
+// if ($isHeaderTemplate) {
+//
+// $beforeHeader = apply_filters('jvbAboveHeader', $beforeHeader);
+// if ($beforeHeader !== '') {
+// $beforeHeader = '<aside class="pre header row btw">'.$beforeHeader.'</aside>';
+// }
+// $themeSwitch = jvbDarkModeToggle();
+// $breadcrumbs = BreadcrumbManager::getInstance()->renderNavigation();
+// $afterHeader = apply_filters('jvbBelowHeader', $afterHeader);
+//
+// if ($afterHeader !== '') {
+// $afterHeader = '<aside class="sub header row btw">'.$afterHeader.'</aside>';
+// }
+// $footerText = '<div class="scroll-progress"><div class="bar"></div>
+//</div>';
+// } elseif ($isFooterTemplate) {
+// $beforeHeader = apply_filters('jvbBeforeFooter', '');
+// if ($beforeHeader !== '') {
+// $beforeHeader = '<aside class="footer">'.$beforeHeader.'</aside>';
+// }
+// $footerText = jvbRandomFooterText();
+// }
+//
+// $content = $beforeHeader.'<'.$tag.$this->getClassesAndStyles($block['attrs']??[]).'>'.
+// $themeSwitch .
+// $this->innerBlocks($block).
+//// $this->innerBlocks($block).
+//// $innerContent.
+// $footerText.'</'.$tag.'>'.$afterHeader.$breadcrumbs;
+// }
+//
+// return $content;
}
//core_term_description
@@ -876,10 +1159,14 @@
//core_rss
//core_search
//core_shortcode
- public function render_core_social_link(array $block, string $content):string
+ public function prerender_core_social_link(array $block, ?string $content, ?WP_Block $parent):?string
{
- $url = $block['attrs']['url'];
- $service = $block['attrs']['service'];
+// jvbDump($block, 'social link');
+// jvbDump($parent, 'Parent');
+
+ $attrs = $block['attrs']??[];
+ $url = $attrs['url']??'';
+ $service = $attrs['service']?:'';
$iconName = ($service === 'bluesky') ? 'butterfly' : $service.'-logo';
$icon = jvbIcon($iconName);
if (!$icon) {
@@ -887,9 +1174,12 @@
}
return '<li><a href="'.$url.'" target="_blank" rel="nofollow" title="Find us on '.ucfirst($service).'">'.$icon.'<span class="screen-reader-text">Find us on '.ucfirst($service).'</span></a></li>';
}
- public function render_core_social_links(array $block, string $content):string
+ public function prerender_core_social_links(array $block, ?string $content, ?WP_Block $parent):?string
{
- return '<ul class="socials">'.$this->inside($block, false, $content).'</ul>';
+// jvbDump($block, 'social links');
+// jvbDump($parent, 'Parent');
+
+ return '<ul class="socials">'.$this->innerBlocks($block).'</ul>';
}
//core_tag_cloud
@@ -911,56 +1201,42 @@
/***********************************
* Helpers
**********************************/
- public function stripTagContents(string $tag, string $content):string
+ public function stripTagContents(string $tag, ?string $content):string
{
$clean = preg_replace('/<'.$tag.'\b[^>]*>.*?<\/'.$tag.'>/is', '', $content);
$clean = preg_replace('/\s+/', ' ', $clean);
return trim($clean);
}
- public function innerBlocks(array $block, string $before = '', string $after = ''):string
+ public function innerBlocks(array $block, string $before = '', string $after = '', bool $prerender = true):string
{
$content = '';
foreach ($block['innerBlocks'] as $b) {
- $content .= $this->render('', $b);
+ $content .= sprintf('%s%s%s',
+ $before,
+ render_block($b),
+ $after
+ );
}
return $content;
}
public function inside(array $block, mixed $tag = false, mixed $o = false): string
{
- if (!$o) {
- $o = trim($block['innerHTML']);
+ $html = $o ?: trim($block['innerHTML']);
+
+ if (empty($html)) {
+ return '';
}
- $dom = new \DOMDocument();
-
- @$dom->loadHTML('<?xml encoding="utf-8"?>' . $o, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
-
- // Find the real outermost element
- $root = null;
- foreach ($dom->childNodes as $node) {
- if ($node->nodeType === XML_ELEMENT_NODE) {
- $root = $node;
- break;
+ if (preg_match('/^<(\w+)[^>]*>(.*)<\/\1>$/s', $html, $matches)) {
+ if ($tag && strtolower($matches[1]) !== strtolower($tag)) {
+ return $html;
}
+ return trim($matches[2]);
}
- if (!$root) {
- return $o;
- }
-
- // Only enforce tag match if explicitly provided
- if ($tag && strtolower($root->nodeName) !== strtolower($tag)) {
- return $o;
- }
-
- $inner = '';
- foreach ($root->childNodes as $child) {
- $inner .= $dom->saveHTML($child);
- }
-
- return trim($inner);
+ return $html;
}
/**
@@ -997,7 +1273,7 @@
(!array_key_exists('attrs', $block) && !array_key_exists('id', $block['attrs'])))) {
$ID = get_post_thumbnail_id();
} else {
- if (array_key_exists('id', $block['attrs'])) {
+ if (array_key_exists('id', $block['attrs']??[])) {
$ID = $block['attrs']['id'];
} elseif (array_key_exists('mediaId', $block['attrs'])) {
$ID = $block['attrs']['mediaId'];
diff --git a/inc/managers/DashboardManager.php b/inc/managers/DashboardManager.php
index 062c1bf..eea1901 100644
--- a/inc/managers/DashboardManager.php
+++ b/inc/managers/DashboardManager.php
@@ -27,7 +27,7 @@
{
$this->cache = Cache::for('dashboard', WEEK_IN_SECONDS)->connect('user');
add_action('init', [$this, 'registerDashboard']);
- $this->cache->flush();
+
$this->user = wp_get_current_user();
$this->role = jvbUserRole($this->user->ID);
$this->userLink = (int)get_user_meta($this->user->ID, BASE.'profile_link', true);
@@ -677,7 +677,10 @@
if (function_exists($function)) {
echo $function([],'');
} else {
- echo JVB()->blocks()->render_core_site_logo([],'');
+ echo render_block( [
+ 'blockName' => 'core/site-logo',
+ 'attrs' => [],
+ ]);
}
?>
@@ -754,8 +757,11 @@
$itemMenu = $item->submenu($slug);
foreach ($taxonomies as $s) {
$taxRegistrar = Registrar::getInstance($s);
- $itemMenu->addItem($taxRegistrar->getPlural(), $taxRegistrar->getIcon())
+ if ($taxRegistrar) {
+ $itemMenu->addItem($taxRegistrar->getPlural(), $taxRegistrar->getIcon())
->url($this->baseURL.'/'.$s);
+ }
+
}
}
}
diff --git a/inc/managers/LoginManager.php b/inc/managers/LoginManager.php
index 55481ad..970b5e6 100644
--- a/inc/managers/LoginManager.php
+++ b/inc/managers/LoginManager.php
@@ -23,6 +23,7 @@
protected array $fields = [];
protected ?string $action = null;
protected string $title = '';
+ protected static LoginManager $instance;
// Token handlers registry
protected array $messageHandlers = [];
@@ -35,8 +36,10 @@
];
private int $max_file_size = 5242880; // 5MB in bytes
+
public function __construct()
{
+ self::$instance = $this;
$this->cache = Cache::for('login');
$this->cache->flush();
// Initialize magic link support if enabled
@@ -65,6 +68,10 @@
add_action('user_register', array($this, 'saveRegistrationFields'), 999, 2);
add_filter('the_seo_framework_sitemap_exclude_ids', [$this, 'excludeLoginSitemap'], 10, 1);
}
+ public static function getInstance():self
+ {
+ return self::$instance;
+ }
public function excludeLoginSitemap(array $ids): array
{
@@ -183,8 +190,13 @@
protected function setupFields():void
{
+ $this->fields = $this->getFieldsForAction($this->action);
+ }
+
+ protected function getFieldsForAction(string $action):array
+ {
$fields = [];
- switch($this->action) {
+ switch($action) {
case 'register':
$fields = $this->getRegistrationFormFields();
break;
@@ -255,9 +267,10 @@
break;
}
- $this->fields = $fields;
+ return $fields;
}
+
/**
* Ensure login page exists
*/
@@ -540,28 +553,8 @@
<h1><?=$this->labels['title']?></h1>
<?= $this->labels['description'] ?>
+ <?= $this->renderLoginForm($this->action); ?>
- <form name="<?=$form?>" method="post" data-action="jvb_<?=$this->action?>">
- <?= jvbFormStatus() ?>
- <?php wp_nonce_field('jvb_'.$this->action, '_wpnonce'); ?>
- <input type="hidden" name="action" value="jvb_<?=$this->action?>">
- <input type="hidden" name="redirect_to" value="<?= esc_attr($_GET['redirect_to'] ?? '') ?>">
- <input type="hidden" name="request_id" value="<?= wp_generate_password(16, false) ?>">
- <?= ($this->action === 'magic') ? '<input type="hidden" name="type" value="login">' : '' ?>
- <?php
- do_action('jvb_add_token_inputs', $this->action);
-
- foreach ($this->fields as $name => $config) {
- echo Form::render($name, '', $config);
- }
-
- $this->maybeTurnstile();
- ?>
- <div class="row btw nowrap">
- <button type="submit" class="button button-primary button-large"><?=$this->labels['submit']?></button>
- <?php $this->maybeMagicLink(); ?>
- </div>
- </form>
<?php
if (is_array($this->labels['extra'])) {
@@ -610,6 +603,59 @@
</div>
<?php
}
+ public function renderLoginForm(string $action = 'login', string $redirect = '', string $title = ''):string
+ {
+ ob_start();
+ do_action('jvb_add_token_inputs', $this->action);
+ $additionalInputs = ob_get_clean();
+
+ $fields = '';
+ $theFields = $this->getFieldsForAction($action);
+ foreach ($theFields as $name => $config) {
+ $fields .= Form::render($name, '', $config);
+ }
+
+ ob_start();
+ $this->maybeTurnstile();
+ $turnstile = ob_get_clean();
+
+ ob_start();
+ $this->maybeMagicLink();
+ $magicLink = ob_get_clean();
+
+ $redirect = !empty($redirect) ? $redirect : esc_attr($_GET['redirect_to'] ?? '');
+
+ return sprintf(
+ '<form name="%sform" method="post" data-action="jvb_%s">
+ %s%s%s
+ <input type="hidden" name="action" value="jvb_%s">
+ <input type="hidden" name="redirect_to" value="%s">
+ <input type="hidden" name="request_id" value="%s">
+ %s
+ %s
+ %s
+ %s
+ <div class="row btw nowrap">
+ <button type="submit" class="button button-primary button-large">%s</button>
+ %s
+ </div>
+ </form>',
+ $action,
+ $action,
+ jvbFormStatus(),
+ $title,
+ wp_nonce_field('jvb_'.$action),
+ $action,
+ $redirect,
+ wp_generate_password(16, false),
+ ($action === 'magic') ? '<input type="hidden" name="type" value="login">' : '',
+ $additionalInputs,
+ $fields,
+ $turnstile,
+ $this->labels['submit'],
+ $magicLink
+ );
+ }
protected function renderHeader():void
{
?>
@@ -968,6 +1014,11 @@
{
}
+ public function setAction(string $action = 'login'):void
+ {
+ $this->action = $action;
+ $this->setup();
+ }
}
// Initialize the login manager
diff --git a/inc/managers/SEO/BreadcrumbManager.php b/inc/managers/SEO/BreadcrumbManager.php
index bb33a3d..341debf 100644
--- a/inc/managers/SEO/BreadcrumbManager.php
+++ b/inc/managers/SEO/BreadcrumbManager.php
@@ -215,6 +215,7 @@
$name = jvbNoBase($type);
$registrar = Registrar::getInstance($name);
+
if($registrar && $registrar->hasFeature('show_directory')) {
$directory = JVB()->directories();
if ($directory && !empty($directory->directories($name)??[])){
@@ -228,11 +229,15 @@
'name' => JVB()->directories()->referAs(true),
'url' => get_post_type_archive_link($type)
];
- } elseif (is_post_type_archive() && $registrar && $registrar->hasFeature('show_directory')) {
-
+ } elseif ($registrar) {
$crumbs[] = [
- 'name' => $registrar->getConfig('breadcrumbs')['title'] ?? $registrar->getPlural(),
- 'url' => get_post_type_archive_link($type)
+ 'name' => $registrar->getConfig('breadcrumbs')['title'] ?? $registrar->getPlural(),
+ 'url' => get_post_type_archive_link($type)
+ ];
+ } else {
+ $crumbs[] = [
+ 'name' => $obj->label,
+ 'url' => get_post_type_archive_link($type)
];
}
diff --git a/inc/managers/SEO/render/Thing/Place/AdministrativeArea/State.php b/inc/managers/SEO/render/Thing/Place/AdministrativeArea/State.php
index 4d2d850..7e56ac7 100644
--- a/inc/managers/SEO/render/Thing/Place/AdministrativeArea/State.php
+++ b/inc/managers/SEO/render/Thing/Place/AdministrativeArea/State.php
@@ -1,5 +1,5 @@
<?php
-namespace JVBase\inc\managers\SEO\render\Thing\Place\AdministrativeArea;
+namespace JVBase\managers\SEO\render\Thing\Place\AdministrativeArea;
use JVBase\managers\SEO\render\Thing\Intangible\ContactPoint\PostalAddress;
use JVBase\managers\SEO\render\Thing\Place\AdministrativeArea\AdministrativeArea;
diff --git a/src/drawer-menu/block.json b/src/drawer-menu/block.json
deleted file mode 100644
index 93bc73d..0000000
--- a/src/drawer-menu/block.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/drawer-menu",
- "title": "Drawer Menu",
- "category": "jvb",
- "icon": "menu",
- "version": "1.0.0",
- "textdomain": "jvb",
- "supports": {
- "html": false
- },
- "attributes": {
- "menuId": {
- "type": "string",
- "default": ""
- },
- "collapsed": {
- "type": "boolean",
- "default": true
- }
- },
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css",
- "render": "file:./render.php"
-}
diff --git a/src/drawer-menu/edit.js b/src/drawer-menu/edit.js
deleted file mode 100644
index c37544b..0000000
--- a/src/drawer-menu/edit.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
-import { PanelBody, ToggleControl, TextControl } from '@wordpress/components';
-
-export default function Edit({ attributes, setAttributes }) {
- const { menuId, collapsed } = attributes;
- const blockProps = useBlockProps();
-
- return (
- <>
- <InspectorControls>
- <PanelBody title="Drawer Settings">
- <TextControl
- label="Menu ID"
- value={menuId}
- onChange={(value) => setAttributes({ menuId: value })}
- help="PHP-generated menu identifier"
- />
- <ToggleControl
- label="Start Collapsed"
- checked={collapsed}
- onChange={(value) => setAttributes({ collapsed: value })}
- />
- </PanelBody>
- </InspectorControls>
- <div {...blockProps}>
- <div className="drawer-menu-preview">
- <p>Drawer Menu: {menuId || 'Not configured'}</p>
- <p>State: {collapsed ? 'Collapsed' : 'Expanded'}</p>
- </div>
- </div>
- </>
- );
-}
diff --git a/src/drawer-menu/editor.scss b/src/drawer-menu/editor.scss
deleted file mode 100644
index e69de29..0000000
--- a/src/drawer-menu/editor.scss
+++ /dev/null
diff --git a/src/drawer-menu/index.js b/src/drawer-menu/index.js
deleted file mode 100644
index e81333b..0000000
--- a/src/drawer-menu/index.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { registerBlockType } from '@wordpress/blocks';
-import edit from './edit';
-import save from './save';
-import './style.scss';
-import './editor.scss';
-
-registerBlockType('jvb/drawer-menu', {
- edit,
- save,
-});
diff --git a/src/drawer-menu/index.php b/src/drawer-menu/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/drawer-menu/index.php
+++ /dev/null
diff --git a/src/drawer-menu/render.php b/src/drawer-menu/render.php
deleted file mode 100644
index 05da41a..0000000
--- a/src/drawer-menu/render.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-use JVBase\managers\Cache;
-use JVBase\ui\Navigation;
-
-$menu_id = $attributes['menuId'] ?? '';
-$collapsed = $attributes['collapsed'] ?? true;
-
-// You'd populate this from options, a filter, or however you store menu data
-$menu_items = apply_filters('jvbDrawerItems', [], $menu_id);
-
-if (empty($menu_items) || empty($menu_id)) {
- return '<p>Please configure the drawer menu in block settings.</p>';
-}
-
-$cache = Cache::for('drawer');
-
-if (!is_front_page()) {
- $menu_items[] = [
- 'text' => 'Home',
- 'url' => home_url(),
- 'icon' => 'house-simple',
- ];
-}
-$items = array_map(function($item) { return $item['text'];}, $menu_items);
-
-$key = $cache->generateKey($items);
-$menu = $cache->remember($key,
-function () use ($menu_items, $menu_id, $collapsed) {
- $menu = new Navigation($menu_id);
- $menu->asDrawer($collapsed)->populateFromArray($menu_items);
- return $menu->render();
-});
-
-global $wp;
-
-$current = home_url($wp->request.'/');
-echo str_replace($current.'"', $current.'" class="current" aria-current="page"', $menu);
diff --git a/src/drawer-menu/save.js b/src/drawer-menu/save.js
deleted file mode 100644
index 8432b3a..0000000
--- a/src/drawer-menu/save.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function save() {
- return null; // Server-side rendered
-}
diff --git a/src/drawer-menu/style.scss b/src/drawer-menu/style.scss
deleted file mode 100644
index 52e11af..0000000
--- a/src/drawer-menu/style.scss
+++ /dev/null
@@ -1,88 +0,0 @@
-nav.drawer {
- position: fixed;
- right: 0;
- bottom: 0;
- max-height: 100vh;
- overflow: hidden auto;
- width: var(--btn);
- z-index: var(--z-5);
- transition: var(--trans-size);
- --dir: column-reverse;
- background-color: var(--base);
- border-left: 1px solid var(--base-200);
- box-shadow:rgba(var(--base-rgb), var(--op-4)) var(--shdw-left);
- height: auto;
- --w: var(--chip_);
-
- .title,
- .section-title {
- display: none;
- }
- ul .icon {
- min-width: var(--chip_);
- }
-
- a,
- .a {
- height: var(--chipchip);
- padding: 0 .5rem;
- width: 100%;
- gap: .5rem;
- justify-content: center;
- }
-
- .toggle {
- width: 100%;
- .icon {
- transform: rotate(0);
- transition: var(--trans-transform);
- }
- }
- &.open {
- width: 240px;
- .title,
- .section-title {
- display: block;
- }
-
- .toggle .icon {
- transform: rotate(180deg);
- }
-
- a,
- .a {
- justify-content: flex-start;
- }
- }
-
- ul {
- --dir: column;
- --gap: 0;
- --height: auto;
- padding: 0;
- margin: 0;
- height: auto;
- width:100%;
- }
- li {
- height: auto;
- width: 100%;
- }
-
-
- .row {
- width: 100%;
- }
-
- .menu-section {
- border-bottom: 1px solid var(--contrast-200);
- }
- .section-title {
- padding: 0.5rem var(--px);
- font-size: var(--small);
- text-transform: uppercase;
- opacity: 0.6;
- font-weight: bold;
- }
-
-}
diff --git a/src/drawer-menu/view.js b/src/drawer-menu/view.js
deleted file mode 100644
index 8b13789..0000000
--- a/src/drawer-menu/view.js
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/faq/block.json b/src/faq/block.json
deleted file mode 100644
index f88d33a..0000000
--- a/src/faq/block.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/faq",
- "title": "FAQ Block",
- "category": "jvb",
- "icon": "info",
- "description": "Display FAQs organized by sections with customizable ordering",
- "keywords": ["faq", "questions", "help"],
- "version": "1.0.0",
- "textdomain": "jvb",
- "attributes": {
- "sectionOrder": {
- "type": "array",
- "default": []
- },
- "showSectionTitles": {
- "type": "boolean",
- "default": true
- },
- "collapseByDefault": {
- "type": "boolean",
- "default": false
- }
- },
- "supports": {
- "align": ["wide", "full"],
- "html": false
- },
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css",
- "viewScript": "file:./view.js"
-}
diff --git a/src/faq/edit.js b/src/faq/edit.js
deleted file mode 100644
index 73e36e4..0000000
--- a/src/faq/edit.js
+++ /dev/null
@@ -1,145 +0,0 @@
-import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
-import { PanelBody, ToggleControl, Notice, Button } from '@wordpress/components';
-import { __ } from '@wordpress/i18n';
-import { useState, useEffect } from '@wordpress/element';
-import './editor.scss';
-
-export default function Edit({ attributes, setAttributes }) {
- const { sectionOrder, showSectionTitles, collapseByDefault } = attributes;
- const [sections, setSections] = useState([]);
-
- // Get sections from localized script
- const allSections = window.jvbFaq?.sections || [];
-
- // Initialize sections with proper ordering
- useEffect(() => {
- if (!allSections.length) return;
-
- if (sectionOrder.length === 0) {
- // First time - use default order
- const orderedSections = allSections.map(section => ({
- id: section.id,
- name: section.name,
- }));
- setSections(orderedSections);
- setAttributes({ sectionOrder: orderedSections.map(s => s.id) });
- } else {
- // Use saved order, add any new sections at the end
- const orderedSections = [];
- const existingIds = new Set(sectionOrder);
-
- // Add sections in saved order
- sectionOrder.forEach(id => {
- const section = allSections.find(s => s.id === id);
- if (section) {
- orderedSections.push({ id: section.id, name: section.name });
- }
- });
-
- // Add any new sections that weren't in the saved order
- allSections.forEach(section => {
- if (!existingIds.has(section.id)) {
- orderedSections.push({ id: section.id, name: section.name });
- }
- });
-
- setSections(orderedSections);
- }
- }, [allSections, sectionOrder]);
-
- const moveSection = (index, direction) => {
- const newSections = [...sections];
- const newIndex = direction === 'up' ? index - 1 : index + 1;
-
- if (newIndex < 0 || newIndex >= newSections.length) return;
-
- // Swap sections
- [newSections[index], newSections[newIndex]] = [newSections[newIndex], newSections[index]];
-
- setSections(newSections);
- setAttributes({ sectionOrder: newSections.map(s => s.id) });
- };
-
- const blockProps = useBlockProps({
- className: 'faq-block-editor',
- });
-
- return (
- <>
- <InspectorControls>
- <PanelBody title={__('FAQ Settings', 'jvb')} initialOpen={true}>
- <ToggleControl
- label={__('Show Section Titles', 'jvb')}
- checked={showSectionTitles}
- onChange={(value) => setAttributes({ showSectionTitles: value })}
- help={__('Display section names as headings', 'jvb')}
- />
- <ToggleControl
- label={__('Collapse by Default', 'jvb')}
- checked={collapseByDefault}
- onChange={(value) => setAttributes({ collapseByDefault: value })}
- help={__('Questions start collapsed and expand on click', 'jvb')}
- />
- </PanelBody>
-
- <PanelBody title={__('Section Order', 'jvb')} initialOpen={false}>
- <p className="components-base-control__help">
- {__('Use the arrow buttons to reorder sections', 'jvb')}
- </p>
- {sections.length > 0 ? (
- <div className="faq-section-list">
- {sections.map((section, index) => (
- <div key={section.id} className="faq-section-item">
- <div className="faq-section-controls">
- <Button
- icon="arrow-up-alt2"
- label={__('Move up', 'jvb')}
- disabled={index === 0}
- onClick={() => moveSection(index, 'up')}
- className="faq-section-button"
- />
- <Button
- icon="arrow-down-alt2"
- label={__('Move down', 'jvb')}
- disabled={index === sections.length - 1}
- onClick={() => moveSection(index, 'down')}
- className="faq-section-button"
- />
- </div>
- <span className="faq-section-name">{section.name}</span>
- </div>
- ))}
- </div>
- ) : (
- <Notice status="info" isDismissible={false}>
- {__('No sections found. Create sections in the FAQ taxonomy.', 'jvb')}
- </Notice>
- )}
- </PanelBody>
- </InspectorControls>
-
- <div {...blockProps}>
- <div className="faq-block-preview">
- <h3>{__('FAQ Block', 'jvb')}</h3>
- <p>
- {__('This block will display FAQs organized by sections.', 'jvb')}
- </p>
- {sections.length > 0 ? (
- <div className="faq-sections-preview">
- <strong>{__('Section Order:', 'jvb')}</strong>
- <ol>
- {sections.map((section) => (
- <li key={section.id}>{section.name}</li>
- ))}
- </ol>
- </div>
- ) : (
- <Notice status="warning" isDismissible={false}>
- {__('No sections available. Create sections in the FAQ taxonomy.', 'jvb')}
- </Notice>
- )}
- </div>
- </div>
- </>
- );
-}
diff --git a/src/faq/editor.scss b/src/faq/editor.scss
deleted file mode 100644
index d9955a2..0000000
--- a/src/faq/editor.scss
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * FAQ Block - Editor Styles
- */
-
-.faq-block-editor {
- padding: 2rem;
- border: 2px dashed #ccc;
- border-radius: 8px;
- background: #f9f9f9;
-
- .faq-block-preview {
- text-align: center;
-
- h3 {
- margin: 0 0 0.5rem;
- font-size: 1.25rem;
- font-weight: 600;
- }
-
- > p {
- margin: 0 0 1.5rem;
- color: #666;
- }
-
- .faq-sections-preview {
- margin-top: 1.5rem;
- text-align: left;
- background: white;
- padding: 1rem;
- border-radius: 4px;
-
- strong {
- display: block;
- margin-bottom: 0.5rem;
- }
-
- ol {
- margin: 0;
- padding-left: 1.5rem;
-
- li {
- margin: 0.25rem 0;
- padding: 0.25rem 0;
- }
- }
- }
- }
-}
-
-// Inspector Controls
-.faq-section-list {
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
- margin-top: 0.5rem;
-}
-
-.faq-section-item {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- padding: 0.5rem 0.75rem;
- background: white;
- border: 1px solid #ddd;
- border-radius: 4px;
- transition: background 150ms ease;
-
- &:hover {
- background: #f9f9f9;
- }
-}
-
-.faq-section-controls {
- display: flex;
- gap: 0.25rem;
- flex-shrink: 0;
-}
-
-.faq-section-button {
- min-width: 30px !important;
- padding: 4px !important;
- height: 30px !important;
-
- &:disabled {
- opacity: 0.3;
- cursor: not-allowed;
- }
-}
-
-.faq-section-name {
- flex: 1;
- font-weight: 500;
- padding-left: 0.5rem;
-}
-
-// Notice adjustments
-.components-panel__body .components-notice {
- margin: 1rem 0;
-}
diff --git a/src/faq/index.js b/src/faq/index.js
deleted file mode 100644
index 2f2dd15..0000000
--- a/src/faq/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { registerBlockType } from '@wordpress/blocks';
-import Edit from './edit';
-import './style.scss';
-import './editor.scss';
-import metadata from './block.json';
-
-registerBlockType(metadata.name, {
- edit: Edit,
- // No save function - dynamic block rendered on server
- save: () => null,
-});
diff --git a/src/faq/index.php b/src/faq/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/faq/index.php
+++ /dev/null
diff --git a/src/faq/render.php b/src/faq/render.php
deleted file mode 100644
index e69de29..0000000
--- a/src/faq/render.php
+++ /dev/null
diff --git a/src/faq/style.scss b/src/faq/style.scss
deleted file mode 100644
index 13564fa..0000000
--- a/src/faq/style.scss
+++ /dev/null
@@ -1,72 +0,0 @@
-nav#faq {
- height: max-content;
- display: block;
- background-color: var(--base-100);
- border-radius: var(--radius-outer);
- padding: 1.5rem;
- touch-action: auto;
- ol {
- list-style: decimal-leading-zero;
- height: fit-content;
- display: block;
- counter-reset: faq;
- li {
- counter-increment: faq;
- width: max-content;
- &::before {
- content: counter(faq);
- display: block;
- font-family: var(--heading);
- font-weight: var(--fw-h-bold);
- }
- }
- }
- h2 {
- left: 0;
- font-size: var(--txt-large);
- margin: .5rem 0 .5rem;
- }
- a {
- padding: .5rem;
- }
-}
-
-.faq-block {
- padding-bottom: 3rem;
- max-width: none;
- width: 100%;
- > * {
- max-width: var(--wide);
- margin: 1rem auto;
- }
- h2 {
- margin: 5rem 0 1.5rem;
- }
- h3 {
- margin: 0;
- text-transform: none;
- }
- :target {
- background-color: var(--base);
- outline: none;
-
- h2 {
- background-color: var(--base);
- padding: 1rem 1.5rem;
- border-radius: var(--radius-outer);
- }
- }
- details {
- max-width: var(--content);
- margin: 1rem auto;
- padding: .75rem;
- }
- details + details {
- margin-top: 3rem;
- }
- details .button {
- height: fit-content;
- display: flex;
- margin-left: auto;
- }
-}
diff --git a/src/faq/view.js b/src/faq/view.js
deleted file mode 100644
index 1de987d..0000000
--- a/src/faq/view.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * FAQ Block - Frontend Interactions
- * Handles accordion functionality for FAQ items
- */
-
-document.addEventListener('DOMContentLoaded', () => {
- const faqBlocks = document.querySelectorAll('.faq-block');
-
- faqBlocks.forEach((block) => {
- const faqItems = block.querySelectorAll('.faq-item');
-
- faqItems.forEach((item) => {
- const button = item.querySelector('.faq-item__question');
- const answer = item.querySelector('.faq-item__answer');
-
- if (!button || !answer) return;
-
- button.addEventListener('click', () => {
- const isExpanded = button.getAttribute('aria-expanded') === 'true';
-
- // Toggle this item
- button.setAttribute('aria-expanded', !isExpanded);
-
- if (isExpanded) {
- // Collapse
- answer.style.height = answer.scrollHeight + 'px';
- // Force reflow
- answer.offsetHeight;
- answer.style.height = '0';
-
- setTimeout(() => {
- answer.style.display = 'none';
- answer.style.height = '';
- }, 300);
-
- item.classList.remove('faq-item--expanded');
- } else {
- // Expand
- answer.style.display = 'block';
- answer.style.height = '0';
- // Force reflow
- answer.offsetHeight;
- answer.style.height = answer.scrollHeight + 'px';
-
- setTimeout(() => {
- answer.style.height = 'auto';
- }, 300);
-
- item.classList.add('faq-item--expanded');
- }
- });
-
- // Handle keyboard navigation
- button.addEventListener('keydown', (e) => {
- // Space or Enter triggers the button
- if (e.key === ' ' || e.key === 'Enter') {
- e.preventDefault();
- button.click();
- }
- });
- });
- });
-
- // Optional: Add URL hash navigation
- // If URL has #faq-123, open that specific FAQ
- if (window.location.hash) {
- const hash = window.location.hash.substring(1);
- const targetItem = document.querySelector(`[data-faq-id="${hash}"]`);
-
- if (targetItem) {
- const button = targetItem.querySelector('.faq-item__question');
- const isExpanded = button.getAttribute('aria-expanded') === 'true';
-
- if (!isExpanded) {
- button.click();
- }
-
- // Scroll to item after a short delay
- setTimeout(() => {
- targetItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
- }, 350);
- }
- }
-});
diff --git a/src/feed/block.json b/src/feed/block.json
deleted file mode 100644
index 3fec37e..0000000
--- a/src/feed/block.json
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/feed",
- "title": "Feed",
- "category": "jvb",
- "icon": "grid-view",
- "description": "Displays a filterable feed of registered content types",
- "keywords": [ "feed", "grid" ],
- "version": "0.9.0",
- "textdomain": "jvb",
- "supports": {
- "html": false,
- "align": ["wide", "full"]
- },
- "attributes": {
- "title": {
- "type": "string",
- "default": "Your Scene"
- },
- "inheritQuery": {
- "type": "boolean",
- "default": false
- },
- "contentTypes": {
- "type": "array",
- "default": ["tattoo", "artwork", "artist"],
- "items": {
- "type": "string"
- }
- },
- "itemsPerPage": {
- "type": "number",
- "default": 36
- },
- "defaultOrder": {
- "type": "string",
- "default": "date_desc"
- }
- },
- "selectors": {
- "root": ".feed-block"
- },
- "styles": [
- { "name": "default", "label": "Default", "isDefault": true },
- { "name": "other", "label": "Other" }
- ],
- "example": {
- "attributes": {
- "message": "This is a notice!"
- }
- },
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css",
- "viewScript": "file:./view.js"
-}
diff --git a/src/feed/edit.js b/src/feed/edit.js
deleted file mode 100644
index 2a5228d..0000000
--- a/src/feed/edit.js
+++ /dev/null
@@ -1,256 +0,0 @@
-/**
- * Feed Block - Edit Component
- * Fetches available feed types from /jvb/v1/feed/types
- * Allows configuration of content types and inherit query setting
- */
-
-import { useEffect, useState } from '@wordpress/element';
-import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
-import {
- PanelBody,
- CheckboxControl,
- ToggleControl,
- Spinner,
- Notice
-} from '@wordpress/components';
-import apiFetch from '@wordpress/api-fetch';
-import { __ } from '@wordpress/i18n';
-
-export default function Edit({ attributes, setAttributes }) {
- const [feedTypes, setFeedTypes] = useState(null);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState(null);
-
- const blockProps = useBlockProps({
- className: 'feed-block-editor'
- });
-
- /**
- * Fetch available feed types on component mount
- */
- useEffect(() => {
- apiFetch({
- path: '/jvb/v1/feed/types',
- headers: {
- 'If-Modified-Since': localStorage.getItem('feed_types_modified'),
- }
- })
- .then(types => {
- setFeedTypes(types);
- setLoading(false);
-
- // Store Last-Modified for future requests
- // (apiFetch doesn't expose response headers easily,
- // but the server will handle 304s)
-
- // Initialize contentTypes if not set and not inheriting
- if (!attributes.contentTypes && !attributes.inheritQuery) {
- const firstType = Object.keys(types)[0];
- if (firstType) {
- setAttributes({ contentTypes: [firstType] });
- }
- }
- })
- .catch(err => {
- console.error('Error loading feed types:', err);
- setError(err.message);
- setLoading(false);
- });
- }, [attributes.inheritQuery]);
-
- /**
- * Toggle a content type in the selection
- */
- const toggleContentType = (slug, checked) => {
- const currentTypes = attributes.contentTypes || [];
-
- const newTypes = checked
- ? [...currentTypes, slug]
- : currentTypes.filter(t => t !== slug);
-
- setAttributes({ contentTypes: newTypes });
- };
-
- /**
- * Get friendly label for content type
- */
- const getTypeLabel = (slug, config) => {
- return `${config.plural} (${config.type})`;
- };
-
- /**
- * Group types by category for better UX
- */
- const groupedTypes = feedTypes ? {
- content: Object.entries(feedTypes)
- .filter(([_, config]) => config.type === 'content'),
- taxonomy: Object.entries(feedTypes)
- .filter(([_, config]) => config.type === 'taxonomy')
- } : { content: [], taxonomy: [] };
-
- return (
- <div {...blockProps}>
- <InspectorControls>
- <PanelBody
- title={__('Feed Settings', 'jvb')}
- initialOpen={true}
- >
- <ToggleControl
- label={__('Inherit from Page Context', 'jvb')}
- help={
- attributes.inheritQuery
- ? __('Feed will adapt to the current page (profile, taxonomy, etc.)', 'jvb')
- : __('Manually select content types to display', 'jvb')
- }
- checked={attributes.inheritQuery}
- onChange={(value) => setAttributes({ inheritQuery: value })}
- />
-
- {!attributes.inheritQuery && (
- <>
- {loading && (
- <div style={{ textAlign: 'center', padding: '20px' }}>
- <Spinner />
- <p>{__('Loading feed types...', 'jvb')}</p>
- </div>
- )}
-
- {error && (
- <Notice status="error" isDismissible={false}>
- {__('Error loading feed types: ', 'jvb')} {error}
- </Notice>
- )}
-
- {!loading && !error && feedTypes && (
- <>
- {groupedTypes.content.length > 0 && (
- <>
- <h4>{__('Content Types', 'jvb')}</h4>
- {groupedTypes.content.map(([slug, config]) => (
- <CheckboxControl
- key={slug}
- label={getTypeLabel(slug, config)}
- checked={
- attributes.contentTypes?.includes(slug) || false
- }
- onChange={(checked) =>
- toggleContentType(slug, checked)
- }
- help={
- config.taxonomies?.length > 0
- ? `Filters: ${config.taxonomies.join(', ')}`
- : null
- }
- />
- ))}
- </>
- )}
-
- {groupedTypes.taxonomy.length > 0 && (
- <>
- <h4 style={{ marginTop: '20px' }}>
- {__('Content Taxonomies', 'jvb')}
- </h4>
- <p style={{ fontSize: '12px', color: '#757575' }}>
- {__('These are collections that group other content', 'jvb')}
- </p>
- {groupedTypes.taxonomy.map(([slug, config]) => (
- <CheckboxControl
- key={slug}
- label={getTypeLabel(slug, config)}
- checked={
- attributes.contentTypes?.includes(slug) || false
- }
- onChange={(checked) =>
- toggleContentType(slug, checked)
- }
- help={
- config.for_content?.length > 0
- ? `Contains: ${config.for_content.join(', ')}`
- : null
- }
- />
- ))}
- </>
- )}
-
- {!attributes.contentTypes?.length && (
- <Notice status="warning" isDismissible={false}>
- {__('Please select at least one content type', 'jvb')}
- </Notice>
- )}
- </>
- )}
- </>
- )}
- </PanelBody>
-
- <PanelBody
- title={__('Display Settings', 'jvb')}
- initialOpen={false}
- >
- <ToggleControl
- label={__('Show Gallery View', 'jvb')}
- help={__('Enable lightbox for images', 'jvb')}
- checked={attributes.enableGallery || false}
- onChange={(value) =>
- setAttributes({ enableGallery: value })
- }
- />
- </PanelBody>
- </InspectorControls>
-
- <div className="feed-block-placeholder">
- <div className="feed-block-icon">
- <svg width="48" height="48" viewBox="0 0 24 24" fill="none">
- <rect x="3" y="3" width="7" height="7" fill="currentColor" opacity="0.3" />
- <rect x="13" y="3" width="7" height="7" fill="currentColor" opacity="0.3" />
- <rect x="3" y="13" width="7" height="7" fill="currentColor" opacity="0.3" />
- <rect x="13" y="13" width="7" height="7" fill="currentColor" opacity="0.3" />
- </svg>
- </div>
-
- <h3>{__('Feed Block', 'jvb')}</h3>
-
- {attributes.inheritQuery ? (
- <p className="feed-block-description">
- {__('📍 Inheriting from page context', 'jvb')}
- </p>
- ) : (
- <div className="feed-block-description">
- {attributes.contentTypes?.length > 0 ? (
- <>
- <p><strong>{__('Showing:', 'jvb')}</strong></p>
- <ul style={{
- listStyle: 'none',
- padding: '0',
- margin: '8px 0'
- }}>
- {attributes.contentTypes.map(type => {
- const config = feedTypes?.[type];
- return (
- <li key={type} style={{
- padding: '4px 0',
- color: '#2271b1'
- }}>
- ✓ {config?.plural || type}
- </li>
- );
- })}
- </ul>
- </>
- ) : (
- <p style={{ color: '#d63638' }}>
- {__('⚠️ No content types selected', 'jvb')}
- </p>
- )}
- </div>
- )}
-
- <p className="feed-block-note">
- {__('Feed will be displayed on the frontend', 'jvb')}
- </p>
- </div>
- </div>
- );
-}
diff --git a/src/feed/editor.scss b/src/feed/editor.scss
deleted file mode 100644
index 8f0baab..0000000
--- a/src/feed/editor.scss
+++ /dev/null
@@ -1,128 +0,0 @@
-.feed-content-types {
- margin-bottom: 16px;
-
- .components-base-control__label {
- margin-bottom: 8px;
- font-weight: 500;
- }
-
- .components-checkbox-control {
- margin-bottom: 8px;
-
- &:last-child {
- margin-bottom: 0;
- }
-
- .components-checkbox-control__input-container {
- margin-right: 8px;
- }
- }
-}
-.feed-block {
- border: 1px solid #ddd;
- padding: 20px;
- background: white;
-
- .feed-block-preview {
- .filter-preview {
- display: flex;
- gap: 8px;
- margin: 16px 0;
- flex-wrap: wrap;
-
- .content-type-badge {
- background: #f0f0f0;
- padding: 4px 8px;
- border-radius: 4px;
- font-size: 12px;
- }
- }
-
- .feed-grid-placeholder {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
- gap: 16px;
- margin-top: 20px;
-
- .grid-item-placeholder {
- background: #f0f0f0;
- aspect-ratio: 1;
- border-radius: 4px;
- }
- }
- }
-}
-
-.feed-content-types {
- margin-bottom: 16px;
-
- .components-base-control__label {
- margin-bottom: 8px;
- font-weight: 500;
- }
-
- .checkbox-list {
- border: 1px solid #ddd;
- border-radius: 4px;
- max-height: 200px;
- overflow-y: auto;
- padding: 8px;
- background: white;
-
- .components-checkbox-control {
- margin: 4px 0;
-
- &:first-child {
- margin-top: 0;
- }
-
- &:last-child {
- margin-bottom: 0;
- }
- }
- }
-
- .select-all-wrapper {
- margin-top: 8px;
- padding-top: 8px;
- border-top: 1px solid #ddd;
- }
-
- .components-checkbox-control__input-container {
- margin-right: 8px;
- }
-}
-.feed-block {
- border: 1px solid #ddd;
- padding: 20px;
- background: white;
-
- .feed-block-preview {
- .filter-preview {
- display: flex;
- gap: 8px;
- margin: 16px 0;
- flex-wrap: wrap;
-
- .content-type-badge {
- background: #f0f0f0;
- padding: 4px 8px;
- border-radius: 4px;
- font-size: 12px;
- }
- }
-
- .feed-grid-placeholder {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
- gap: 16px;
- margin-top: 20px;
-
- .grid-item-placeholder {
- background: #f0f0f0;
- aspect-ratio: 1;
- border-radius: 4px;
- }
- }
- }
-}
diff --git a/src/feed/index.js b/src/feed/index.js
deleted file mode 100644
index c477d23..0000000
--- a/src/feed/index.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Registers a new block provided a unique name and an object defining its behavior.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-import { registerBlockType } from '@wordpress/blocks';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * All files containing `style` keyword are bundled together. The code used
- * gets applied both to the front of your site and to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './style.scss';
-
-/**
- * Internal dependencies
- */
-import Edit from './edit';
-import save from './save';
-import metadata from './block.json';
-
-/**
- * Every block starts by registering a new block type definition.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-registerBlockType( metadata.name, {
- /**
- * @see ./edit.js
- */
- edit: Edit,
-
- /**
- * @see ./save.js
- */
- save,
-} );
diff --git a/src/feed/index.php b/src/feed/index.php
deleted file mode 100644
index 6220032..0000000
--- a/src/feed/index.php
+++ /dev/null
@@ -1,2 +0,0 @@
-<?php
-// Silence is golden.
diff --git a/src/feed/render.php b/src/feed/render.php
deleted file mode 100644
index 0112ad1..0000000
--- a/src/feed/render.php
+++ /dev/null
@@ -1,4 +0,0 @@
-<?php
-if (!defined('ABSPATH')) {
- exit; // Exit if accessed directly
-}
diff --git a/src/feed/save.js b/src/feed/save.js
deleted file mode 100644
index 8169594..0000000
--- a/src/feed/save.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function save() {
- return null; // Dynamic block rendered by PHP
-}
diff --git a/src/feed/style.scss b/src/feed/style.scss
deleted file mode 100644
index 3d4e777..0000000
--- a/src/feed/style.scss
+++ /dev/null
@@ -1,956 +0,0 @@
-//.feed-block {
-// max-width: var(--full);
-// margin: 0 auto;
-//
-// &:target {
-// scroll-snap-margin-top: 5rem;
-// scroll-margin-top: 5rem;
-// outline: 0;
-// border-radius: 0;
-// padding: 0;
-//
-// .feed-item {
-// outline: double var(--pink-0);
-// }
-// }
-//}
-//
-//.loading .feed-block {
-// opacity: .7;
-//}
-//
-//.label {
-// display: flex;
-// align-items: center;
-// gap: .25rem;
-// font-size: .9rem;
-// a:hover {
-// color: var(--pink-0);
-// }
-//}
-//
-///** Filters Form **/
-//.feed-filters {
-// margin: 2rem 0;
-// position: sticky;
-// top: 3rem;
-// z-index: 15;
-// background: rgba(var(--base-rgb),var(--op-6));
-// padding: .25rem 3rem;
-// details[open] summary {
-// background-color: var(--overlay);
-// }
-// summary {
-// justify-content: flex-start;
-//
-// > * {
-// order: 3;
-// }
-// .label {
-// order: 1;
-// }
-// .filter-label {
-// order: 2;
-// }
-// &::after {
-// order: 4;
-// }
-// #favourites + label {
-// margin-left: auto;
-// }
-// #favourites + label:hover,
-// #favourites:checked + label {
-// border-color: var(--pink-0);
-// background-color: var(--pink-0);
-// color: var(--white);
-//
-// }
-// #favourites:checked + label {
-// animation: pop 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
-// }
-// }
-//
-// details[open],
-// summary:hover {
-// background-color: rgba(var(--base-rgb),var(--op-6));
-// }
-//
-// &:has(#favourites) {
-// summary::after {
-// margin-left: 1rem;
-// }
-// }
-//}
-//summary > * {
-// order: 3;
-//}
-//summary .label {
-// order: 1;
-//}
-//.filter-label {
-// display: inline-block;
-// vertical-align: middle;
-// height: 1.3em;
-// order: 2;
-// margin: 0;
-// padding: 0;
-//
-// li {
-// list-style: none;
-// height: 0;
-// overflow: hidden;
-// &.active {
-// height: 100%;
-// }
-// }
-//}
-//.filter-group {
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-// flex-wrap: wrap;
-// label {
-// font-weight: 100;
-// }
-// &:has(.order-by) {
-// justify-content: space-between;
-// }
-// .order-by,
-// .order-direction {
-// display: flex;
-// flex-wrap: wrap;
-// gap: .5rem;
-// flex: 1;
-// justify-content: flex-start;
-// .label {
-// width: 100%;
-// }
-//
-// label:has(.label) {
-// padding: 0 .35rem;
-// gap: .5rem;
-// .label {
-// font-size: 1rem;
-// }
-// }
-// }
-//}
-//.filter-group >.label {
-// width: 100%;
-//}
-//
-///**
-//Feed Grid
-// */
-//.feed-grid {
-// display: grid;
-// grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
-// gap: .5rem;
-// margin-bottom: 2rem;
-// padding: 0 4rem;
-// --delay: 0s;
-// --increase: .1s;
-//}
-//.feed-empty-state {
-// grid-column: 1/-1;
-// text-align: center;
-// padding: 2rem;
-// background: var(--base-100);
-// border-radius: var(--radius);
-// margin: 0 auto;
-// max-width: 600px;
-//}
-///**
-//Placeholders
-// */
-//.placeholder {
-// aspect-ratio: 1;
-// background: var(--base);
-// border: 1rem solid var(--base-50);
-// border-radius: 1rem;
-// display: flex;
-// justify-content: center;
-// align-items: center;
-//
-// .icon {
-// --w: 50%;
-// color: var(--base-200);
-// animation: dance 2.5s ease-in-out infinite;
-//
-// }
-//}
-//
-///**
-//Feed Items
-// */
-//.feed-item {
-// position: relative;
-// border-radius: 0.5rem;
-// overflow: hidden;
-// background: var(--base-50);
-// box-shadow: 0 2px 4px rgba(0,0,0,0.1);
-// opacity: 0;
-// transition: opacity var(--trans-base) var(--delay);
-// height: fit-content;
-// padding: 0;
-//
-// img {
-// opacity: .7;
-// filter: grayscale(.5) sepia(.3) blur(7px);
-// }
-// &[data-loaded=true] {
-// img {
-// opacity: 1;
-// filter: none;
-// }
-// }
-//
-// a {
-// &::before,
-// &::after {
-// display: none;
-// }
-// }
-// details a {
-// font-size: clamp(1rem, 0.9306rem + 0.2222vw, 1.125rem);
-// }
-//
-// &[data-loaded] {
-// opacity: 1;
-// + [data-loaded] {
-// --delay: var(--delay) + var(--increase);
-// }
-// }
-//
-// &.highlighted {
-// box-shadow: 0 0 0 4px #FF0080, 0 8px 16px rgba(0, 0, 0, 0.1);
-// animation: highlight-puls 2s ease-in-out;
-// }
-// &[open],
-// &:hover {
-// .handle {
-// background-color: var(--overlay-pink-medium);
-// backdrop-filter: blur(5px);
-// }
-// }
-// summary {
-// width: calc(100% - 1rem);
-// height: 100%;
-// aspect-ratio: 1;
-// .handle {
-// position: absolute;
-// bottom: 0;
-// left: 0;
-// right: 0;
-// background-color: rgba(var(--base-rgb),var(--op-3));
-// backdrop-filter: blur(5px);
-// border-radius: var(--radius);
-// z-index: 1;
-// padding: .25rem .25rem .25rem 1.1rem;
-// }
-// &::after {
-// z-index: 11;
-// position: absolute;
-// bottom: .35rem;
-// right: .7rem;
-// width: 1.5rem;
-// height: 1.5rem;
-// cursor: pointer;
-// }
-// }
-//
-// label {
-// font-weight: normal;
-// text-transform: none;
-// .icon {
-// --w: 1.5em;
-// }
-// }
-//}
-///**
-//Load More Button
-// */
-//.load-more {
-// opacity: 1;
-// margin: 1rem auto;
-// width: 66.666%;
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-// padding: .75rem 1.5rem;
-// background: var(--base);
-// color: var(--contrast);
-// border-radius: 4px;
-// font-size: var(--txt-medium);
-// transition: all var(--trans-base);
-// border: 2px solid transparent;
-// &[hidden] {
-// opacity: 0;
-// transition: all var(--trans-base);
-// }
-// &:hover {
-// background: var(--pink-50);
-// border-color: var(--contrast);
-// color: var(--white);
-// }
-//}
-///**
-//favourite button
-// */
-//button.favourite {
-// position: absolute;
-// top: .5rem;
-// right: .5rem;
-// z-index: 10;
-// background: rgba(var(--base-rgb),var(--op-4));
-// border-radius: 50%;
-// box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw-subtle);
-// border: none;
-// width: 2rem;
-// height: 2rem;
-// display: flex;
-// justify-content: center;
-// align-items: center;
-// backdrop-filter: blur(5px);
-// transition: all var(--trans-base);
-//
-// &:hover {
-// transform: scale(1.1);
-// color: var(--pink-0);
-// background: var(--base);
-// box-shadow: 0 4px 8px rgba(0,0,0,0.15);
-// }
-//
-// &.favourited {
-// animation: pop 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
-// }
-//}
-//
-//
-///** Images **/
-//.feed-image {
-// display: block;
-// aspect-ratio: 1;
-// overflow: hidden;
-// width: 100%;
-// height: 100%;
-//}
-//.feed-images {
-// width: 100%;
-// height: 100%;
-// &.multi {
-// display :grid;
-// grid-template-columns: repeat(3, 1fr);
-// grid-auto-rows: 1fr;
-// gap: 4px;
-//
-// > a {
-// width: 100%;
-// height: 100%;
-// aspect-ratio: 1;
-// }
-// .feed-image {
-// grid-row: span 2;
-// grid-column: span 2;
-// }
-// }
-// img {
-// width: 100%;
-// height: 100%;
-// object-fit: cover;
-// transition: transform var(--trans-t) var(--trans-fn);
-// }
-// a:hover img {
-// transform: scale(1.05);
-// }
-//}
-//
-//.feed-item {
-// &:nth-of-type(4n+2) .multi .feed-image {
-// grid-column: 2 / span 2;
-// grid-row: 1 / span 2;
-// }
-// &:nth-of-type(4n+3) .multi .feed-image {
-// grid-row: 2 / span 2;
-// grid-column: 1 / span 2;
-// }
-// &:nth-of-type(4n+4) .multi .feed-image {
-// grid-column: 2 / span 2;
-// grid-row: 2 / span 2;
-// }
-//}
-//
-///** Item Information **/
-//.item-info {
-// padding: .25rem;
-// border-left: 1px solid var(--base-200);
-// >div + div {
-// margin-top: .5em;
-// position: relative;
-//
-// &::before {
-// content: '';
-// display: block;
-// position: absolute;
-// top: -.3em;
-// left: -.25rem;
-// width: 66.6%;
-// border-bottom: 1px solid var(--base-200);
-// }
-// }
-// h3 {
-// margin: 0 0 .5em 0!important;
-// font-size: 1.1rem;
-// font-family: var(--body);
-// font-weight: var(--fw-b);
-// }
-// span {
-// text-transform: uppercase;
-// display: flex;
-// align-items: center;
-// }
-// .icon {
-// --w: 1.1em;
-// margin-right: .5em;
-// display: inline-block;
-// vertical-align: middle;
-// }
-//}
-//.item-list {
-// ul {
-// margin: 0;
-// padding: .5em 0;
-// display: flex;
-// flex-wrap: wrap;
-// gap: .5rem;
-// li {
-// list-style: none;
-// }
-// }
-// a {
-// background-color: var(--pink-0);
-// border: 1px solid transparent;
-// border-radius: 4px;
-// color: var(--light-0);
-// padding: .25em;
-// line-height: .8;
-// &:visited {
-// background-color: var(--pink-100);
-// color: var(--white);
-// }
-// &:visited:hover,
-// &:visited:focus,
-// &:hover,
-// &:focus {
-// background-color: transparent;
-// border-color: var(--contrast);
-// color: var(--contrast);
-// }
-// }
-//}
-///**
-//Loading
-// */
-//.loading {
-// opacity: .7;
-//}
-//
-//.loading-overlay {
-// position: fixed;
-// top: 0;
-// left: 0;
-// right: 0;
-// bottom: 0;
-// background-color: rgba(var(--base-rgb),var(--op-4));
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// transition: opacity 0.3s ease, visibility 0.3s ease;
-// z-index: 9999;
-// opacity: 0;
-// visibility: hidden;
-//}
-//.loading .loading-overlay {
-// opacity: 1;
-// visibility: visible;
-//
-// &::after {
-// content: '';
-// position: absolute;
-// z-index: -1;
-// inset: 0;
-// background: linear-gradient(
-// 90deg,
-// var(--shimmer)
-// );
-// animation: shimmer 3s ease-in-out infinite;
-// }
-//
-// .wrapper {
-// background-color: rgba(var(--base-rgb),var(--op-6));
-// padding: 2rem;
-// border-radius: var(--radius);
-// text-align: center;
-// max-width: 90%;
-// width: 400px;
-// height: 300px;
-// z-index: 5;
-// display: flex;
-// justify-content: center;
-// align-items: center;
-// position: relative;
-//
-// .spinner {
-// --h: 150px;
-// --w: calc(var(--h) * 2);
-// border-top: 5px solid var(--pink-0);
-// border-radius: 50%;
-// position: absolute;
-// width: var(--w);
-// height: var(--w);
-// top: calc(50% - var(--h));
-// left: calc(50% - var(--h));
-// opacity: .5;
-// z-index: 0;
-// animation: spin 1s var(--trans-t) infinite;
-// }
-// div.icon {
-// height: 50px;
-// width: 50px;
-//
-// .icon {
-// --w: 100%;
-// svg {
-// animation: dance 2s ease-in-out infinite;
-// transition: color 0.3s ease;
-// }
-// }
-// }
-// .status {
-// height: 200px;
-// width: 100%;
-// z-index: 5;
-// display: flex;
-// flex-direction: column;
-// align-items: center;
-//
-// h3 {
-// margin: 1.5rem 0 .25rem!important;
-// color: var(--contrast-200);
-// }
-// .message {
-// margin: 0;
-// max-width: 275px;
-// color: var(--contrast-100);
-// font-size: var(--txt-x-small);
-// animation: flicker 2s infinite;
-// }
-// }
-// }
-//}
-//
-//
-///* Animations */
-//@keyframes highlight {
-// 0%, 100% {
-// box-shadow: none;
-// }
-// 50% {
-// box-shadow: 0 0 0 4px var(--pink-0);
-// }
-//}
-//
-//@keyframes pop {
-// 0% { transform: scale(1); }
-// 50% { transform: scale(1.3); }
-// 75% { transform: scale(0.9); }
-// 100% { transform: scale(1); }
-//}
-//
-//@keyframes bubble {
-// 50% { box-shadow: 19px 0 0 3px, 38px 0 0 7px, 57px 0 0 3px }
-// 100% { box-shadow: 19px 0 0 0, 38px 0 0 3px, 57px 0 0 7px }
-//}
-//@keyframes highlight-pulse {
-// 0%, 100% { box-shadow: 0 0 0 4px #FF0080, 0 8px 16px rgba(0, 0, 0, 0.1); }
-// 50% { box-shadow: 0 0 0 8px #FF0080, 0 12px 24px rgba(0, 0, 0, 0.15); }
-//}
-//
-//@keyframes spin {
-// to { transform: rotate(360deg); }
-//}
-//
-//@keyframes shimmer {
-// 0% { transform: translateX(-100%); }
-// 50%, 100% { transform: translateX(100%); }
-//}
-//@keyframes dance {
-// 0%, 100% { transform: rotate(-5deg) scale(1);}
-// 50% { transform: rotate(5deg) scale(1.1); }
-//}
-//@keyframes flicker {
-// 0% { opacity: 0.6; }
-// 50% { opacity: 1; }
-// 100% { opacity: 0.6; }
-//}
-//
-//
-///**
-//Accessibility
-// */
-//
-///* Keyboard navigable feed items */
-//.feed-item[tabindex="0"] {
-// position: relative;
-//}
-//
-//.feed-item[tabindex="0"]::after {
-// content: '';
-// position: absolute;
-// top: 0;
-// left: 0;
-// right: 0;
-// bottom: 0;
-// pointer-events: none;
-// border: 2px solid transparent;
-// transition: border-color 0.2s ease;
-//}
-//
-//.feed-item[tabindex="0"]:focus::after {
-// border-color: #FF0080;
-//}
-//
-//
-//.feed-block .item {
-// summary {
-// a {
-// background-color: var(--action-0);
-// display: flex;
-// gap: .25rem;
-// flex-wrap: nowrap;
-// aspect-ratio: 1;
-// position: relative;
-// }
-// img {
-// width: 50%;
-// height: 100%;
-// object-fit: cover;
-// }
-//
-// }
-//}
-
-
-.feed-block {
- grid-column: full;
- .filters {
- padding: 1rem 0;
- max-width:var(--wide);
- margin: 0 auto;
-
- .remove-term.remove-term {
- width: max-content;
- height: max-content
- }
- }
- .filter-group {
- position: relative;
- padding: 2rem 0;
- .label {
- position: absolute;
- left: 0;
- }
- > .label {
- top: 0;
- }
- [type=radio] {
- position:absolute;
- left: var(--offScreen);
- }
- button, label {
- position: relative;
- padding: .5rem;
- height: max-content;
- &:hover {
- color: var(--action-contrast);
- }
- }
- button:hover .label,
- :checked + label .label,
- label:hover .label {
- opacity: 1;
- visibility: visible;
- }
- &:has(label:hover) :checked + label .label,
- button .label,
- label .label {
- --height: max-content;
- opacity: 0;
- visibility: hidden;
- bottom: -2rem;
- width: max-content;
- white-space: nowrap;
- font-weight: var(--fw-b);
- }
-
-
- }
- h3 {
- margin: 0 0 .25rem;
- font-size: var(--medium);
- }
-}
-/** PLACEHOLDERS **/
-.placeholder {
- aspect-ratio: 1;
- background: var(--base);
- border: 1rem solid var(--base-50);
- border-radius: 1rem;
- display: flex;
- justify-content: center;
- align-items: center;
-
- i.icon {
- --w: 50%;
- color: var(--base-200);
- animation: dance 2.5s ease-in-out infinite;
- }
-}
-
-.item-grid {
- padding: 0 var(--chip);
- max-width: 100%;
-}
-/** FEED ITEM **/
-.feed.item {
- position: relative;
- border-radius: 0.5rem;
- overflow: hidden;
- background: var(--base-50);
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
- height: fit-content;
- padding: 0;
- details {
- z-index: var(--z-2);
- width: 100%;
- position: relative;
- padding: 0;
- summary {
- position:absolute;
- top: -3rem;
- left:0;
- width: 100%;
- background-color: rgba(var(--base-rgb),var(--op-2));
- backdrop-filter: blur(5px);
- &:hover {
- background-color: rgba(var(--action-rgb),var(--op-45));
- }
- }
- &[open] {
- padding: .25rem .5rem;
- summary .icon {
- opacity: 0;
- }
- }
-
- }
-
- img {
- object-fit: cover;
- width: 100%;
- height: 100%;
- //opacity: .7;
- //filter: grayscale(.5) sepia(.3);
- &:hover {
- opacity: .8;
- }
- }
-
- &[data-timeline] {
- .images {
- aspect-ratio: 3/2;
- padding: 0 0 1rem;
- span {
- width: 50%;
- position: absolute;
- background-color: var(--action-0);
- color: var(--action-contrast);
- padding: .25rem .5rem;
- &:first-of-type {
- bottom: 0;
- right: 50%;
- text-align: right;
- }
- &:last-of-type {
- top: 0;
- left: 50%;
- }
- }
- > a {
- position: relative;
- display: flex;
- flex-wrap: nowrap;
- width: 100%;
- height: 100%;
- }
- }
- img {
- width: 50%;
- object-fit: cover;
- height: 100%;
- &:first-of-type {
- border-right: 1px solid var(--action-0);
- }
- }
- }
-
-
- a {
- &::before,
- &::after {
- display: none;
- }
- }
-
- label {
- font-weight: normal;
- text-transform: none;
- .icon {
- --w: 1.5em;
- }
- }
-}
-
-
-.item-grid:has([data-timeline]) {
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
-}
-
-.items-wrap [type=radio],
-.items-wrap [type=checkbox] {
- position: absolute;
- opacity: 0;
- left: -200vw;
-}
-
-.items-wrap [type=radio] + label,
-.items-wrap [type=checkbox] + label {
- position: relative;
- cursor: pointer;
-}
-
-.items-wrap [type=radio] + label:hover,
-.items-wrap [type=checkbox] + label:hover {
- color: var(--action-0);
-}
-
-.items-wrap [type=radio] + label::before,
-.items-wrap [type=checkbox] + label::before,
-.items-wrap [type=radio] + label::after,
-.items-wrap [type=checkbox] + label::after {
- content: '';
- position: absolute;
- top: 50%;
-}
-
-.items-wrap [type=radio] + label::after,
-.items-wrap [type=checkbox] + label::after {
- left: 5px;
- transform: translateY(-70%) rotate(45deg);
- width: 5px;
- height: 10px;
- border: solid var(--light-0);
- border-width: 0 2px 2px 0;
- display: none;
-}
-
-.items-wrap [type=radio] + label::before,
-.items-wrap [type=checkbox] + label::before {
- left: 0;
- transform: translateY(-50%);
- width: 1rem;
- height: 1rem;
- border: 2px solid var(--contrast-200);
- background-color: var(--base);
- border-radius: var(--radius);
-}
-
-.items-wrap [type=radio]:hover + label::before,
-.items-wrap [type=checkbox]:hover + label::before {
- border-color: var(--action-200);
-}
-
-.items-wrap [type=radio]:checked + label::before,
-.items-wrap [type=checkbox]:checked + label::before{
- background-color: var(--action-0);
- border-color: var(--action-100);
-}
-
-.items-wrap [type=radio]:checked + label::before {
- border-radius: 50%;
-}
-
-.items-wrap [type=checkbox]:checked + label::after {
- display: block;
- left: 5px;
- top: 50%;
- transform: translateY(-70%) rotate(45deg);
- width: .35rem;
- height: .66rem;
- border: solid var(--light-0);
- border-width: 0 2px 2px 0;
-}
-
-.items-wrap :disabled + label {
- cursor: not-allowed;
- background-color: var(--base-50);
- color: var(--base-200);
- border-color: var(--base-200);
-}
-
-.items-wrap :disabled + label:hover {
- background-color: var(--base-50);
- color: var(--base-200);
- border-color: var(--base-200);
-}
-
-.items-wrap :disabled + label::before {
- border-color: var(--base-200);
-}
-
-#jvb-selector .items-wrap [type=radio] + label,
-#jvb-selector .items-wrap [type=checkbox] + label{
- flex: 1;
- padding-left: 2rem!important;
- transform-origin: top center;
- will-change: transform;
-}
-
-.feed-block + footer {
- grid-column: full;
- padding: 0!important;
- margin: 0;
- background-color: var(--base-50);
- z-index: 0;
- display: flex;
- justify-content: flex-end;
- button {
- width: max-content;
- margin-left: auto;
- padding: .35rem .25rem;
- --w: 1.3em!important;
- flex-wrap: nowrap;
- justify-content:flex-start;
- transition: var(--trans-size);
- font-size: var(--txt-x-small);
- min-height: 0;
- span {
- display: none;
- white-space: nowrap;
- }
- &:focus span,
- &:hover span {
- display: block;
- }
- }
-}
diff --git a/src/feed/view.js b/src/feed/view.js
deleted file mode 100644
index 87e79b9..0000000
--- a/src/feed/view.js
+++ /dev/null
@@ -1,747 +0,0 @@
-class FeedBlock {
- constructor() {
- this.container = document.querySelector('section.feed-block');
- if(!this.container) return;
-
- this.a11y = window.jvbA11y;
- this.error = window.jvbError;
- this.cache = new window.jvbCache('feed');
- this.templates = window.jvbTemplates;
-
- this.config = {
- source: '',
- context: '',
- highlight: null,
- gallery: false,
- view: this.cache.get('feedView') || 'grid',
- ... this.container.dataset
- };
-
- this.init();
- }
- init() {
- this.initElements();
- this.defineTemplates();
- this.initListeners();
- this.initFilters();
-
- if ('requestIdleCallback' in window) {
- requestIdleCallback(() => {
- this.initStore();
- this.initTaxonomies();
-
- this.processCachedFilters();
- this.processURLFilters();
- this.updateFilterUI();
- this.initGallery();
- }, { timeout: 2000 });
- } else {
- setTimeout(() => {
- this.initStore();
- this.initTaxonomies();
-
- this.processCachedFilters();
- this.processURLFilters();
- this.updateFilterUI();
- this.initGallery();
- }, 100);
- }
- }
-
- initElements() {
- this.selectors = {
- filterTrigger: '[data-filter]',
- filters: {
- actions: '.filter-actions .toggle-text',
- container: '.filters',
- content: '[data-filter="content"]',
- orderby: '[data-filter="orderby"]',
- order: '[data-filter="order"]',
- match: '[data-filter="match"]',
- favourites: '[data-filter="favourites"]',
- taxonomy: '[data-filter^="taxonomy"]',
- },
- grid: '.item-grid',
- selected: '.selected-items',
- buttons: {
- loadMore: 'button.load-more',
- remove: '.remove-term',
- clearFilters: 'button.clear-filters',
- refresh: 'button[data-action="refresh"]'
- }
- };
- this.ui = window.uiFromSelectors(this.selectors, this.container);
- this.ui.buttons.refresh = document.querySelector(this.selectors.buttons.refresh);
-
- //Add content and taxonomies
- this.ui.content = this.ui.filters.container.querySelectorAll('[name="content"]');
- if (this.ui.content.length === 0) this.ui.content = false;
- this.ui.taxonomies = this.ui.filters.container.querySelectorAll('[data-taxonomy]');
- if (this.ui.taxonomies.length === 0) this.ui.taxonomies = false;
- this.ui.orderbyWrap = this.ui.filters.container.querySelector('[data-for-order]');
- if (this.ui.orderbyWrap.length === 0) this.ui.orderbyWrap = false;
- this.ui.order = this.ui.filters.container.querySelectorAll('[data-filter="order"]');
- if (this.ui.order.length === 0) this.ui.order = false;
- this.ui.orderby = this.ui.filters.container.querySelectorAll('[data-filter="orderby"]');
- if (this.ui.orderby.length === 0) this.ui.orderby = false;
-
- this.orderbyFilters = (this.ui.orderby)
- ? Array.from(this.ui.orderby).map(o => o.value)
- : [];
-
- this.contentTypes = (this.ui.content)
- ? Array.from(this.ui.content).map(c => c.value)
- : [this.container.dataset.content];
- this.taxonomies = (this.ui.taxonomies?.length > 0)
- ? Array.from(this.ui.taxonomies).map(t => t.dataset.taxonomy)
- : [];
- }
-
- initListeners() {
- this.popStateHandler = this.handlePopState.bind(this);
- this.clickHandler = this.handleClick.bind(this);
- this.changeHandler = this.handleChange.bind(this);
-
- window.addEventListener('popstate', this.popStateHandler);
- document.addEventListener('click', this.clickHandler);
- document.addEventListener('change', this.changeHandler);
- }
-
- initFilters() {
- this.allowedFilters = ['content', 'order', 'orderby', 'favourites', 'match'];
- let defaults = {
- content: this.contentTypes[0],
- orderby: 'date',
- order: 'desc',
- page: 1,
- };
- if (this.config.context) defaults.context = this.config.context;
- if (this.config.source) defaults.source = this.config.source;
-
- this.filters = defaults;
- this.defaults = {...defaults};
- }
- updateFilterUI() {
- if (this.ui.filters.container) {
- //Get cached inputs
- let groups = [
- this.ui.content,
- this.ui.orderby,
- this.ui.order
- ];
-
- groups.forEach(group => {
- if(group) {
- for (let input of group) {
- let [filter, value] = [input.dataset.filter, input.value];
- if (!Object.hasOwn(this.store.filters, filter)) break;
- let doit = this.store.filters[filter] === value;
- if (doit) {
- input.checked = doit;
- break;
- }
- }
- }
- });
-
- if (Object.hasOwn(this.store.filters, 'taxonomy')) {
- for (let [taxonomy, terms] of Object.entries(this.store.filters.taxonomy)) {
- terms.forEach(termId => {
- termId = parseInt(termId);
- const term = this.selector.store.get(termId);
- if (term) {
- this.createTermElement(termId);
- }
- });
- }
- }
- }
- }
-
- handlePopState(e) {
- if (e.state?.filters) {
- if (this.processURLFilters()) {
- this.store.setFilters(this.filters);
- this.a11y.announce('Feed filters updated from browser history');
- }
- }
- }
-
- handleClick(e) {
- if (window.targetCheck(e, this.selectors.buttons.loadMore)) {
- this.nextPage();
- } else if (window.targetCheck(e, this.selectors.buttons.clearFilters)) {
- this.clearFilters();
- }
- let remove = window.targetCheck(e, this.selectors.buttons.remove);
- if (remove) {
- this.removeSelectedTerm(remove);
- }
-
- let refresh = window.targetCheck(e, this.selectors.buttons.refresh);
- if (refresh) {
- this.store.clearCache();
- this.store.fetch();
- }
-
- let orderbyButton = window.targetCheck(e, '[data-filter="orderby"]');
- if (orderbyButton && orderbyButton.value === 'random' && orderbyButton.checked) {
- // Already selected random, just re-render to trigger new shuffle
- this.renderItems();
- }
- }
-
- nextPage() {
- const nextPage = (this.store.filters.page || 1) + 1;
- const maxPage = this.store.lastResponse?.pages || nextPage;
- this.store.setFilters({ page: Math.min(nextPage, maxPage) });
- }
-
- handleChange(e) {
- const target = e.target;
- if (Object.hasOwn(target.dataset, 'filter')) {
- if (this.allowedFilters.includes(target.dataset.filter)) {
- let filters = {};
- filters[target.dataset.filter] = target.value;
- this.resetFilters(filters);
- }
- switch (target.dataset.filter) {
- case 'content':
- this.updateContentFor(target.value);
- break;
- case 'orderby':
- this.updateOrderOptions(target.value);
- break;
- }
- }
- }
-
- clearFilters() {
- this.taxFilters = {};
- window.removeChildren(this.ui.selected);
-
- this.taxonomies.forEach(tax => {
- let fieldId = this.getFieldId(tax);
- this.selector.selectedTerms.get(fieldId)?.clear();
- });
-
- this.store.setFilters({
- ...this.defaults,
- taxonomy: null
- });
-
- this.updateURL();
- this.saveToCacheFilters();
- }
-
- resetFilters(filters) {
- filters = {
- ...this.store.filters,
- page: 1,
- ... filters
- }
- this.store.setFilters(filters);
-
- this.updateURL();
- this.saveToCacheFilters();
- }
-
- getFieldId(taxonomy) {
- return this.selector.getFieldId(Array.from(this.ui.taxonomies).filter(tax => tax.dataset.taxonomy === taxonomy)[0]??null);
- }
- removeSelectedTerm(button) {
- const termId = parseInt(button.dataset.id);
- const taxonomy = button.dataset.taxonomy;
-
- if (Object.hasOwn(this.taxFilters, taxonomy)){
- this.taxFilters[taxonomy] = this.taxFilters[taxonomy]
- .filter(id => id !== termId);
- if (this.taxFilters[taxonomy].length === 0) {
- delete this.taxFilters[taxonomy];
- }
- }
- button.remove();
-
- // Find the fieldId for this taxonomy
- const field = this.getFieldId(taxonomy);
- if (field) {
- this.selector.activeField = field;
- // Notify selector to remove from its selectedTerms
- this.selector.removeSelected(termId, field);
- }
-
- this.resetFilters({
- taxonomy: Object.keys(this.taxFilters).length > 0
- ? this.taxFilters
- : null
- });
- }
-
- updateContentFor(content) {
- let checkIt = [
- this.ui.taxonomies,
- this.ui.orderby
- ];
- checkIt.forEach(check => {
- if (!check) return;
- check.forEach(button => {
- const forTypes = button.dataset.for?.split(',')??[];
- button.hidden = forTypes.length > 0 && !forTypes.includes(content);
- if (button.hidden && button.checked) {
- button.checked = false;
- }
- });
- });
- }
- updateOrderOptions(order) {
- if (this.ui.orderbyWrap) {
- let options = this.ui.orderbyWrap.dataset.forOrder.split(',')??[];
- this.ui.orderbyWrap.hidden = !options.includes(order);
- }
- }
-
- updateFilterControls() {
- const isHidden = Object.keys(this.taxFilters).length === 0;
- if (this.ui.buttons.clearFilters) {
- this.ui.buttons.clearFilters.hidden = isHidden;
- }
- if (this.ui.filters.actions) {
- this.ui.filters.actions.hidden = isHidden;
- }
- }
-
- async initTaxonomies() {
- this.taxFilters = {};
- this.selector = window.jvbSelector;
- // this.selector.scanExistingFields(this.ui.filters.container);
- // this.taxonomies.map(tax => this.selector.batchFetch.add(tax));
- // this.selector.batchFetchTaxonomies();
- this.selector.subscribe((event, data) => {
- switch (event) {
- case 'selected-terms':
-
- this.handleTaxonomyChange(data);
- break;
-
- }
- });
- }
- handleTaxonomyChange(data) {
- const {terms, taxonomy } = data;
- if (terms.size === 0) return;
- this.taxFilters[taxonomy] = Array.from(terms);
- this.resetFilters({ taxonomy: this.taxFilters });
-
- terms.forEach(t => {
- this.createTermElement(t);
- });
- this.updateFilterControls();
- }
- getTaxonomyIcon(taxonomy) {
- let iconButton = Array.from(this.ui.taxonomies)
- .find(t => t.dataset.taxonomy === taxonomy);
- return iconButton?.dataset.icon.trim() || 'tag';
- }
- createTermElement(termId) {
- const term = this.selector.store.get(termId);
- if (!term) return;
- if (this.ui.selected.querySelector(`[data-id="${termId}"]`)) return;
-
- term.icon = this.getTaxonomyIcon(term.taxonomy);
- this.ui.selected.append(this.templates.create('feedTerm', term));
- }
-
- processCachedFilters() {
- Object.keys(this.filters).forEach(filter => {
- let cached = this.cache.get(`${this.config.source}_${this.config.context}_${filter}`);
- if (cached && cached !== this.filters[filter]) {
- this.filters[filter] = cached;
- }
- });
- }
-
- processURLFilters() {
- if (!this.isFirstPage()) return false;
- const params = new URLSearchParams(window.location.search);
- if (!params.toString()) return false;
- let shouldUpdate = false;
- this.allowedFilters.forEach(filter => {
- let value = params.get(`f_${filter}`);
- if (value) {
- shouldUpdate = true;
- this.filters[filter] = value;
- }
- });
-
- let hasTax = false;
- params.forEach((value, key) => {
- if (key.startsWith('f_tax_')) {
- hasTax = true;
- shouldUpdate = true;
- const taxonomy = key.replace('f_tax_','');
- this.taxFilters[taxonomy] = value.split(',').map(Number);
- }
- });
- if (shouldUpdate) {
- if (hasTax) {
- this.filters.taxonomy = this.taxFilters;
- }
- this.resetFilters(this.filters);
- }
- return true;
- }
-
- updateURL() {
- const params = new URLSearchParams();
- this.allowedFilters.forEach(key => {
- if (Object.hasOwn(this.store.filters, key) && this.store.filters[key] !== this.defaults[key]) {
- params.set(`f_${key}`, this.store.filters[key]);
- }
- });
-
- for (let [tax, terms] of Object.entries(this.taxFilters)) {
- if (terms.length > 0) {
- params.set(`f_tax_${tax}`, terms.join(','));
- }
- }
-
- const newURL = `${window.location.pathname}${params.toString() ? '?' + params.toString() : ''}`;
- const currentURL = window.location.pathname + window.location.search; // Change this line
-
- if (newURL !== currentURL) {
- window.history.pushState({filters:this.store.filters}, '', newURL);
- }
- }
-
- saveToCacheFilters() {
- Object.keys(this.store.filters).forEach(filter => {
- const cacheKey = `${this.config.source}_${this.config.context}_${filter}`;
-
- if (this.store.filters[filter] !== this.defaults[filter]) {
- this.cache.set(cacheKey, this.store.filters[filter]);
- } else {
- this.cache.remove(cacheKey);
- }
- });
-
- const taxCacheKey = `${this.config.source}_${this.config.context}_taxonomy`;
- if (Object.keys(this.taxFilters).length > 0) {
- this.cache.set(taxCacheKey, this.taxFilters);
- } else {
- this.cache.remove(taxCacheKey);
- }
- }
-
- initGallery() {
- this.gallery = (this.config.gallery) ? window.jvbGallery : false;
- if (this.gallery) {
- this.gallery.subscribe((event, data) => {
- if (event === 'load-more' && this.store.lastResponse?.has_more) {
- this.nextPage();
- }
- });
- }
- }
- initStore() {
- let extraOrderby = this.orderbyFilters.filter(v => !['date','modified','title','random'].includes(v));
- let extraIndexes = [];
- extraOrderby.forEach(orderby =>{
- extraIndexes.push({name:orderby, keyPath: orderby});
- });
- const store = window.jvbStore.register(
- 'feed',
- {
- storeName: 'feed',
- endpoint: 'feed',
- keyPath: 'id',
- indexes: [
- { name: 'content', keyPath: 'content'},
- { name: 'taxonomy', keyPath: 'taxonomy'},
- { name: 'user', keyPath: 'user'},
- { name: 'date', keyPath: 'date'},
- { name: 'modified', keyPath: 'modified'},
- { name: 'title', keyPath: 'title'},
- ... extraIndexes
- ],
- filters: this.filters,
- TTL: 6 * 60 * 60 * 1000, //6 hours
- showLoading: true,
- required: 'content',
- }
- );
-
- this.store = store.feed;
-
- this.store.subscribe((event, data) => {
- switch (event) {
- case 'data-loaded':
- this.renderItems(data.items);
- this.ui.buttons.loadMore.hidden = true;
- if (this.store.lastResponse && this.store.lastResponse?.has_more) {
- this.ui.buttons.loadMore.hidden = !this.store.lastResponse?.has_more??true;
- }
- break;
- }
- });
- }
-
- isFirstPage() {
- return this.store.filters.page === 1;
- }
-
- renderItems(items = null) {
- items = items??this.store.getFiltered();
- if (this.isFirstPage()) {
- window.removeChildren(this.ui.grid);
- }
- if (items.length === 0) {
- this.showEmptyState();
- this.a11y.announceItems(0, this.isFirstPage());
- } else {
- window.chunkIt(
- items,
- (item) => this.createItemElement(item),
- (fragment) => {
- this.removePlaceholders();
- this.ui.grid.append(fragment);
- if (this.config.gallery) this.gallery.buildGalleryItems('.item img');
- this.a11y.makeNavigable(this.ui.grid.querySelectorAll('.item:not([data-keyboard-nav])'));
- this.a11y.announceItems(items.length, !this.isFirstPage(), this.store.lastResponse?.has_more??false);
- },
- 5
- ).then(()=>{});
- }
-
- this.updateFilterControls();
- }
-
- showEmptyState() {
- window.removeChildren(this.ui.grid);
- this.ui.grid.append(this.templates.create('emptyState'));
- }
-
- createItemElement(item) {
- if (typeof item !== 'object') {
- item = this.store.get(item);
- if (!item) return;
- }
- return this.templates.create(`feedItem${window.uppercaseFirst(item.content)}`, item);
- }
- splitIDs(value) {
- return String(value).split(',').map((value) => parseInt(value.trim())).filter(value=>value);
- }
-
- isImageField(item, value) {
- if (!Object.hasOwn(item, 'images') || Object.keys(item.images).length === 0) {
- return false;
- }
- let values = this.splitIDs(value);
-
- return values.some(v =>
- Object.keys(item.images).map(k => parseInt(k)).includes(parseInt(v))
- );
- }
- formatImageFields(element, value, item) {
- let values = this.splitIDs(value); // Convert string to array first
- if (values.length === 0) return;
-
- if (values.length > 1) {
- let image = element.querySelector('img');
- if (!image) return;
- values.forEach(imgID => {
- let img = image.cloneNode(true);
- this.formatImageField(img, imgID, item);
- element.append(img);
- });
- image.remove();
- } else {
- if (element.tagName !== 'IMG') {
- element = element.querySelector('img');
- if (!element) return;
- }
- this.formatImageField(element, values[0], item);
- }
- }
- formatImageField(element, value, item) {
- let imgData = item.images[value]??false;
- if (!imgData) return;
- [
- element.src,
- element.srcset,
- element.alt
- ] = [
- imgData.tiny,
- `${imgData.tiny} 50w, ${imgData.small} 300w, ${imgData.medium} 1024w`,
- imgData['image-alt-text']
- ]
- }
- isTaxonomyField(item, field) {
- if (!Object.hasOwn(item, 'taxonomies') || Object.keys(item.taxonomies).length === 0) {
- return false;
- }
-
- return Object.keys(item.taxonomies).includes(field);
- }
- formatTaxonomyField(element, item, field, value) {
- if (element.tagName !== 'UL' || !element.querySelector('li')) return;
- let values = this.splitIDs(value);
- if (values.length === 0) {
- element.remove();
- }
- let listItem = element.querySelector('li');
- for (let termID of values) {
- let term = item.taxonomies[field][termID]??false;
- if (!term) continue;
- let termItem = listItem.cloneNode(true);
- let link = termItem.querySelector('a');
- if (!link) continue;
-
- let title = window.decodeHTMLEntities(term.title);
-
- [
- link.href,
- link.title,
- link.textContent
- ] = [
- term.url,
- `See more ${title}`,
- title
- ];
- element.append(termItem);
- }
- listItem.remove();
- }
- isTimeField(el) {
- return el.tagName === 'TIME' || el.querySelector('time') !== null;
- }
- formatTimeField(element, value) {
- if (element.tagName !== 'TIME') {
- element = element.querySelector('time');
- if (!element) return;
- }
- element.setAttribute('datetime', value);
- element.textContent = window.formatTimeAgo(value, 'F Y');
- }
- formatField(element, value) {
- element.textContent = window.decodeHTMLEntities(value);
- }
-
- addTimelineElements(item, template) {
- let [
- afterEl,
- number,
- started,
- last
- ] = [
- template.querySelector('span.after-text'),
- template.querySelector('[data-field="number"] b'),
- template.querySelector('[data-field="started"] time'),
- template.querySelector('[data-field="updated"] time')
- ];
-
- if (afterEl) {
- afterEl.textContent = `After ${item.number - 1} Tx`;
- }
- if (number) {
- number.textContent = item.number - 1;
- }
- if (started) {
- this.formatTimeField(started, item.fields.timeline[0]['post_date']);
- }
- if (last) {
- this.formatTimeField(last, item.fields.timeline[item.fields.timeline.length - 1]['post_date']);
- }
- }
-
- removePlaceholders() {
- const placeholders = this.ui.grid.querySelectorAll('.placeholder');
- if (placeholders.length > 0) {
- placeholders.forEach(p => p.remove());
- }
- }
-
- defineTemplates() {
- const T = this.templates;
- const f = this;
-
- T.define('feedTerm', {
- refs: {
- icon: '.icon',
- span: 'span'
- },
- setup({el, refs, manyRefs, data}) {
- el.dataset.id = data.id;
- el.dataset.taxonomy = data.taxonomy;
- if (refs.icon) refs.icon.className = `icon icon=${data.icon}`;
- if (refs.span) refs.span.textContent = window.decodeHTMLEntities(data.name);
- }
- });
- T.define('emptyState');
-
- this.contentTypes.forEach(content => {
- T.define(`feedItem${window.uppercaseFirst(content)}`, {
- refs: {
- link: 'a',
- },
- manyRefs: {
- fields: '[data-field]',
- },
- setup({el, refs, manyRefs, data}) {
- const isTimeline = Object.hasOwn(el.dataset, 'timeline');
- if (manyRefs.fields) {
- for (let field of manyRefs.fields) {
- if (isTimeline && ['timeline','number'].includes(field.dataset.field)) continue;
-
- const value = Object.hasOwn(data.fields, field.dataset.field)? data.fields[field.dataset.field] : false;
- if (!value) {
- field.remove();
- continue;
- }
- if (f.isImageField(data, value)) {
- f.formatImageField(field, value, data);
- } else if (f.isTaxonomyField(data, field.dataset.field)) {
- f.formatTaxonomyField(field, data, field.dataset.field, value);
- } else if (f.isTimeField(field)) {
- f.formatTimeField(field, value);
- } else {
- f.formatField(field, value);
- }
- }
- if (refs.link && data.url !== '') {
- refs.link.href = data.url;
- refs.link.title = `View ${data.fields['post_title']??'Item'}`;
- }
- if (isTimeline ) f.addTimelineElements(data, el);
- }
- }
- })
- });
- }
-
- // addPlaceholders() {
- // let total = this.contentTypes.length;
- // const fragment = document.createDocumentFragment();
- // for (let i = 0; i < 12; i++) {
- // let template = window.getTemplate('placeholderTemplate');
- //
- // let rand = Math.floor(Math.random() * total);
- // let icon;
- // if (this.ui.content && this.ui.content.length > 0) {
- // icon = this.ui.content.filter((content) => { return content.value === this.contentTypes[rand]}).querySelector('.icon').cloneNode(true);
- // } else {
- // icon = window.getIcon(this.container.dataset.icon);
- // }
- // template.append(icon);
- // fragment.append(template);
- // }
- // this.ui.grid.append(fragment);
- // }
-}
-
-document.addEventListener('DOMContentLoaded', async function() {
- window.auth.subscribe(event => {
- if (event === 'auth-loaded') {
- window.feedBlock = new FeedBlock();
- }
- });
-});
diff --git a/src/feed/viewOld.js b/src/feed/viewOld.js
deleted file mode 100644
index c65be60..0000000
--- a/src/feed/viewOld.js
+++ /dev/null
@@ -1,770 +0,0 @@
-class FeedBlockOld {
- constructor() {
- this.container = document.querySelector('section.feed-block');
- if (!this.container) {
- return;
- }
-
- this.a11y = window.jvbA11y;
- this.cache = new window.jvbCache('feed');
- this.error = window.jvbError;
-
- this.config = {
- source: '',
- context: '',
- highlight: null,
- gallery: false,
- view: this.cache.get('feedView') || 'grid',
- ... this.container.dataset
- };
- this.initElements();
- this.initFilters();
-
-
- this.loadWhenAble();
- }
-
- loadWhenAble() {
- if ('requestIdleCallback' in window) {
- requestIdleCallback(() => {
- this.initTaxonomies();
- this.initStore();
- this.initListeners();
- this.initGallery();
- }, { timeout: 2000 });
- } else {
- setTimeout(() => {
- this.initTaxonomies();
- this.initStore();
- this.initListeners();
- this.initGallery();
- }, 100);
- }
- }
-
- initElements() {
- this.currentTaxonomies = new Set(); // Allowed Taxonomies, grabbed from active buttons
- this.taxonomyFilters = {};
- this.elements = {
- filterTrigger: '[data-filter]',
- filters: {
- content: '[data-filter="content"]',
- orderby: '[data-filter="orderby"]',
- order: '[data-filter="order"]',
- match: '[data-filter="match"]',
- favourites: '[data-filter="favourites"]',
- taxonomy: '[data-filter^="taxonomy"]'
- },
- selectedTax: '.selected-items',
- clearFilter: 'button.clear-filters',
- loadMore: 'button.load-more',
- filterContainer: '.filters',
- grid: '.item-grid',
- };
- this.ui = window.uiFromSelectors(this.elements);
-
-
- this.ui.content = this.ui.filterContainer.querySelectorAll('[name="content"]')??false;
- this.ui.taxonomies = this.ui.filterContainer.querySelectorAll('[data-taxonomy]');
- if (this.ui.content && this.ui.content.length > 0) {
- this.contentTypes = Array.from(
- this.ui.content
- ).map(content => content.value);
- } else {
- this.contentTypes = [this.container.dataset['content']];
- }
-
- if (this.ui.taxonomies.length>0) {
- this.taxonomies = Array.from(
- this.ui.taxonomies,
- ).map(content => content.dataset.taxonomy);
- } else {
- this.taxonomies = [];
- }
-
-
- }
-
- async initTaxonomies() {
- this.selector = window.jvbSelector;
- const buttons = document.querySelectorAll('[data-filter="taxonomy"]');
-
- this.selector.isInitializing = true;
- buttons.forEach((button) => {
- const taxonomy = button.dataset.taxonomy;
- this.currentTaxonomies.add(taxonomy);
-
- this.selector.registerFilterButton(button, {
- button: button,
- buttonSelector: '[data-filter="taxonomy"]',
- selected: this.ui.selectedTax
- });
-
- // Add preload listeners
- this.addTaxonomyPreloadListeners(button, taxonomy);
- });
-
- this.selector.isInitializing = false;
-
- this.selector.subscribe((event, data) => {
- if (event === 'selected-terms') this.handleTaxonomyChange(data);
- });
- }
-
- addTaxonomyPreloadListeners(button, taxonomy) {
- const preload = () => {
- this.selector.preloadTaxonomy(taxonomy);
- };
-
- // Desktop hover
- button.addEventListener('mouseenter', preload, { once: true });
-
- // Touch/keyboard (fires before click)
- button.addEventListener('pointerdown', preload, { once: true });
-
- // Keyboard focus
- button.addEventListener('focus', preload, { once: true });
- }
-
- handleTaxonomyChange(data) {
- const { terms, taxonomy } = data;
-
- // Update only the current taxonomy's terms
- if (terms.size > 0) {
- this.taxonomyFilters[taxonomy] = Array.from(terms.keys());
- } else {
- // Remove taxonomy if no terms selected
- delete this.taxonomyFilters[taxonomy];
- }
-
- // Build filters object with all taxonomies
- let filters = {
- page: 1
- };
-
- // Add taxonomy filters if any exist
- if (Object.keys(this.taxonomyFilters).length > 0) {
- filters.taxonomy = this.taxonomyFilters;
- }
-
- this.updateFilter(filters);
- }
-
- clearAllTaxonomies() {
- this.taxonomyFilters = {};
- window.removeChildren(this.ui.selectedTax);
-
- this.updateFilter({
- taxonomy: null,
- page: 1
- });
- }
-
- initFilters() {
- //defaults
- this.filters = {
- content: this.contentTypes[0],
- orderby: 'date',
- order: 'desc',
- page: 1
- };
- if (this.config.context) this.filters.context = this.config.context;
- if (this.config.source) this.filters.source = this.config.source;
-
- //check the cache
- this.processCachedFilters();
- //check url
- this.processURLFilters();
-
- // Set initial UI state
- this.syncUIToFilters();
- }
- syncUIToFilters() {
- if (this.ui.filterContainer) {
- // Check radio buttons
- Object.entries(this.filters).forEach(([key, value]) => {
- const input = this.ui.filterContainer.querySelector(`[data-filter="${key}"][value="${value}"]`);
- if (input) {
- input.checked = true;
- }
- });
- }
-
- // Update content-specific visibility
- this.updateContentFor(this.filters.content);
- }
- nextPage() {
- this.store.setFilter('page', this.store.filters.page++);
- }
-
- initStore() {
- const store = window.jvbStore.register(
- 'feed',
- {
- storeName: 'feed',
- endpoint: 'feed',
- keyPath: 'id',
- indexes: [
- { name: 'content', keyPath: 'content'},
- { name: 'taxonomy', keyPath: 'taxonomy'},
- { name: 'user', keyPath: 'user'},
- { name: 'date', keyPath: 'modified'},
- { name: 'title', keyPath: 'title'}
- ],
- filters: this.filters,
- TTL: 6 * 60 * 60 * 1000,
- showLoading: true,
- required: 'content',
- delayFetch: true
- }
- );
- this.store = store.feed;
-
- this.store.subscribe((event, data) => {
- switch (event) {
- case 'data-loaded':
- this.renderItems();
- this.ui.loadMore.hidden = true;
- if (this.store.lastResponse && this.store.lastResponse['has_more']) {
- this.ui.loadMore.hidden = !this.store.lastResponse['has_more'];
- }
- break;
- }
- });
- }
-
- initGallery() {
- this.gallery = (this.config.gallery) ? window.jvbGallery : false;
- if (this.gallery) {
- this.gallery.subscribe((event, data) => {
- if (event === 'load-more' && this.store.lastResponse) {
- if (this.store.lastResponse['has_more']) {
- this.nextPage();
- }
- }
- });
- }
- }
-
- processCachedFilters() {
- Object.keys(this.filters).forEach(filter => {
- let cached = this.cache.get(`${this.config.source}_${this.config.context}_${filter}`);
- if (cached && cached !== this.filters[filter]){
- this.filters[filter] = cached;
- }
- });
- }
-
- processURLFilters() {
- if (this.filters.page > 1) {
- return false;
- }
- const params = new URLSearchParams(window.location.search);
-
- if (!params.toString()) {
- return false;
- }
- let filters = ['content', 'order', 'orderby', 'favourites', 'match'];
- filters.forEach(filter => {
- let value = params.get(`f_${filter}`);
- if (value) {
- this.filters[filter] = value;
- let input = this.ui.filters[filter];
- if (input) {
- input.checked = true;
- }
- }
- });
-
- let hasTaxonomy = false;
- // Load taxonomy filters from URL
- params.forEach((value, key) => {
- if (key.startsWith('f_tax_')) {
- hasTaxonomy = true;
- const taxonomy = key.replace('f_tax_', '');
- if (!this.taxonomyFilters[taxonomy]) {
- this.taxonomyFilters[taxonomy] = [];
- }
- this.taxonomyFilters[taxonomy] = value.split(',').map(Number);
- }
- });
- if (this.ui.filterContainer && hasTaxonomy) {
- for (let [tax, ids] in Object.entries(this.taxonomyFilters)) {
- let button = this.ui.filterContainer.querySelector(`[data-taxonomy="${tax}"]`);
- if (button) {
- if (button.dataset.fieldId) {
- let field = this.selector.get(button.dataset.fieldId);
- field.selectedTerms = new Set(ids);
- this.selector.initFieldDisplay(button.dataset.fieldId);
- } else {
- this.selector.registerField(button, {
- button: button,
- buttonSelector: '[data-filter="taxonomy"]',
- selected: this.ui.selectedTax,
- selectedItems: ids
- });
- }
- }
- }
- }
- return true;
- }
-
- /**
- * Update URL with current filters (for sharing/bookmarking)
- */
- updateURL() {
- const params = new URLSearchParams();
-
- // Add simple filters
- ['content', 'order', 'orderby', 'match'].forEach(key => {
- if (this.filters[key]) {
- params.set(`f_${key}`, this.filters[key]);
- }
- });
-
- // Add taxonomy filters
- Object.entries(this.taxonomyFilters).forEach(([taxonomy, terms]) => {
- if (terms.length > 0) {
- params.set(`f_tax_${taxonomy}`, terms.join(','));
- }
- });
-
- // Update URL without reload
- const newURL = `${window.location.pathname}${params.toString() ? '?' + params.toString() : ''}`;
- window.history.pushState({ filters: this.filters }, '', newURL);
- }
-
- renderItems() {
- let items = this.store.getFiltered();
- if (this.store.filters['page'] === 1) {
- window.removeChildren(this.ui.grid);
- }
-
- if (items.length === 0) {
- this.a11y.announceItems(0, this.store.filters['page'] > 0);
- return;
- }
-
- const fragment = document.createDocumentFragment();
- const batchSize = 10;
-
- const processBatch = (startIndex) => {
- const endIndex = Math.min(startIndex + batchSize, items.length);
-
- for (let i = startIndex; i < endIndex; i++) {
- const item = items[i];
- const element = this.createItemElement(item);
-
- fragment.appendChild(element);
- }
-
- if (endIndex < items.length) {
- requestAnimationFrame(() => processBatch(endIndex));
- } else {
- this.removePlaceholders();
- this.ui.grid.append(fragment);
-
- if (this.config.gallery) {
- this.gallery.updateGalleryItems(this.gallery.getGalleryItems());
- }
-
- this.a11y.makeNavigable(this.ui.grid.querySelectorAll('.item:not([data-keyboard-nav])'));
- this.a11y.announceItems(items.length, this.store.filters['page'] > 1, this.store.hasMore);
- }
- };
-
- if (items.length > 0) {
- processBatch(0);
- } else {
- this.a11y.announceItems(0, this.store.filters['page'] >1, false);
- }
-
- if (this.ui.filters.match) {
- this.ui.filters.match.hidden = Object.keys(this.taxonomyFilters).length === 0;
- }
- if (this.ui.clearFilter) {
- this.ui.clearFilter.hidden = Object.keys(this.taxonomyFilters).length === 0;
- }
- }
-
- /**
- *
- * @param {object} item
- */
- createItemElement(item) {
- let template = window.getTemplate(`feedItem${window.uppercaseFirst(item.content)}`);
-
- const isTimeline = Object.hasOwn(template.dataset, 'timeline');
-
- // Format fields using helpers
- for (let [fieldName, value] of Object.entries(item.fields)) {
- if (isTimeline && ['timeline', 'number'].includes(fieldName)) continue;
- let el = template.querySelector(`[data-field="${fieldName}"]`);
- if (!el) continue;
-
- if (value === '') {
- el.remove();
- continue;
- }
-
- if (this.isImageField(item, value)) {
- this.formatImageFields(el, value, item);
- } else if (this.isTaxonomyField(item, fieldName)) {
- this.formatTaxonomyField(el, item, fieldName, value);
- } else if (this.isTimeField(el)) {
- this.formatTimeField(el, value);
- } else {
- this.formatField(el, value);
- }
- }
-
- // Handle link
- let link = template.querySelector('a');
- if (link && item.url !== '') {
- [
- link.href,
- link.title
- ] = [
- item.url,
- `View ${item.fields['post_title']??'Item'}`
- ];
- }
-
- if (isTimeline) {
- this.addTimelineElements(item, template);
- }
-
- return template;
- }
- splitIDs(value) {
- return String(value).split(',').map((value) => parseInt(value.trim())).filter(value=>value);
- }
- isImageField(item, value) {
- if (!Object.hasOwn(item, 'images') || Object.keys(item.images).length === 0) {
- return false;
- }
- let values = this.splitIDs(value);
-
- return values.some(v =>
- Object.keys(item.images).map(k => parseInt(k)).includes(parseInt(v))
- );
- }
- formatImageFields(element, value, item) {
- let values = this.splitIDs(value); // Convert string to array first
- if (values.length === 0) return;
-
- if (values.length > 1) {
- let image = element.querySelector('img');
- if (!image) return;
- values.forEach(imgID => {
- let img = image.cloneNode(true);
- this.formatImageField(img, imgID, item);
- element.append(img);
- });
- image.remove();
- } else {
- if (element.tagName !== 'IMG') {
- element = element.querySelector('img');
- if (!element) return;
- }
- this.formatImageField(element, values[0], item);
- }
- }
- formatImageField(element, value, item) {
- let imgData = item.images[value]??false;
- if (!imgData) return;
- [
- element.src,
- element.srcset,
- element.alt
- ] = [
- imgData.tiny,
- `${imgData.tiny} 50w, ${imgData.small} 300w, ${imgData.medium} 1024w`,
- imgData['image-alt-text']
- ]
- }
- isTaxonomyField(item, field) {
- if (!Object.hasOwn(item, 'taxonomies') || Object.keys(item.taxonomies).length === 0) {
- return false;
- }
-
- return Object.keys(item.taxonomies).includes(field);
- }
- formatTaxonomyField(element, item, field, value) {
- if (element.tagName !== 'UL' || !element.querySelector('li')) return;
- let values = this.splitIDs(value);
- if (values.length === 0) {
- element.remove();
- }
- let listItem = element.querySelector('li');
- for (let termID of values) {
- let term = item.taxonomies[field][termID]??false;
- if (!term) continue;
- let termItem = listItem.cloneNode(true);
- let link = termItem.querySelector('a');
- if (!link) continue;
-
- [
- link.href,
- link.title,
- link.textContent
- ] = [
- term.url,
- `See more ${term.title}`,
- term.title
- ];
- element.append(termItem);
- }
- listItem.remove();
- }
- isTimeField(el) {
- return el.tagName === 'TIME' || el.querySelector('time') !== null;
- }
- formatTimeField(element, value) {
- if (element.tagName !== 'TIME') {
- element = element.querySelector('time');
- if (!element) return;
- }
- element.setAttribute('datetime', value);
- element.textContent = window.formatTimeAgo(value, 'F Y');
- }
- formatField(element, value) {
- element.textContent = value;
- }
-
- addTimelineElements(item, template) {
- let [
- afterEl,
- number,
- started,
- last
- ] = [
- template.querySelector('span.after-text'),
- template.querySelector('[data-field="number"] b'),
- template.querySelector('[data-field="started"] time'),
- template.querySelector('[data-field="updated"] time')
- ];
-
- if (afterEl) {
- afterEl.textContent = `After ${item.fields.number} Tx`;
- }
- if (number) {
- number.textContent = item.fields.number;
- }
- if (started) {
- this.formatTimeField(started, item.fields.timeline[0]['post_date']);
- }
- if (last) {
- this.formatTimeField(last, item.fields.timeline[item.fields.timeline.length - 1]['post_date']);
- }
- }
-
- removePlaceholders() {
- const placeholders = this.ui.grid.querySelectorAll('.placeholder');
- if (placeholders.length > 0) {
- placeholders.forEach(p => p.remove());
- }
- }
-
-
- addPlaceholders() {
- let total = this.contentTypes.length;
- const fragment = document.createDocumentFragment();
- for (let i = 0; i < 12; i++) {
- let template = window.getTemplate('placeholderTemplate');
-
- let rand = Math.floor(Math.random() * total);
- let icon;
- if (this.ui.content && this.ui.content.length > 0) {
- icon = this.ui.content.filter((content) => { return content.value === this.contentTypes[rand]}).querySelector('.icon').cloneNode(true);
- } else {
- icon = window.getIcon(this.container.dataset.icon);
- }
- template.append(icon);
- fragment.append(template);
- }
- this.ui.grid.append(fragment);
- }
-
-
-
- /**
- *
- * @param {object} filters {name: value}
- */
- updateFilter(filters) {
- //double check filters are what we're expecting
- let allowed = ['taxonomy','favourites','match', ... Object.keys(this.filters)];
-
- filters = Object.keys(filters)
- .filter(key => allowed.includes(key))
- .reduce((obj, key) => {
- obj[key] = filters[key];
- return obj;
- }, {});
-
- if (window.getDifferences.map(this.filters, filters)) {
- this.filters = { ...this.filters, ...filters }; // Merge instead of replace
- this.updateURL();
- this.store.setFilters(filters);
- }
- }
- /**
- * Update visible filters based on selected content type
- */
- updateContentFor(contentType) {
- // Update taxonomy filter visibility
- const taxonomyButtons = this.ui.filterContainer.querySelectorAll('[data-filter="taxonomy"]');
- taxonomyButtons.forEach(button => {
- const forTypes = button.dataset.for?.split(',') || [];
- button.hidden = forTypes.length > 0 && !forTypes.includes(contentType);
- });
-
- // Update ordering options
- const orderButtons = this.ui.filterContainer.querySelectorAll('[data-for]');
- orderButtons.forEach(button => {
- const forTypes = button.dataset.for?.split(',') || [];
- if (forTypes.length > 0) {
- button.hidden = !forTypes.includes(contentType);
- // Uncheck if hiding
- if (button.hidden && button.checked) {
- button.checked = false;
- }
- }
- });
-
- // Update order direction visibility based on selected orderby
- const orderBy = this.ui.filterContainer.querySelector('[name="orderby"]:checked');
- this.updateOrderDirectionVisibility(orderBy?.value);
- }
-
- /**
- * Show/hide order direction based on orderby selection
- */
- updateOrderDirectionVisibility(orderBy) {
- const orderDirection = this.ui.filterContainer.querySelector('.order-direction');
- if (orderDirection) {
- const forOrders = orderDirection.dataset.forOrder?.split(',') || [];
- orderDirection.hidden = forOrders.length > 0 && !forOrders.includes(orderBy);
- }
- }
- /*********************************************************************
- LISTENERS
- *********************************************************************/
- initListeners() {
- this.popStateHandler = this.handlePopState.bind(this);
- this.clickHandler = this.handleClick.bind(this);
- this.changeHandler = this.handleChange.bind(this);
- this.imageObserver = null;
- this.resizeObserver = null;
- if ('IntersectionObserver' in window) {
- this.imageObserver = new IntersectionObserver(entries => {
- entries.forEach(entry => {
- this.loadImage(entry.target);
- this.imageObserver.unobserve(entry.target);
- });
- }, {
- rootMargin: '100px',
- threshold: .1
- });
- }
-
- if ('ResizeObserver' in window) {
- this.resizeObserver = new ResizeObserver(() => {
- window.debouncer.schedule(
- 'feed-update-images',
- () => this.updateImageSizes(),
- 250
- );
- });
- } else {
- window.addEventListener('resize', () => {
- window.debouncer.schedule(
- 'feed-update-images',
- () => this.updateImageSizes(),
- 250
- );
- });
- }
-
- window.addEventListener('popstate', this.popStateHandler);
- document.addEventListener('click', this.clickHandler);
- document.addEventListener('change', this.changeHandler);
- }
-
- handlePopState(e) {
- if (e.state?.filters) {
- if (this.processURLFilters()) {
- this.store.setFilters(this.filters);
- this.a11y.announce('Feed filters updated from browser history');
- }
- }
- }
-
- handleClick(e) {
- if (window.targetCheck(e, this.elements.loadMore)) {
- this.nextPage();
- } else if (window.targetCheck(e, this.elements.clearFilter)) {
- this.clearAllTaxonomies();
- } else if (window.targetCheck(e, '.remove-item')) {
- this.handleRemoveSelectedTerm(e);
- }
- }
-
- handleRemoveSelectedTerm(e) {
- const selectedItem = e.target.closest('.selected-item');
- if (!selectedItem) return;
-
- const termId = parseInt(selectedItem.dataset.id);
- const taxonomy = selectedItem.dataset.taxonomy;
-
- // Remove from filters
- if (this.taxonomyFilters[taxonomy]) {
- this.taxonomyFilters[taxonomy] = this.taxonomyFilters[taxonomy]
- .filter(id => id !== termId);
-
- if (this.taxonomyFilters[taxonomy].length === 0) {
- delete this.taxonomyFilters[taxonomy];
- }
- }
-
- // Remove from UI
- selectedItem.remove();
-
- // Update filters
- this.updateFilter({
- taxonomy: Object.keys(this.taxonomyFilters).length > 0
- ? this.taxonomyFilters
- : null,
- page: 1
- });
- }
-
- handleChange(e) {
- let target = e.target;
- if (Object.hasOwn(target.dataset, 'filter')) {
- if (target.dataset.filter === 'content') {
- this.updateContentFor(target.value);
- this.updateFilter({ content: target.value, page: 1 });
- } else if (target.dataset.filter === 'orderby') {
- this.updateOrderDirectionVisibility(target.value);
- this.updateFilter({ orderby: target.value, page: 1 });
- } else if (target.dataset.filter === 'order') {
- this.updateFilter({ order: target.value, page: 1 });
- } else if (target.dataset.filter === 'match') {
- this.updateFilter({ match: target.checked ? 'all' : 'any', page: 1 });
- } else if (target.dataset.filter === 'favourites') {
- this.updateFilter({ favourites: target.checked, page: 1 });
- }
- }
- }
-}
-
-document.addEventListener('DOMContentLoaded', async function() {
- window.auth.subscribe(event => {
- if (event === 'auth-loaded') {
- window.feedBlock = new FeedBlock();
- }
- });
-});
diff --git a/src/fields/block.json b/src/fields/block.json
deleted file mode 100644
index 085ccdf..0000000
--- a/src/fields/block.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/fields",
- "title": "JakeVan Fields",
- "category": "jvb",
- "icon": "ellipses",
- "description": "Access data from your custom fields",
- "keywords": [ "field", "custom", "jake" ],
- "version": "0.9.0",
- "textdomain": "jvb",
- "supports": {
- "html": false,
- "align": ["wide", "full"]
- },
- "selectors": {
- "root": ".jvb-f"
- },
- "styles": [],
- "render": "file:./render.php",
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css",
- "viewScript": "file:./view.js"
-}
diff --git a/src/fields/edit.js b/src/fields/edit.js
deleted file mode 100644
index 6741773..0000000
--- a/src/fields/edit.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
-import { SelectControl, ToggleControl, PanelBody } from '@wordpress/components';
-
-/**
- * Styles
- */
-import './editor.scss';
-
-/**
- * Edit function for Summary Block
- */
-export default function Edit({ attributes, setAttributes }) {
- const blockProps = useBlockProps();
-
- return (
- <div {...blockProps}>
- <div className="jvb-summary-preview">
- <h3>{__('Summary', 'jvb')}</h3>
- <p className="jvb-list-preview-note">
- {__('This will inherit the current query to build the information from our custom meta on the front end.', 'jvb')}
- </p>
- </div>
- </div>
- );
-}
diff --git a/src/fields/editor.scss b/src/fields/editor.scss
deleted file mode 100644
index bdf5776..0000000
--- a/src/fields/editor.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Directory List Block Editor Styles
- */
-.jvb-summary-preview {
- padding: 20px;
- background-color: #f8f9fa;
- border: 1px solid #e2e4e7;
- border-radius: 4px;
-
- h3 {
- margin-top: 0;
- padding-bottom: 10px;
- border-bottom: 1px solid #ff0080;
- }
- &-note {
- font-style: italic;
- color: #555d66;
- margin-bottom: 0;
- }
-}
diff --git a/src/fields/index.js b/src/fields/index.js
deleted file mode 100644
index c477d23..0000000
--- a/src/fields/index.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Registers a new block provided a unique name and an object defining its behavior.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-import { registerBlockType } from '@wordpress/blocks';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * All files containing `style` keyword are bundled together. The code used
- * gets applied both to the front of your site and to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './style.scss';
-
-/**
- * Internal dependencies
- */
-import Edit from './edit';
-import save from './save';
-import metadata from './block.json';
-
-/**
- * Every block starts by registering a new block type definition.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-registerBlockType( metadata.name, {
- /**
- * @see ./edit.js
- */
- edit: Edit,
-
- /**
- * @see ./save.js
- */
- save,
-} );
diff --git a/src/fields/index.php b/src/fields/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/fields/index.php
+++ /dev/null
diff --git a/src/fields/render.php b/src/fields/render.php
deleted file mode 100644
index 1c2b31a..0000000
--- a/src/fields/render.php
+++ /dev/null
@@ -1,320 +0,0 @@
-<?php
-
-use JVBase\managers\Cache;
-use JVBase\meta\Meta;
-use JVBase\meta\Render;
-use JVBase\registrar\Registrar;
-
-if (!defined('ABSPATH')) {
- exit; // Exit if accessed directly
-}
-/**
- * Summary Block Render
- *
- * @package Edmonton_Ink
- */
-
-function jvbRenderSummaryBlock(array $attributes):string
-{
-
- // Buffer output
- if (is_tax()) {
- switch (get_queried_object()->taxonomy) {
- case BASE.'shop':
- return jvbRenderShopSummary();
- default:
- return jvbRenderTermSummary();
- }
- } elseif (is_singular()) {
- return jvbRenderArtistSummary();
- }
- return '';
-}
-
-function jvbRenderArtistSummary():string
-{
- $current = get_queried_object();
- $cache = Cache::for('artistSummary', WEEK_IN_SECONDS);
- $key = $current->ID;
- $cached = $cache->get($key);
- if ($cached !== false) {
- return $cached;
- }
-
- ob_start();
- $meta = Meta::forPost($current->ID);
- $artist = jvbContentFromUser((int)$current->post_author);
-
- $registrar = Registrar::getInstance($current->post_type);
- $sections = [];
- if ($registrar) {
- $sections = $registrar->getSections();
- }
-
-
-
-
-// $handler = JVB()->getContent(str_replace(BASE,'', $current->post_type));
- ?>
- <nav id="artist" class="on-this-page index">
- <label>Jump to:
- <button type="button" aria-label="Show Index" title="Show Index" class="toggle" aria-expanded="false">
- <?= jvbIcon('plus-square')?>
- </button>
- </label>
- <ul>
- <li><a href="#top" title="Back to Top"><?=jvbIcon('caret-circle-up')?></a></li>
- <li><a href="#about">About</a></li>
- <li><a href="#styles">Styles</a></li>
- <li><a href="#contact">Contact</a></li>
- <li><a href="#work">Work</a></li>
- </ul>
- </nav>
- <header id="top">
- <h1><small><?=(!empty($artist['city'])) ? $artist['city']['name'] :'Edmonton'?>'s Best <?= (!empty($artist['type'])) ?
- $artist['type']['name']:'Tattoo Artists'?>:
- </small><?=$artist['display_name']?></h1>
- <div>
- <?php if (!empty($artist['shop'])) : ?>
- <ul class="term-list shop">
- <li>
- <a href="<?=$artist['shop']['url']?>" title="Learn more about <?=$artist['shop']['name']?>">
- <?= strtolower($artist['shop']['name'])?>
- </a>
- </li>
- </ul>
- <?php endif; ?>
- <?php if (!empty($artist['city'])): ?>
- <ul class="term-list city">
- <li>
- <a href="<?=$artist['city']['url']?>" title="See who else is rocking out of <?=$artist['city']['name']?>">
- <?= strtolower($artist['city']['name'])?>
- </a>
- </li>
- </ul>
- <?php endif; ?>
- <?php $styles = $meta->get('top_styles');
- if (!empty($styles)) {
- ?>
- <ul class="term-list style">
- <?php
- foreach ($styles as $style) {
- $term = get_term((int)$style, BASE.'style');
- if ($term && !is_wp_error($term)) {
- $link = get_term_link((int)$style, BASE.'style');
- ?>
- <li>
- <a href="<?=$link?>" title="Learn more about <?=html_entity_decode($term->name)?>">
- <?=strtolower(html_entity_decode($term->name))?>
- </a>
- </li>
- <?php
- }
- }
- ?>
- </ul>
- <?php
- }
- ?>
- </div>
- </header>
- <section>
- <details class="bio-info">
- <summary class="row btw">
- <h2>About <?= ($artist['name'] !== '') ? $artist['name'] : strtok($artist['display_name'], ' ')?></h2>
- </summary>
- <div class="columns stack-small">
- <div class="column">
- <?= Render::renderFrom($meta, 'image_portrait'); ?>
- </div>
- <div class="column">
- <?= Render::renderFrom($meta, 'short_bio'); ?>
- </div>
- </div>
- <div id="styles">
- <h3>Works In</h3>
- <?= jvbGetTheTerms('style', $current->ID) ?>
- </div>
- <div class="contact">
- <h3>Contact:</h3>
- <?php
- echo jvbRenderContactInfo($current->ID, $meta);
- echo jvbRenderLinks($current->ID, $meta);
- ?>
- </div>
-
- <div id="about">
- <?= Render::renderFrom($meta, 'bio')?>
- </div>
- </details>
- </section>
- <section id="contact" class="">
- <h2>Contact <?=$artist['name']?></h2>
- <?php
- echo jvbRenderContactInfo($current->ID, 'post');
- echo jvbRenderLinks($current->ID, 'post');
- ?>
- </section>
- <?php
- $finished = ob_get_clean();
- $cache->set($key, $finished);
- return $finished;
-}
-
-function jvbRenderShopSummary()
-{
- $current = get_queried_object();
-
- $cache = Cache::for('shop_bio', WEEK_IN_SECONDS)->connect('taxonomy');
- $key = $current->term_id;
- $cached = $cache->get($key);
- if ($cached !== false) {
- return $cached;
- }
-
- ob_start();
-
- $meta = Meta::forTerm($current->term_id);
- $fields = $meta->getAll(['average_rating', 'established', 'bio','location','hours','specialties','awards','reviews']);
- ?>
- <nav id="shop" class="on-this-page index">
- <label>Jump to:
- <button type="button" aria-label="Show Index" title="Show Index" class="toggle" aria-expanded="false">
- <?= jvbIcon('plus-square')?>
- </button>
- </label>
- <ul>
- <li><a href="#top" title="Back to Top"><?=jvbIcon('caret-circle-up')?></a></li> <?php
- if ($fields['rating'] !== 'none') {
- ?>
- <li><a href="#rating">Rating</a></li>
- <?php
- } elseif ($fields['opened'] !== '') {
- ?>
- <li><a href="#opened">Opened</a></li>
- <?php
- } elseif ($fields['location'] !== '') {
- ?>
- <li><a href="#location">Location</a></li>
- <?php
- } elseif ($fields['about'] !== '') {
- ?>
- <li><a href="#about">About</a></li>
- <?php
- } elseif ($fields['hours'] !== '') {
- ?>
- <li><a href="#hours">Hours</a></li>
- <?php
- } elseif ($fields['specialties'] !== '') {
- ?>
- <li><a href="#specialties">Specialties</a></li>
- <?php
- } elseif ($fields['awards'] !== '') {
- ?>
- <li><a href="#awards">Awards</a></li>
- <?php
- } elseif ($fields['reviews'] !== '') {
- ?>
- <li><a href="#reviews">Reviews</a></li>
- <?php
- }
- ?>
- <li><a href="#contact">Contact</a></li>
- <li><a href="#artists">Artists</a></li>
- </ul>
- </nav>
- <header id="top">
- <div class="columns stack-small">
- <div class="column">
- <?=jvbFormatImage($meta->get('image'))?>
- </div>
- <div class="column">
- <h1>
- <small><?= (get_term((int)$meta->get('city'), BASE.'city')) ?
- get_term((int)$meta->get('city'), BASE.'city')->name :
- 'Edmonton'?>'s Best Tattoo Shops</small>
- <?=$current->name?>
- </h1>
- <?= jvbFormatRating($current->term_id, 'term') ?>
- <?= Render::renderFrom($meta, 'slogan'); ?>
- </div>
- </div>
- </header>
- <section>
- <details class="bio-info">
- <summary class="row btw">
- <h2>Learn More About <?=$current->name?></h2>
- </summary>
- <div class="map">
- <?= Render::renderFrom($meta, 'location'); ?>
- </div>
- <div class="short-bio">
- <?= Render::renderFrom($meta, 'short_bio'); ?>
- </div>
-
- <div class="contact">
- <h3>Contact:</h3>
- <?php
- echo jvbRenderContactInfo($current->term_id, 'term');
- echo jvbRenderLinks($current->term_id, 'term');
- ?>
- </div>
-
- <div id="about">
- <?= Render::renderFrom($meta, 'bio')?>
- </div>
- </details>
- </section>
- <section id="contact" class="">
- <h2>Contact </h2>
- <?php
- echo jvbRenderContactInfo($current->term_id, 'term');
- echo jvbRenderLinks($current->term_id, 'term');
- ?>
- </section>
- <?= jvbRenderHours($current->term_id, 'term')?>
-
-
- <?php
- $finished = ob_get_clean();
- $cache->set($key, $finished);
- return $finished;
-}
-
-
-function jvbRenderTermSummary()
-{
- $current = get_queried_object();
- $cache = Cache::for('term_summary', WEEK_IN_SECONDS)->connect('taxonomy');
- $key = $current->ID;
- $cached = $cache->get($key);
- if ($cached !== false) {
- return $cached;
- }
-
- ob_start();
- $tax = jvbNoBase($current->taxonomy);
- switch ($tax) {
- case 'style':
- $title = 'Tattoo Artists';
- break;
- case 'theme':
- $title = 'Tattoos';
- break;
- default:
- $title = '';
- }
-
- $meta = Meta::forTerm($current->ID);
- $fields = $meta->getAll();
-
- ?>
- <header id="top">
- <h1><?= get_the_archive_title() ?></h1>
- </header>
-
- <?php
- $finished = ob_get_clean();
- $cache->set($key, $finished);
- return $finished;
-}
diff --git a/src/fields/save.js b/src/fields/save.js
deleted file mode 100644
index 8169594..0000000
--- a/src/fields/save.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function save() {
- return null; // Dynamic block rendered by PHP
-}
diff --git a/src/fields/style.scss b/src/fields/style.scss
deleted file mode 100644
index b182fe9..0000000
--- a/src/fields/style.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-details > div {
- margin: 1rem 0;
-}
-
-main > header:not(:has(img)) {
- margin-top: 3rem!important;
-}
-
-header a::before {
- display: none!important;
-}
-
-header + details {
- margin: 1.5rem auto 3rem!important;
- max-width: var(--wide);
-}
-
-main {
- padding-top: 0!important;
-}
diff --git a/src/fields/view.js b/src/fields/view.js
deleted file mode 100644
index 8b13789..0000000
--- a/src/fields/view.js
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/forms/block.json b/src/forms/block.json
deleted file mode 100644
index 7279b38..0000000
--- a/src/forms/block.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/forms",
- "title": "Contact Forms",
- "category": "jvb",
- "icon": "align-center",
- "description": "Our custom contact forms",
- "keywords": [ "form", "forms", "contact" ],
- "version": "1.0.0",
- "textdomain": "jvb",
- "attributes": {
- "formType": {
- "type": "string",
- "default": ""
- },
- "showLabels": {
- "type": "boolean",
- "default": true
- },
- "customEmailTo": {
- "type": "string",
- "default": ""
- }
- },
- "supports": {
- "html": false,
- "align": ["wide", "full"]
- },
- "selectors": {
- "root": ".jvb-form-block"
- },
- "styles": [
- { "name": "default", "label": "Default", "isDefault": true }
- ],
- "example": {
- "attributes": {
- "formType": "contact",
- "showLabels": true
- }
- },
- "render": "file:./render.php",
- "editorScript": "file:./index.js",
- "editorStyle": "file:./editor.scss",
- "style": "file:./style-index.css",
- "viewScript": "file:./view.js"
-}
diff --git a/src/forms/edit.js b/src/forms/edit.js
deleted file mode 100644
index 3a8387b..0000000
--- a/src/forms/edit.js
+++ /dev/null
@@ -1,319 +0,0 @@
-/**
- * edit.js
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
-import {
- PanelBody,
- SelectControl,
- ToggleControl,
- TextControl,
- Notice
-} from '@wordpress/components';
-import { useState, useEffect } from '@wordpress/element';
-
-/**
- * Styles
- */
-import './editor.scss';
-
-/**
- * Edit function for Form Block
- */
-export default function Edit({ attributes, setAttributes }) {
- const {
- formType,
- showLabels,
- customEmailTo,
- turnstileEnabled
- } = attributes;
-
- const [isPreviewVisible, setIsPreviewVisible] = useState(true);
-
- const blockProps = useBlockProps({
- className: `jvb-form-block ${formType ? `jvb-form-block-${formType}` : ''}`
- });
-
- // Get form types from localized data, with fallback
- const getFormTypes = () => {
- if (typeof window !== 'undefined' && window.jvbFormsData && window.jvbFormsData.formTypes) {
- return window.jvbFormsData.formTypes;
- }
-
- // Fallback if data isn't available
- return [
- { label: __('Select a form type', 'jvb'), value: '' },
- { label: __('No forms available', 'jvb'), value: '', disabled: true }
- ];
- };
-
- // Get available forms configuration
- const getAvailableForms = () => {
- console.log(window.jvbFormsData);
- if (typeof window !== 'undefined' && window.jvbFormsData && window.jvbFormsData.availableForms) {
- return window.jvbFormsData.availableForms;
- }
- return {};
- };
-
- const formTypes = getFormTypes();
- const availableForms = getAvailableForms();
-
- // Get form configuration based on selected type
- const getCurrentFormConfig = () => {
- if (!formType || !availableForms[formType]) {
- return null;
- }
- return availableForms[formType];
- };
-
- // Form labels based on the selected form type
- const getFormLabels = () => {
- const formConfig = getCurrentFormConfig();
-
- if (formConfig) {
- return {
- title: formConfig.title || __('Form', 'jvb'),
- description: Array.isArray(formConfig.description)
- ? formConfig.description.join(' ')
- : (formConfig.description || ''),
- button: formConfig.submit || __('Submit', 'jvb')
- };
- }
-
- return {
- title: __('Form', 'jvb'),
- description: formType ? __('Loading form configuration...', 'jvb') : __('Please select a form type in the sidebar', 'jvb'),
- button: __('Submit', 'jvb')
- };
- };
-
- const formLabels = getFormLabels();
-
- // Render a preview of the form in the editor
- const renderFormPreview = () => {
- if (!formType) {
- return (
- <Notice status="warning" isDismissible={false}>
- {__('Please select a form type in the block settings.', 'jvb')}
- </Notice>
- );
- }
-
- let formFields = [];
-
- switch (formType) {
- case 'contact':
- formFields = [
- { id: 'name', label: __('Name', 'jvb'), type: 'text', required: true },
- { id: 'email', label: __('Email', 'jvb'), type: 'email', required: true },
- { id: 'phone', label: __('Phone', 'jvb'), type: 'tel', required: true },
- { id: 'instagram', label: __('Instagram URL', 'jvb'), type: 'url' },
- { id: 'contact_methods', label: __('Preferred Contact', 'jvb'), type: 'checkboxes',
- options: [
- { value: 'text', label: __('Text', 'jvb') },
- { value: 'call', label: __('Call', 'jvb') },
- { value: 'email', label: __('Email', 'jvb') },
- { value: 'instagram', label: __('Instagram', 'jvb') }
- ]
- },
- { id: 'message', label: __('Your Message', 'jvb'), type: 'textarea', required: true }
- ];
- break;
-
- case 'feature_request':
- formFields = [
- { id: 'name', label: __('Name', 'jvb'), type: 'text', help: __('Required if you want us to follow up.', 'jvb') },
- { id: 'email', label: __('Email', 'jvb'), type: 'email', help: __('Required if you want us to follow up.', 'jvb') },
- { id: 'follow_up', label: __('Would you like me to follow up with you?', 'jvb'), type: 'checkbox' },
- { id: 'target_audience', label: __('This Feature is For', 'jvb'), type: 'checkboxes',
- options: [
- { value: 'artists', label: __('Artists', 'jvb') },
- { value: 'visitors', label: __('Site Visitors', 'jvb') },
- { value: 'partners', label: __('Partners', 'jvb') },
- { value: 'other', label: __('Other', 'jvb') }
- ]
- },
- { id: 'feature_name', label: __('Name your Feature', 'jvb'), type: 'text', required: true },
- { id: 'message', label: __('Describe Your Feature', 'jvb'), type: 'textarea', required: true }
- ];
- break;
-
- case 'technical_issue':
- formFields = [
- { id: 'name', label: __('Name', 'jvb'), type: 'text' },
- { id: 'email', label: __('Email', 'jvb'), type: 'email' },
- { id: 'follow_up', label: __('Would you like me to follow up with you?', 'jvb'), type: 'checkbox' },
- { id: 'issue_type', label: __('Type of Issue', 'jvb'), type: 'checkboxes',
- options: [
- { value: 'visual', label: __('Visual', 'jvb') },
- { value: 'error', label: __('Error Page', 'jvb') },
- { value: 'other', label: __('Other', 'jvb') }
- ]
- },
- { id: 'message', label: __('Please describe the issue.', 'jvb'), type: 'textarea', required: true }
- ];
- break;
- }
-
- return (
- <>
- <h3 className="jvb-form-title">{formLabels.title}</h3>
- <p className="jvb-form-description">{formLabels.description}</p>
-
- <div className="jvb-form-preview">
- {formFields.map((field) => (
- <div key={field.id} className="jvb-form-field">
- {showLabels && (
- <label htmlFor={`jvb-${field.id}`} className={field.required ? 'required' : ''}>
- {field.label}
- </label>
- )}
-
- {field.type === 'text' && (
- <input
- type="text"
- id={`jvb-${field.id}`}
- placeholder={showLabels ? '' : field.label}
- disabled
- />
- )}
-
- {field.type === 'email' && (
- <input
- type="email"
- id={`jvb-${field.id}`}
- placeholder={showLabels ? '' : field.label}
- disabled
- />
- )}
-
- {field.type === 'tel' && (
- <input
- type="tel"
- id={`jvb-${field.id}`}
- placeholder={showLabels ? '' : field.label}
- disabled
- />
- )}
-
- {field.type === 'url' && (
- <input
- type="url"
- id={`jvb-${field.id}`}
- placeholder={showLabels ? '' : field.label}
- disabled
- />
- )}
-
- {field.type === 'textarea' && (
- <textarea
- id={`jvb-${field.id}`}
- placeholder={showLabels ? '' : field.label}
- disabled
- rows="4"
- ></textarea>
- )}
-
- {field.type === 'checkbox' && (
- <div className="jvb-form-checkbox">
- <input
- type="checkbox"
- id={`jvb-${field.id}`}
- disabled
- />
- <label htmlFor={`jvb-${field.id}`}>{field.label}</label>
- </div>
- )}
-
- {field.type === 'checkboxes' && field.options && (
- <div className="jvb-form-checkboxes">
- {field.options.map((option) => (
- <div key={option.value} className="jvb-form-checkbox">
- <input
- type="checkbox"
- id={`jvb-${field.id}-${option.value}`}
- disabled
- />
- <label htmlFor={`jvb-${field.id}-${option.value}`}>{option.label}</label>
- </div>
- ))}
- </div>
- )}
-
- {field.help && (
- <p className="jvb-form-help">{field.help}</p>
- )}
- </div>
- ))}
-
- {turnstileEnabled && (
- <div className="jvb-form-turnstile">
- <div className="jvb-turnstile-placeholder">
- <span>{__('Cloudflare Turnstile will appear here', 'jvb')}</span>
- </div>
- </div>
- )}
-
- <div className="jvb-form-submit">
- <button type="button" className="jvb-form-button">{formLabels.button}</button>
- </div>
- </div>
- </>
- );
- };
-
- return (
- <>
- <InspectorControls>
- <PanelBody title={__('Form Settings', 'jvb')}>
- <SelectControl
- label={__('Form Type', 'jvb')}
- value={formType}
- options={formTypes}
- onChange={(value) => setAttributes({ formType: value })}
- />
-
- <ToggleControl
- label={__('Show Field Labels', 'jvb')}
- checked={showLabels}
- onChange={(value) => setAttributes({ showLabels: value })}
- help={__('Toggle to show or hide field labels.', 'jvb')}
- />
-
- <TextControl
- label={__('Custom Recipient Email', 'jvb')}
- value={customEmailTo || ''}
- onChange={(value) => setAttributes({ customEmailTo: value })}
- help={__('Leave empty to use the default email.', 'jvb')}
- type="email"
- />
- </PanelBody>
-
- <PanelBody title={__('Form Preview', 'jvb')} initialOpen={false}>
- <ToggleControl
- label={__('Show Preview', 'jvb')}
- checked={isPreviewVisible}
- onChange={(value) => setIsPreviewVisible(value)}
- />
-
- <p className="components-base-control__help">
- {__('This is just a preview. The actual form will be rendered on the frontend.', 'jvb')}
- </p>
- </PanelBody>
- </InspectorControls>
-
- <div {...blockProps}>
- {isPreviewVisible ? (
- renderFormPreview()
- ) : (
- <div className="jvb-form-placeholder">
- <h3>{formLabels.title}</h3>
- <p>{__('Form preview is hidden. Edit settings in the sidebar.', 'jvb')}</p>
- </div>
- )}
- </div>
- </>
- );
-}
diff --git a/src/forms/editor.scss b/src/forms/editor.scss
deleted file mode 100644
index e69de29..0000000
--- a/src/forms/editor.scss
+++ /dev/null
diff --git a/src/forms/index.js b/src/forms/index.js
deleted file mode 100644
index fc49c90..0000000
--- a/src/forms/index.js
+++ /dev/null
@@ -1,40 +0,0 @@
-//index.js
-/**
- * Registers a new block provided a unique name and an object defining its behavior.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-import { registerBlockType } from '@wordpress/blocks';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * All files containing `style` keyword are bundled together. The code used
- * gets applied both to the front of your site and to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './style.scss';
-
-/**
- * Internal dependencies
- */
-import Edit from './edit';
-import save from './save';
-import metadata from './block.json';
-
-/**
- * Every block starts by registering a new block type definition.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-registerBlockType( metadata.name, {
- /**
- * @see ./edit.js
- */
- edit: Edit,
-
- /**
- * @see ./save.js
- */
- save,
-} );
diff --git a/src/forms/index.php b/src/forms/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/forms/index.php
+++ /dev/null
diff --git a/src/forms/render.php b/src/forms/render.php
deleted file mode 100644
index 9865bf3..0000000
--- a/src/forms/render.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-if (!defined('ABSPATH')) {
- exit; // Exit if accessed directly
-}
-/**
- * Form Block Render
- *
- * @package Edmonton_Ink
- */
-
-/**
- * Renders the form block on the frontend
- *
- * @param array $attributes The block attributes.
- * @param string $content The block content.
- * @param WP_Block $block The block instance.
- * @return string The rendered output.
- */
-function jvbRenderFormBlock(array $attributes, string $content, WP_Block $block):string
-{
- // Get form type from attributes
- $form_type = isset($attributes['formType']) ? $attributes['formType'] : '';
-
- // If no form type selected, return a message
- if (empty($form_type)) {
- return '<div class="jvb-form-error">No form type selected. Please edit this block and select a form type.</div>';
- }
-
- // Get other attributes
- $show_labels = isset($attributes['showLabels']) ? $attributes['showLabels'] : true;
- $custom_email_to = isset($attributes['customEmailTo']) ? $attributes['customEmailTo'] : '';
-
- // Set custom options for the form
- $form_options = array();
-
- // Set custom email recipient if provided
- if (!empty($custom_email_to)) {
- $form_options['email_to'] = $custom_email_to;
- }
-
- // Render the form with the specified options
- $form_output = JVB()->forms()->renderForm($form_type);
-
- // Get block classes
- $wrapper_attributes = get_block_wrapper_attributes([
- 'class' => 'jvb-forms'
- ]);
-
- // Return the wrapped form
- return sprintf(
- '<div %1$s>%2$s</div>',
- $wrapper_attributes,
- $form_output
- );
-}
diff --git a/src/forms/save.js b/src/forms/save.js
deleted file mode 100644
index 933c127..0000000
--- a/src/forms/save.js
+++ /dev/null
@@ -1,23 +0,0 @@
-//save.js
-/**
- * React hook that is used to mark the block wrapper element.
- * It provides all the necessary props like the class name.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
- */
-import { useBlockProps } from '@wordpress/block-editor';
-
-/**
- * The save function defines the way in which the different attributes should
- * be combined into the final markup, which is then serialized by the block
- * editor into `post_content`.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save
- *
- * @return {WPElement} Element to render.
- */
-export default function save() {
- // This is a dynamic block that is rendered on the server side
- // Return null to let WordPress handle the saving and rendering
- return null;
-}
diff --git a/src/forms/style.scss b/src/forms/style.scss
deleted file mode 100644
index d540000..0000000
--- a/src/forms/style.scss
+++ /dev/null
@@ -1,5572 +0,0 @@
-//:target {
-// outline: none!important;
-// padding: 0!important;
-//}
-//:root {
-// --height: 3rem;
-//}
-//body:has(#theme-switcher:checked) {
-//
-//}
-//
-//.dashboard h1:first-of-type {
-// margin-top: 0!important;
-//}
-//main > footer {
-// max-width: 100%!important;
-// position: fixed;
-// z-index: 20;
-// bottom: 0;
-// left: 0;
-// right: 0;
-// width: 100%;
-// margin: 4rem 0 0 0!important;
-// height: var(--btn);
-// padding: 0;
-// background-color: var(--base);
-// box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw);
-//}
-//main>* {
-// max-width: min(768px, 90vw)!important;
-// margin: 0 auto!important;
-//}
-//main h1 {
-// margin: 0!important;
-// font-size: var(--txt-large);
-//}
-//main h1 + p + h2 {
-// font-size: var(--txt-medium);
-// text-transform: none;
-// margin: 0!important;
-//}
-//.replace {
-// max-width: 90vw!important;
-// margin: 1rem auto!important;
-//}
-//
-//.dash .replace {
-// text-align: center;
-//}
-//.dash input {
-// text-align: center;
-//}
-//.dash .true-false {
-// display: flex;
-// justify-content: center;
-// margin: 0;
-//}
-//.dash button[type=submit] {
-// width: 50%;
-// margin: 1rem auto;
-//}
-//
-//
-//form h2 {
-// margin: .5rem 0 1.5rem!important;
-//}
-//
-//.dashboard-nav {
-// height: var(--btn);
-// max-width:100vw;
-// padding: 0 .5rem;
-//}
-//.dashboard-nav ul {
-// height: var(--btn);
-// overflow-x: auto;
-//}
-//.dashboard-nav li + li:before {
-// display: none!important;
-//}
-//.dashboard-nav a {
-// height: var(--btn);
-// min-width: var(--btn);
-// padding: 0 .75rem;
-// color: var(--contrast)!important;
-//}
-//.dashboard-nav a .icon {
-// margin: 0;
-//}
-//.dashboard-nav a span {
-// display: none;
-//}
-//.dashboard-nav .current a:hover,
-//.dashboard-nav a:hover {
-// background-color: var(--action-0)!important;
-// color: var(--action-contrast)!important;
-//}
-//.dashboard-nav .current a {
-// background-color: var(--base-100)!important;
-// color: var(--contrast)!important;
-//}
-//.dashboard-nav .current a span {
-// display: block;
-//}
-//
-//
-//
-///* Loading states */
-//main {
-// opacity: 1;
-// transition: opacity .3s ease-in-out;
-// padding-bottom: 7rem;
-// min-height: calc(100vh - 12rem);
-//}
-//
-//main.transitioning {
-// opacity: 0;
-//}
-//.loading {
-// opacity: .7;
-//}
-//
-//
-///*Upload overlay*/
-//.loading-overlay {
-// position: fixed;
-// top: 0;
-// left: 0;
-// right: 0;
-// bottom: 0;
-// background-color: rgba(var(--base-rgb),var(--op-4));
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// opacity: 0;
-// visibility: hidden;
-// transition: opacity .3s ease, visibility .3s ease;
-// z-index: 9999;
-//}
-//
-//.loading-overlay.active {
-// opacity: 1;
-// visibility: visible;
-//}
-//
-//.loading-overlay .wrapper {
-// background-color: var(--base);
-// padding: 2rem;
-// border-radius: 8px;
-// text-align: center;
-// max-width: 90%;
-// width: 400px;
-//}
-//
-//.upload-spinner {
-// width: 50px;
-// height: 50px;
-// border: 5px solid var(--base-200);
-// border-top: 5px solid var(--action-0);
-// border-radius: 50%;
-// margin: 0 auto 1rem;
-// animation: spin 1s linear infinite;
-//}
-//
-//.upload-status h3 {
-// margin: 0 0 .5rem;
-// color: var(--contrast);
-//}
-//
-//.upload-message {
-// margin: 0;
-// color: var(--contrast-100);
-// font-size: var(--txt-x-small);
-//}
-//
-//
-//
-///* Optional: Add a pulsing effect to the text */
-//.upload-message {
-// animation: flicker 2s infinite;
-//}
-//
-//@keyframes flicker {
-// 0% { opacity: .6; }
-// 50% { opacity: 1; }
-// 100% { opacity: .6; }
-//}
-//
-//.form-section {
-// max-height: 0;
-// transform: scale(0);
-// visibility: hidden;
-//}
-//form:not(:has(.form-section.active)) .form-section:first-of-type,
-//.form-section.active {
-// max-height: fit-content;
-// transform: scale(1);
-// visibility: visible;
-//}
-//
-//
-//
-//.form-sections {
-// --height: fit-content;
-// position: fixed;
-// bottom: 3rem;
-// left: 0;
-// right: 0;
-// background-color: var(--base-100);
-// box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw);
-// z-index: 10;
-//}
-//.form-sections ul {
-// gap: 0;
-//}
-//.form-sections li {
-// width: 50%;
-//}
-//
-//.form-sections a:visited,
-//.form-sections a {
-// padding: .25rem;
-// width: 100%;
-// color: var(--contrast);
-//}
-//.replace:not(:has(.form-section.active)) .form-sections li:first-of-type a,
-//.current a {
-// background-color: var(--base-200);
-// color: var(--contrast);
-//}
-//.form-sections .icon {
-// margin: 0;
-//}
-//@media (min-width: 768px){
-// .form-sections ul {
-// flex-wrap: nowrap;
-// }
-// .form-sections li {
-// width: 100%;
-// }
-//}
-//
-//.submit-container {
-// position: fixed;
-// z-index: 20;
-//}
-//.submit-container button {
-// padding: 0;
-// width: 3rem;
-// height: 3rem;
-// color: var(--contrast-200);
-// background-color: var(--base-200);
-// justify-content: center;
-// border: 3px solid transparent;
-//}
-//.submit-container .save-popup {
-// position: absolute;
-// z-index: -1;
-// top: calc(50% - (1.875rem / 2));
-// font-size: var(--txt-x-small);
-// background-color: var(--action-0);
-// color: var(--action-contrast);
-// padding: .25rem .5rem;
-// border-radius: 4px;
-// white-space: nowrap;
-// visibility: hidden;
-// transition: all var(--trans-base);
-// opacity: 0;
-//}
-//.submit-container .icon {
-// --w: 2em;
-//}
-//.save-popup::before {
-// content: '';
-// position: absolute;
-// top: 50%;
-// transform: translateY(-50%);
-// border-top: .5rem solid transparent;
-// border-bottom: .5rem solid transparent;
-//}
-//.save-popup.show {
-// opacity: 1;
-// visibility: visible;
-//}
-//
-//@media (max-width: 767px){
-// .submit-container {
-// top: 3.5rem;
-// right: .5rem;
-// }
-// .save-popup {
-// right: 0;
-// }
-// .save-popup:before{
-// right: -.25rem;
-// border-left: .5rem solid var(--action-0);
-// }
-// .save-popup.show {
-// right: calc(100% + .5rem);
-// }
-//}
-//@media (min-width: 768px){
-// .submit-container {
-// bottom: 6rem;
-// left: .5rem;
-// }
-// .save-popup {
-// left: 0;
-// }
-// .save-popup:before{
-// left: -.25rem;
-// border-right: .5rem solid var(--action-0);
-// }
-// .save-popup.show {
-// left: calc(100% + .5rem);
-// }
-//}
-//
-//.autosaving span.save,
-//.autosaving button[type=submit] {
-// border-color: var(--base-200);
-// border-top-color: var(--action-0);
-// border-bottom-color: var(--action-50);
-// border-radius: 50%;
-// color: var(--contrast-200);
-// transition: color .25s var(--trans-t) var(--trans-fn);
-// transition-property: color, background-color, border;
-// animation: spin 1s linear infinite;
-//}
-//.autosaving .submit-container {
-// animation: pulse 1s linear infinite;
-//}
-//
-//@keyframes spin {
-// 0% { transform: rotate(0deg); }
-// 100% { transform: rotate(360deg); }
-//}
-//@keyframes pulse {
-// 0% {
-// transform: scale(.85);
-// opacity: .6;
-// }
-// 50% {
-// transform:scale(1);
-// opacity: 1; }
-// 100% {
-// transform: scale(.85);
-// opacity: .6; }
-//}
-//
-//.item-link[href=""]{
-// display: none;
-//}
-//.item-link {
-// position: fixed;
-// bottom: 5rem;
-// right: 1.5rem;
-// text-transform: uppercase;
-// color: var(--action-0);
-// background-color: var(--base);
-// border-radius: 4px;
-// padding: .25rem .5rem;
-// box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw-subtle);
-//}
-//.field {
-// margin: 3rem .5rem;
-//}
-//.field:has([required]) label::after {
-// content: 'required';
-// background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="%23B7332E" viewBox="0 0 256 256"><path d="M214.86,180.12a8,8,0,0,1-11,2.74L136,142.13V216a8,8,0,0,1-16,0V142.13L52.12,182.86a8,8,0,1,1-8.23-13.72L112.45,128,43.89,86.86a8,8,0,1,1,8.23-13.72L120,113.87V40a8,8,0,0,1,16,0v73.87l67.88-40.73a8,8,0,1,1,8.23,13.72L143.55,128l68.56,41.14A8,8,0,0,1,214.86,180.12Z"></path></svg>');
-// display: inline-block;
-// background-position: left center;
-// background-size: 1.2em;
-// padding-left: 1.3em;
-// background-repeat: no-repeat;
-// font-weight: 100;
-// text-transform: none;
-// font-size: .75em;
-// vertical-align: super;
-//
-//}
-//.field:has([required]) label {
-// position: relative;
-//}
-//.repeater-items .field {
-// margin: 1rem 0;
-//}
-//.taxonomy .field-group-header {
-// display: flex;
-// justify-content: space-between;
-//}
-//
-//.add-item-btn {
-// padding: .5em;
-// background: var(--base-100);
-// border: 1px solid var(--contrast-200);
-// border-radius: .25rem;
-// cursor: pointer;
-// font-size: .875rem;
-//}
-//.add-item-btn .icon {
-// --w: 1.5em;
-//}
-//
-//.add-item-btn:hover {
-// background: var(--base-200);
-//}
-//summary .type-label {
-// display: flex;
-// align-items: center;
-// gap: 1rem;
-//}
-//summary .type-label .icon {
-// position: relative;
-// top: -.25em;
-//}
-//
-///** Tabs **/
-//.tabs {
-// display: flex;
-// margin-bottom: 1.5rem;
-// border-bottom: 2px solid var(--base-200);
-// flex-wrap: wrap;
-//}
-//.tabs.parent {
-// max-width: 100vw;
-// background-color: var(--base);
-// padding: .5rem;
-// width: 100vw;
-// position: relative;
-// left: -2.1rem;
-// margin: 1rem 0;
-//}
-//.tab-content h2 {
-// font-size: var(--txt-large);
-// margin: 0!important;
-//}
-//.tab-content .tab-navigation,
-//.tab-content button[type=submit] {
-// display: inline-flex;
-// width: 48%;
-// background-color: var(--action-200);
-// color: var(--contrast-200);
-//}
-//
-//.tab-navigation.next {
-// display: flex;
-// margin-left: auto;
-//}
-//.tab-content .tab-navigation + button {
-// display: inline-flex;
-// margin-left: 3%;
-//}
-//
-//.tabs > button {
-// padding: .75rem 1.5rem;
-// border-radius: 0;
-// border: none;
-// background: none;
-// font-size: 1.1rem;
-// font-weight: bold;
-// letter-spacing: .05em;
-// text-transform: uppercase;
-// cursor: pointer;
-// position: relative;
-// color: inherit;
-//}
-//.tabs > button h2 {
-// font-size: 1.1rem;
-// line-height: 1;
-// margin: 0!important;
-//}
-//.tabs > button:hover,
-//.tabs > button:focus {
-// background-color: var(--base-200);
-//}
-//.tabs > button::after {
-// content: '';
-// position: absolute;
-// bottom: -2px;
-// left: 0;
-// width: 0;
-// height: 3px;
-// background-color: var(--action-50);
-// transition: width .3s;
-//}
-//.tabs > button.active::after {
-// width: 100%;
-//}
-//
-//.tabs > button.add-item-btn {
-// margin-left: auto;
-// background-color: var(--action-50);
-// border-radius: 4px;
-// padding: .5em;
-// font-weight: normal;
-// border: 1px solid var(--action-50);
-//}
-//.tabs > button.add-item-btn:focus,
-//.tabs > button.add-item-btn:hover {
-// background-color: var(--base);
-//}
-//
-//.type-filter:not(.active) span:not(.count){
-// display: none;
-//}
-//.type-filter:not(.active):hover span:not(.count){
-// display: block;
-//}
-///** News **/
-//.item-grid.list-view .item.news {
-// flex-direction: column;
-//}
-//.item.news h3 {
-// font-size: var(--txt-medium);
-// margin: 0!important;
-//}
-//.item.news summary .image {
-// width: 5rem;
-// background-color: var(--base-200);
-// border-radius: 4px;
-// aspect-ratio: 1;
-//}
-//.item.news summary {
-// gap: 1rem;
-//}
-//.item.news summary .summary {
-// width: 100%;
-//}
-//.item.news summary .header {
-// display: flex;
-// justify-content: space-between;
-// padding: .5rem 0;
-//}
-//.item .vote {
-// display: flex;
-// justify-content: flex-end;
-// align-items: center;
-// gap: .5em;
-// margin-top: auto;
-//}
-//.vote .count {
-// white-space: nowrap;
-//}
-//.item .vote .count:nth-of-type(2) {
-// margin-right: 2rem;
-//}
-///** Favourites **/
-//.favourites-title {
-// display: flex;
-// justify-content: space-between;
-// align-items: center;
-//}
-//main button {
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-//}
-//
-//.my-lists {
-// margin: .5rem 0;
-//}
-//
-//details.uploader .file-upload-container {
-// margin: 1rem var(--mr) 1rem var(--ml);
-// max-width: var(--content);
-//}
-//details .no-items {
-// text-align: center;
-// font-style: italic;
-// background-color: var(--base-50);
-// padding: var(--p-outer);
-// border-radius: var(--radius);
-//}
-//
-//.controls {
-// display: flex;
-// flex-wrap: wrap;
-// gap: .5rem;
-// justify-content: space-between;
-//}
-//
-//.controls label .icon + span {
-// transform: scaleX(0);
-// max-width: 0;
-//}
-//
-//.type-filters:hover label:hover .icon + span,
-//.controls label:hover .icon + span,
-//.controls :checked + label .icon + span {
-// transform: scaleX(1);
-// max-width: fit-content;
-//}
-//.type-filters,
-//.view-controls {
-// display: flex;
-// flex-wrap: wrap;
-// gap: .5rem;
-//}
-//
-///** Generalized Type Filters **/
-//details.type-filters {
-// flex-direction: column;
-//}
-//details.type-filters form.filters {
-// flex-direction: column;
-//}
-//
-//details.type-filters div.filters {
-// flex-wrap: wrap;
-//}
-//
-//details.type-filters .order-options {
-// width: 100%;
-// display: flex;
-//}
-//details.type-filters .order-options > div {
-// display: flex;
-//}
-//details.type-filters .order-by {
-// width: 100%;
-//}
-//details.type-filters .order-by .radio-group-label {
-// width: 100%;
-// display: flex;
-// gap: 1rem;
-//}
-//details.type-filters label {
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-//}
-//
-//details.type-filters label {
-// display: flex;
-// justify-content: center;
-// align-items: center;
-// padding: .35rem;
-// white-space: nowrap;
-// width: fit-content;
-// height: fit-content;
-// cursor: pointer;
-// border: 1px solid var(--base-200);
-// border-radius: 4px;
-// font-size: .875rem;
-// transition: border-color var(--trans-base);
-// margin-bottom: .5rem;
-//}
-//.filter-toggle .icon {
-// margin-right: .5rem;
-//}
-//details.type-filters label:hover,
-//details.type-filters input:checked + label {
-// background-color: var(--light-0);
-// border-color: var(--action-0);
-// color: var(--action-0);
-//}
-//details.type-filters .order-direction {
-// justify-content: flex-end;
-//}
-//
-///** Generalized Modal **/
-//
-///* Grid */
-//.create-item {
-// left: auto!important;
-// right: 1rem;
-// bottom: calc(var(--btn) + 1rem)!important;
-//}
-//body:has(.group-display:not([hidden])) button.create-item{
-// display: none;
-//}
-//.uploader .groups,
-//.item-grid {
-// --padding: 0;
-// padding: var(--padding);
-// transition: padding var(--trans-base);
-//}
-//.uploader .groups,
-//.item-grid:not(.list-view) {
-// display: grid;
-// grid-template-columns: repeat(2, 50%);
-// gap: .5rem;
-// margin-top: 2rem;
-//}
-//.item-grid.empty {
-// grid-template-columns: repeat(1, 1fr)!important;
-//}
-//.item-grid.empty div {
-// text-align: center;
-// border-radius: var(--radius);
-// background-color: var(--base-100);
-//}
-//.item-grid.empty h3 .icon {
-// display: inline-flex;
-// vertical-align: middle;
-// margin: 0 1rem;
-//}
-//.uploader .group,
-//.item {
-// width: calc(100% - (var(--padding) * 2));
-//}
-//.item-grid .item {
-// user-select: none;
-// -webkit-user-select: none;
-// -moz-user-select: none;
-// -ms-user-select: none;
-//}
-//.item-grid .item .logo {
-// --w: 100%;
-// opacity: .2;
-//}
-///** Grid View **/
-//.item-grid:not(.list-view) .item {
-// display: flex;
-// position: relative;
-// flex-direction: column;
-//}
-//.item-grid:not(.list-view) button.favourite,
-//.item-grid:not(.list-view) .item-select label {
-// position: absolute;
-// width: 3em;
-// height: 3em;
-// display: flex;
-// justify-content: center;
-// align-items: center;
-// top: .125rem;
-// padding: 0!important;
-// border-radius: var(--radius);
-// background-color: rgba(var(--base-rgb),var(--op-3));
-// color: var(--base-200);
-//}
-//.item-grid:not(.list-view) button.favourite:hover,
-//.item-grid:not(.list-view) .item-select label:hover {
-// background-color: rgba(var(--base-rgb),var(--op-6));
-// color: var(--contrast);
-//}
-//.item-grid:not(.list-view) .item-select label::before {
-// border-color: var(--base-200);
-// position: relative;
-// top: 0;
-// left: 0;
-// transform: none;
-//}
-//
-//.item-grid:not(.list-view) .item-select label::after {
-// left: calc(50% - 4px)!important;
-//}
-//.item-grid:not(.list-view) .item-select label:hover::before {
-// border-color: var(--contrast);
-//}
-//.item-grid:not(.list-view) .item-select label {
-// left: .1255rem;
-//}
-//.item-grid:not(.list-view) button.favourite {
-// right: .125rem;
-//}
-//.item-grid:not(.list-view) img {
-// width: 100%;
-// max-width: 100%;
-// height: auto;
-// aspect-ratio: 1;
-// object-fit: cover;
-//}
-///** List View **/
-//.replace:not(:has(.list-view)) button[data-view=grid],
-//.replace:has(.list-view) button[data-view=list] {
-// background-color: var(--base);
-//}
-//.item-grid.list-view .item {
-// display: flex;
-// position: relative;
-// flex-direction: row;
-// margin: .5rem 0;
-// padding: .25rem .5rem;
-// gap: .5rem;
-//}
-//.item-grid.list-view .item:nth-of-type(even){
-// background-color: var(--base-100);
-//}
-//
-//.item-grid.list-view .item-select label{
-// display: flex;
-// height: 100%;
-// justify-content: center;
-// align-items: center;
-// padding: 0 .5rem!important;
-//}
-//.item-grid.list-view .item-select label::after {
-// /*left: calc(50% - 4px)!important;*/
-//}
-//.item-grid.list-view .item-select label::before {
-// position: relative;
-// top: 0;
-// transform: none;
-//}
-//.item-grid.list-view .item-select label:hover {
-// background-color: var(--base);
-//}
-//
-//.item-grid.list-view .item-select label:hover::before {
-// border-color: var(--action-0);
-//}
-//.item-grid.list-view img {
-// width: 200px;
-// height: 200px;
-// aspect-ratio: 1;
-// object-fit: cover;
-//}
-//.item-grid.list-view .item-info {
-// width: 100%;
-//}
-//.item-grid .item-info h3 {
-// margin: 0!important;
-// text-align: right;
-// font-size: var(--txt-medium);
-// text-transform: none;
-//}
-//.item-grid .item-info a {
-// text-align: right;
-// width: 100%;
-// display: block;
-// font-weight: normal;
-// font-family: var(--body);
-//}
-//.item-grid .item-info a::before,
-//.item-grid .item-info a::after {
-// display: none;
-//}
-//.item-grid.list-view .item-info .toggle-notes {
-// display: none;
-//}
-//.item-grid.list-view .item-info .notes-content[hidden]{
-// display: block!important;
-//}
-//.item-grid.list-view .item-info .notes-content {
-// margin-top: 1rem;
-//}
-//.no-favourites {
-// text-align: center;
-//}
-//.no-favourites h3 {
-// margin: 0 auto!important;
-//}
-///**
-//Limit reached
-// */
-//.reached .current {
-// color: var(--action-50);
-//}
-//.reached textarea,
-//.reached input {
-// border: 2px solid var(--action-50);
-//}
-///**
-//Groups
-// */
-//.groups {
-// gap: 1rem;
-//}
-//details.uploaded .file-upload-container,
-//details:not(.uploaded) .selection-container {
-// display: none;
-//}
-//.uploading + .selection-container {
-// display: flex!important;
-//}
-//.selection-container #save-changes {
-// position: sticky;
-// top: 3rem;
-// left: 100%;
-// border: 1px solid transparent;
-// background-color: var(--action-50);
-// box-shadow:rgba(var(--base-rgb),var(--op-45)) var(--shdw);
-// z-index: 5;
-//}
-//.selection-container #save-changes:hover {
-// background-color: var(--base);
-// border: 1px solid var(--action-50);
-// color: 1px solid var(--action-50);
-//}
-//
-//.group {
-// padding: 1rem .66rem;
-// background-color: var(--base-50);
-// border-radius: var(--radius-outer);
-//}
-//.group.empty {
-// aspect-ratio: 1;
-// display: flex;
-// flex-direction: column;
-// align-items: center;
-// justify-content: center;
-// border: 4px dashed var(--base-200);
-//}
-//.group.empty .items {
-// padding: 0;
-// margin-top: 0;
-//}
-//
-//.group .group-header {
-// display: flex;
-// position: relative;
-//}
-//.group .group-header label {
-// position: absolute;
-// top: -1.5rem;
-//}
-//.group .items {
-// margin-top: 1rem;
-// padding: 1rem;
-// border-radius: var(--radius);
-// background-color: var(--base);
-//}
-//.group .item-actions {
-// display: flex;
-// justify-content: space-between;
-// width: 100%;
-//}
-//.group .item-actions > button,
-//.group .item-actions label {
-// flex: 1;
-// padding: .25em!important;
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// margin-bottom: 0;
-//}
-//.group .item-actions :checked + label {
-// background-color: var(--action-50);
-// color: var(--contrast);
-//}
-//.group .item-actions :checked + label:hover {
-// border: 1px solid var(--action-50);
-// background-color: transparent;
-// color: var(--action-50);
-//}
-//.group .item-actions label::before {
-// display: none!important;
-//}
-//
-//.group-item img,
-//.preview-item img {
-// width: 250px;
-// height: auto;
-// object-fit: cover;
-// aspect-ratio: 1;
-//
-//}
-//.uploading .preview-item img,
-//.uploading .group-item img{
-// animation: uploading 3s ease-in-out infinite alternate;
-//}
-//@keyframes uploading {
-// 0% {
-// opacity: .6;
-// filter: sepia(.3) grayscale(.3);
-// transform: scale(.9);
-// }
-// 100% {
-// opacity: 1;
-// filter: sepia(0) grayscale(0);
-// transform: scale(1);
-// }
-//}
-//
-//.image-actions {
-// display: flex;
-// gap: 1rem;
-// flex-wrap: wrap;
-//}
-//
-//.group.drop {
-// border: 2px dashed var(--action-0);
-// background: var(--action-rgb-subtle);
-//}
-//
-//.group .items {
-// padding: 1rem;
-// display: grid;
-// grid-template-columns: repeat(3, 1fr);
-// gap: 1rem;
-//}
-//
-//.group-content {
-// min-height: 100px;
-// padding: 1rem;
-//}
-//
-//.group.drop {
-// background: var(--action-rgb-subtle);
-// border: 2px dashed var(--action-0);
-//}
-//.gallery .group-item,
-//.gallery .preview-item {
-// cursor: grab;
-//}
-//.group-item.dragging,
-//.preview-item.dragging {
-// cursor: grabbing;
-// opacity: .5;
-//}
-//
-//.items {
-// display: grid;
-// grid-template-columns: repeat(3,1fr);
-// gap: 1rem;
-//}
-//
-///** Gallery **/
-//.gallery .preview-item .move-image {
-// cursor: grab;
-// position: absolute;
-// top: .5rem;
-// left: .5rem;
-// opacity: 0;
-// transition: opacity .2s;
-//}
-//.gallery-preview {
-// display: grid;
-// grid-template-columns: repeat(3, 1fr);
-// padding: .5rem;
-// background-color: var(--base-100);
-// border-radius: var(--radius-outer);
-//}
-//.gallery-preview .preview-item {
-// padding: .5rem;
-// background-color: var(--base);
-// border-radius: var(--radius);
-//}
-//
-//.gallery .preview-item:hover .move-image {
-// opacity: 1;
-//}
-//
-//.gallery .preview-item.sortable-ghost {
-// opacity: .5;
-//}
-//
-//.gallery .preview-item.sortable-drag {
-// cursor: grabbing;
-//}
-//
-//.preview-item .upload-status {
-// position: absolute;
-// bottom: 0;
-// left: 0;
-// right: 0;
-// height: 4px;
-// background: rgba(0,0,0,.1);
-//}
-//
-//.preview-item.error {
-// border-color: red;
-//}
-//
-//.preview-item.complete .upload-status {
-// display: none;
-//}
-//
-///** Settings **/
-//.notification-table {
-// width: 100%;
-//}
-//.notification-table tr td:first-of-type {
-// width: 100%;
-//}
-//.notification-table td {
-// vertical-align: middle;
-// padding: 0 .5rem;
-//}
-//.notification-table td + td {
-// text-align: right;
-//}
-//table tr:nth-of-type(even){
-// background-color: var(--base-200);
-//}
-//thead th {
-// width: 50%;
-//}
-//thead tr,
-//tfoot tr {
-// background-color: var(--base);
-// text-transform: uppercase;
-// padding: .5rem 0;
-// line-height: 2;
-// font-weight: normal;
-//}
-//tfoot th {
-// vertical-align: middle;
-//}
-//tfoot th:first-of-type {
-// text-align: right;
-//}
-//@media (max-width: 767px){
-// table .radio-options {
-// display: grid;
-// grid-template-columns: repeat(2, 1fr);
-// }
-// table .radio-options label {
-// width: 85%;
-// margin-bottom: 0;
-// }
-//}
-//
-///** Favourites Lists **/
-//.list-card {
-// background-color: var(--base-50);
-// padding: 1rem;
-// border-radius: var(--radius);
-//}
-//.list-header {
-// display: flex;
-// justify-content: space-between;
-//}
-//h2 .icon {
-// --w: 1.5em;
-// display: inline-block;
-// vertical-align: middle;
-// margin-right: .75rem;
-//}
-//.list-card h3,
-//.list-header h2 {
-// margin: 0!important;
-// font-size: var(--txt-large);
-//}
-//.list-actions {
-// display: flex;
-// justify-content: space-between;
-// gap: .5rem;
-//}
-//.list-header + .list-actions {
-// justify-content: flex-end;
-//}
-//.create-list-btn {
-// font-size: var(--txt-x-small);
-//}
-//.meta-stats {
-// display: flex;
-// justify-content: space-between;
-// font-style: italic;
-//}
-//
-///** Repeater **/
-//.add-repeater-row {
-// margin-left: auto;
-// border: 1px solid var(--contrast-200);
-//}
-//
-///** Image **/
-//
-//.image-display {
-// max-height: 0;
-// overflow: hidden;
-// transform: scaleY(0);
-// transition: max-height var(--trans-t) var(--trans-fn);
-// transition-property: max-height, transform;
-//}
-//.image-display.has-image {
-// max-height: 100%;
-// transform: scaleY(1);
-// transition: max-height var(--trans-t) var(--trans-fn);
-// transition-property: max-height, transform;
-//}
-//.file-upload-container {
-// margin-top: 1rem;
-//}
-//
-//.file-upload-wrapper {
-// border: 2px dashed var(--action-0);
-// border-radius: 4px;
-// padding: 2rem;
-// text-align: center;
-// transition: all .3s ease;
-// background: var(--action-rgb-subtle);
-// position: relative;
-// cursor: pointer;
-//}
-//.file-upload-wrapper h2 {
-// margin: 0!important;
-// font-size: var(--txt-large);
-//}
-//
-//.file-upload-wrapper:hover,
-//.dragover {
-// background: var(--action-rgb-subtle-hover);
-// border-color: var(--action-0)!important;
-//}
-//
-//.file-upload-wrapper input[type="file"] {
-// position: absolute;
-// left: 0;
-// top: 0;
-// width: 100%;
-// height: 100%;
-// opacity: 0;
-// cursor: pointer;
-//}
-//
-//.file-upload-text {
-// color: var(--contrast);
-// margin: 0;
-// font-family: var(--body);
-//}
-//
-//.file-upload-text strong {
-// color: var(--action-0);
-// text-decoration: underline;
-//}
-//
-///* Error state */
-//.file-error {
-// color: var(--action-0);
-// margin-top: .5rem;
-// font-size: .9em;
-//}
-//
-//.file-upload-container.uploading,
-//.image-display.has-image + .file-upload-container {
-// max-height: 0;
-// overflow: hidden;
-// transform: scaleY(0);
-// transition: max-height var(--trans-t) var(--trans-fn), transform var(--trans-t) var(--trans-fn);
-// transition-property: max-height, transform;
-//}
-//
-///** Highlighted Taxonomy **/
-//.highlight-options {
-// display: grid;
-// grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
-// gap: .5rem;
-// margin-top: 1rem;
-//}
-//
-//.highlight-option {
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-// padding: .5rem;
-// border: 1px solid #ddd;
-// border-radius: 4px;
-//}
-//.highlight-option label {
-// margin-bottom: 0;
-// font-weight: normal;
-//}
-//
-//.highlight-option input[type=checkbox]:disabled + label {
-// opacity: .5;
-// cursor: not-allowed;
-//}
-//
-///** Item Selection **/
-//
-//.item-grid {
-// --padding: 0;
-// padding: var(--padding);
-//}
-//.item-grid.selecting {
-// --padding: .5rem;
-// transition: none;
-// background-color: var(--base);
-//}
-//.item {
-// --padding: 0;
-// height: fit-content;
-// padding: var(--padding);
-// max-width: calc(100% - (var(--padding) * 2));
-// transition: padding var(--trans-base);
-//}
-//.selecting .item {
-// opacity: .666;
-//}
-//.selecting .item:has(.select-checkbox:checked) {
-// --padding: .5rem;
-// opacity: 1;
-// background-color: var(--action-0);
-// transition: none;
-//}
-//
-//
-//.item-grid.preview .preview-item {
-// display: flex;
-// gap: 2rem;
-//}
-//.preview-item .preview-image {
-// width: 36%;
-//}
-//.preview-item .remove-file {
-// width: 100%;
-// background-color: var(--base);
-// color: var(--contrast);
-//}
-//.preview-item .field:first-of-type {
-// margin-top: 0!important;
-//}
-//.preview-item + .preview-item {
-// margin-top: 2rem;
-// padding-top: 2rem;
-// border-top: 2px solid var(--contrast-200);
-//}
-//
-//.item-grid.preview:empty + .hint {
-// display: none;
-//}
-//
-//body:has(.item-grid.empty) .bulk-controls {
-// display: none;
-//}
-//.bulk-controls {
-// margin: .75rem 0;
-// display: flex;
-// justify-content: space-between;
-// align-items: center;
-//}
-//.bulk-select {
-// display: flex;
-// justify-content: space-between;
-//}
-//
-//.bulk-actions[hidden]{
-// max-width: 0;
-// max-height: 0;
-// overflow: hidden;
-// transform: scaleY(0);
-// transition: transform var(--trans-t) var(--trans-fn);
-// transition-property: transform, max-height;
-// transform-origin: top;
-// display: flex!important;
-// align-items: center;
-// gap: .5rem;
-// position: relative;
-//}
-//
-//.bulk-actions {
-// max-width: 100%;
-// max-height: 100%;
-// transform: scaleY(1);
-// transition: transform var(--trans-t) var(--trans-fn);
-// transition-property: transform, max-height;
-// overflow:visible;
-// transform-origin: top;
-// display: flex;
-// gap: .25rem;
-//}
-//.bulk-actions select {
-// margin-right: .5rem;
-//}
-//
-//.selected-count {
-// font-size: var(--txt-x-small);
-// font-style: italic;
-// font-weight: normal;
-// margin-left: 1rem;
-//}
-//
-//.input-with-button {
-// display: flex;
-// gap: .5rem;
-//}
-//.input-with-button .icon {
-// --w: 1.5em;
-//}
-///** Single Edit Modal **/
-//dialog > form .field {
-// margin: 2rem 0;
-//}
-//.edit-modal [data-field=featured_image] img {
-// width: 100%;
-// height: 100%;
-// object-fit: cover;
-// aspect-ratio: 1;
-//}
-//
-///** Bulk Edit Modal **/
-//.bulk-edit-modal .selected {
-// display: grid;
-// grid-template-columns: repeat(5, 1fr);
-// gap: .5rem;
-// background-color: var(--base);
-// padding: .5rem;
-// border-radius: var(--radius-outer);
-//}
-//.bulk-edit-modal .selected input[type=checkbox] {
-// position: absolute;
-//}
-//.bulk-edit-modal .selected label::before,
-//.bulk-edit-modal .selected label::after {
-// display: none;
-//}
-//.bulk-edit-modal .selected label {
-// opacity: .5;
-// padding: .5rem!important;
-// aspect-ratio: 1;
-// margin-bottom: 0;
-// max-width: 5rem;
-// height: auto;
-// cursor: pointer;
-//}
-//.bulk-edit-modal .selected label:has(input:checked) {
-// opacity: 1;
-// padding: 0!important;
-//}
-//
-//.bulk-edit-modal .selected img {
-// max-width: 100%;
-// object-fit: cover;
-// aspect-ratio: 1;
-// border-radius: var(--radius);
-//}
-//
-//dialog .term-list {
-// display: block;
-//}
-//.pagination-info {
-// position: sticky;
-// background-color: rgba(var(--base-rgb),var(--op-6));
-// top: 0;
-//}
-//.pagination-info:empty {
-// display: none;
-//}
-//
-///** Quill Stuff **/
-//.editor-container {
-//
-//}
-//.editor-container .ql-toolbar {
-// display: flex;
-// justify-content: flex-start;
-// flex-wrap: wrap;
-// padding: .25rem;
-// gap: 1rem;
-// background-color: var(--base);
-// border-top-left-radius: var(--radius);
-// border-top-right-radius: var(--radius);
-// border-bottom: 4px solid var(--base-50);
-//}
-//.ql-toolbar .ql-formats {
-// display: flex;
-// gap: .25rem;
-//}
-//.editor-container .ql-container {
-// --padding: 1rem;
-// background-color: var(--base);
-// border-bottom-left-radius: var(--radius);
-// border-bottom-right-radius: var(--radius);
-// height: fit-content;
-// padding: 2px;
-//}
-//.editor-container .ql-container .ql-editor {
-// padding: var(--padding);
-// width: calc(100% - (var(--padding) * 2.5));
-// height: calc(100% - (var(--padding) * 2));
-//}
-//.ql-editor img {
-// max-width: 50%;
-// height: auto;
-//}
-//
-//.ql-clipboard {
-// left: -100000px;
-// height: 1px;
-// overflow-y: hidden;
-// position: absolute;
-// top: 50%;
-//}
-//.ql-hidden {
-// display: none;
-//}
-//.ql-tooltip {
-// position: absolute;
-// transform: translateY(10px);
-// background-color: var(--base-100);
-// border: 1px solid var(--base);
-// box-shadow: 0px 0px 5px rgba(var(--base-rgb),var(--op-6));
-// color: var(--contrast);
-// padding: 5px 12px;
-// white-space: nowrap;
-//}
-//
-//#toolbar-einks_short_bio .ql-e_image {
-// display: none;
-//}
-//
-///** Content Grid **/
-//.all-filters {
-// position: relative;
-// background-color: var(--base);
-// border-radius: var(--radius-outer);
-// padding: .5rem;
-// display: flex;
-// flex-direction: column;
-// gap: .5rem;
-//}
-//.controls .radio-options {
-// gap: .25rem 1rem;
-//}
-//.radio-options details {
-// width: 100%;
-//}
-//.radio-options input:not(.ch) + label,
-//.controls input:not(.ch) + .radio-options label {
-// width: fit-content!important;
-// gap: 0;
-// margin: 0;
-// display: flex;
-// padding: .25rem .5rem!important;
-// align-items: center;
-//}
-//
-//.radio-options label span {
-// transform: scaleX(0);
-// max-width: 0;
-//}
-//.edit-form .radio-options label span,
-//.radio-options label:hover span,
-//.radio-options :checked + label span {
-// transform: scaleX(1);
-// max-width: 100%;
-//}
-//.radio-options input:not(.ch):checked + label {
-// padding: .25rem .5rem!important;
-//}
-//.controls .radio-options input:not(.ch):checked+label {
-// background-color: var(--base-100);
-// border-color: var(--contrast-200);
-// color: var(--contrast)!important;
-// gap: .5rem;
-//}
-//
-//dialog .field {
-// margin: 3rem 0;
-//}
-//
-//
-//.filters {
-// display: flex;
-// gap: 1rem;
-// align-items: center;
-//}
-//.filters > div > label {
-// margin: 0;
-//}
-//div.filters > div {
-// display: flex;
-// align-items: center;
-// flex: 100%;
-// gap: .5rem;
-//}
-//.filters .icon {
-// --w: 1.5em;
-//}
-//
-//@media (min-width:768px){
-// div.filters > div {
-// flex: 1;
-// }
-//}
-//
-//.view-controls.radio-options {
-// gap: .5rem;
-// margin-left: auto;
-//}
-//.view-controls.radio-options label {
-// padding: .25rem!important;
-//}
-//
-//.label-group {
-// display: flex;
-// gap: .75rem;
-// align-items: center;
-// flex-wrap: wrap;
-//}
-//.label-group .icon {
-// margin-right: .5rem;
-//}
-//.item-grid.table {
-// display: none;
-//}
-//
-//.item-grid summary {
-// padding: 0;
-// gap: 0;
-//}
-//.item-grid .item-actions {
-// position: absolute;
-// top: .25rem;
-// right: .25rem;
-// gap: .25rem;
-// display: flex;
-//}
-//.item-grid .item-actions button {
-// width: 2em;
-// height: 2em;
-// border-radius: var(--radius);
-// background-color: rgba(var(--base-rgb),var(--op-3));
-// display: flex;
-// justify-content: center;
-// align-items: center;
-//}
-//.item-grid .item-actions button:focus,
-//.item-grid .item-actions button:hover {
-// background-color: rgba(var(--base-rgb),var(--op-6));
-// color: var(--action-0);
-//}
-//
-//
-//
-//@media (max-width: 768px){
-// .replace {
-// margin-top: 4rem!important;
-// }
-// .list-header {
-// flex-wrap: wrap;
-// }
-// .list-card h3 {
-// font-size: var(--txt-medium);
-// }
-// .item-grid.list-view .item {
-// align-items: center;
-// }
-// .item-grid.list-view img {
-// width: 80px;
-// height: 80px;
-// aspect-ratio: 1;
-// object-fit: cover;
-// }
-//}
-//
-//@media (min-width: 768px){
-// .item-grid:not(.list-view) {
-// grid-template-columns: repeat(3, 1fr);
-// }
-//}
-//
-//.term-divider {
-// position: relative;
-// text-align: center;
-// margin: 1rem 0;
-// border-bottom: 1px solid var(--base-200);
-//}
-//
-//.term-divider span {
-// background: var(--base);
-// padding: 0 1rem;
-// color: var(--contrast);
-// font-size: .9rem;
-// position: relative;
-// top: .5em;
-//}
-//
-//.common-term {
-// background: var(--base-50);
-// border-radius: var(--radius);
-//}
-//
-//.loading-indicator {
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// gap: .5rem;
-// padding: 1rem;
-// color: var(--contrast-100);
-// font-size: .9rem;
-//}
-//
-//.loading-indicator svg {
-// animation: spin 1s linear infinite;
-//}
-//
-//.pagination-info {
-// text-align: center;
-// padding: .5rem;
-// font-size: .9rem;
-// color: var(--contrast-100);
-// border-top: 1px solid var(--base-100);
-//}
-//
-//@keyframes spin {
-// from { transform: rotate(0deg); }
-// to { transform: rotate(360deg); }
-//}
-//
-//
-//.term-breadcrumb {
-// margin-bottom: 1rem;
-// padding: .5rem;
-// background: var(--base-50);
-// border-radius: 4px;
-//}
-//
-//.back-to-parent {
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-// border: none;
-// background: none;
-// color: var(--contrast);
-// cursor: pointer;
-// padding: .5rem;
-// border-radius: 4px;
-// font-size: var(--txt-x-small);
-//}
-//
-//.back-to-parent:hover {
-// background: var(--base-100);
-//}
-//
-//.term-row {
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-// width: 100%;
-// padding: .25rem 0;
-//}
-//
-//.toggle-children {
-// border: none;
-// background: none;
-// padding: .25rem;
-// cursor: pointer;
-// color: var(--contrast);
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// margin-left: auto;
-// border-radius: 4px;
-//}
-//
-//.toggle-children:hover {
-// background: var(--base-50);
-//}
-//
-//.loading-indicator {
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// width: 24px;
-// height: 24px;
-//}
-//
-//.loading-indicator .loading {
-// width: 16px;
-// height: 16px;
-// border: 2px solid var(--base-100);
-// border-top-color: var(--contrast);
-// border-radius: 50%;
-// animation: spin 1s linear infinite;
-//}
-//
-//@keyframes spin {
-// to { transform: rotate(360deg); }
-//}
-//
-//.term-breadcrumb {
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-// margin-bottom: 1rem;
-// padding: .5rem;
-// background: var(--base-50);
-// border-radius: 4px;
-//}
-//
-//.term-breadcrumb .path {
-// display: flex;
-// align-items: center;
-// gap: .25rem;
-// flex-wrap: wrap;
-//}
-//
-//.term-breadcrumb button {
-// border: none;
-// background: none;
-// padding: .25rem .5rem;
-// border-radius: 4px;
-// cursor: pointer;
-// color: var(--contrast);
-// font-size: var(--txt-x-small);
-//}
-//
-//.term-breadcrumb button:hover {
-// background: var(--base-100);
-//}
-//
-//.path-separator {
-// color: var(--contrast-50);
-//}
-//
-//.path-level {
-// white-space: nowrap;
-//}
-//
-//.create-new-term {
-// margin-top: 2rem;
-// padding-top: 1rem;
-// border-top: 1px solid var(--base-100);
-//}
-//.create-new-term button {
-// width: 100%;
-//}
-//
-//.suggestion-prompt {
-// font-size: var(--txt-x-small);
-// color: var(--contrast-50);
-// margin-bottom: 1rem;
-//}
-//
-//.create-term-form {
-// display: flex;
-// flex-direction: column;
-// gap: .5rem;
-//}
-//
-//.form-row {
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-//}
-//
-//.name-row {
-// position: relative;
-//}
-//
-//.name-row input {
-// width: 100%!important;
-// padding: .5rem;
-// border: 2px solid var(--base-100);
-// border-radius: 4px;
-// background: var(--base);
-// color: var(--contrast);
-//}
-//
-//.name-row input:focus {
-// border-color: var(--action-0);
-// outline: none;
-//}
-//
-//.parent-row {
-// font-size: var(--txt-x-small);
-//}
-//
-//.parent-row label {
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-// cursor: pointer;
-//}
-//
-//.create-term-form button {
-// display: inline-flex;
-// align-items: center;
-// gap: .5rem;
-// padding: .5rem 1rem;
-// border: none;
-// border-radius: 4px;
-// background: var(--action-0);
-// color: var(--base);
-// cursor: pointer;
-// font-size: var(--txt-x-small);
-// transition: all .2s ease;
-//}
-//
-//.create-term-form button:hover {
-// background: var(--action-50);
-//}
-//
-//.create-term-form button:disabled {
-// opacity: .5;
-// cursor: not-allowed;
-//}
-//
-//.create-term-form button svg {
-// width: 16px;
-// height: 16px;
-//}
-//
-///** Search bar and new term functionality **/
-//.search-bar {
-// position: relative;
-// display: flex;
-// gap: .5rem;
-// align-items: center;
-// z-index: 2;
-//}
-//
-//.search-bar input {
-// flex: 1;
-// /* Make room for button */
-// padding: .5rem 2.5rem .5rem .5rem;
-// border: 2px solid #ddd;
-// border-radius: 4px;
-// font-size: 1rem;
-//}
-//
-//.new-term-toggle {
-// margin: 2rem 0 1rem;
-// width: 100%;
-// display: flex;
-// justify-content: center;
-// align-items: center;
-//}
-//
-///* Peek effect on search bar hover */
-//.search-bar:hover .new-term-toggle {
-// opacity: 1;
-// transform: translateX(-.25rem);
-//}
-//
-///* Form styling */
-//.create-term-form {
-// position: relative;
-// z-index: 1;
-// background: var(--base);
-// border: 2px solid var(--action-0);
-// border-radius: 4px;
-// padding: 1rem;
-// margin-top: -2px;
-//
-// /* Animation */
-// transform-origin: top;
-// transition: all .2s var(--trans-fn);
-//}
-//
-//.create-term-form:not([hidden]) {
-// animation: slideDown .2s var(--trans-fn);
-//}
-//
-//.create-term-form[hidden] {
-// display: block !important; /* Override hidden */
-// transform: scaleY(0);
-// opacity: 0;
-// pointer-events: none;
-//}
-//
-//.form-row {
-// display: flex;
-// gap: .5rem;
-// align-items: center;
-//}
-//
-//.form-row + .form-row {
-// margin-top: .5rem;
-//}
-//
-//@keyframes slideDown {
-// from {
-// transform: scaleY(0);
-// opacity: 0;
-// }
-// to {
-// transform: scaleY(1);
-// opacity: 1;
-// }
-//}
-//
-//.news.item {
-// padding: 1rem!important;
-//}
-//
-//.dashboard .checkbox-options label {
-// font-weight: normal;
-//}
-//
-//.description.space {
-// margin-bottom: 4rem;
-//}
-//
-//.invited-artist {
-// display: flex;
-// align-items: center;
-// margin: 1rem 0;
-//}
-//.invited-artist input[type=text],
-//.invited-artist input[type=email] {
-// width: calc(100% - .5rem);
-//}
-//.invited-artist label {
-// margin: 0;
-// font-weight: normal;
-// white-space:nowrap;
-//}
-//.invited-artist button {
-// height: fit-content;
-// margin-right: .5rem;
-//}
-//.actions {
-// display: flex;
-// gap: 1rem;
-// justify-content: flex-end;
-//}
-//
-//.actions .send-invites {
-// background-color: var(--action-50);
-//}
-//.actions .send-invites:hover,
-//.actions .send-invites:focus {
-// background-color: var(--base);
-// border-color: var(--action-50);
-//}
-//
-//.dashboard .queue-status-panel {
-// bottom: calc(var(--btn) + 1rem);
-//}
-//.dashboard .queue-status-toggle {
-// bottom: 0;
-//}
-//
-//.items-list table th {
-// padding: 0 .75rem;
-// text-transform: none!important;
-//}
-//.items-list table.empty tbody {
-// height: 40vh;
-//}
-//
-//table textarea,
-//table input[type=text],
-//table input[type=url],
-//table input[type=number],
-//table input[type=email] {
-// min-width: 40vw;
-//}
-//
-//.empty tfoot {
-// display: none;
-//}
-//
-//p.hint {
-// margin: 0 0 .5rem 0;
-// font-size: var(--txt-x-small);
-// font-style: italic;
-//}
-//.item-grid + .hint {
-// text-align: center;
-// margin-top: 2rem;
-//}
-//
-//.image.field[data-type=single]:has(.upload-item) .file-upload-container {
-// display: none;
-//}
-//.image.field[data-type=single] .upload-item {
-// grid-column: 1 / -1;
-//}
-///** UPLOADER **/
-//.upload-item {
-// position: relative;
-// border: 1px solid var(--base-200);
-// border-radius: 8px;
-// background: var(--base-50);
-// transition: all .3s ease;
-//}
-//.upload-item:hover {
-// box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw);
-// transform: translateY(-2px);
-//}
-//.upload-item[data-status=processing] {
-// border-color: var(--action-200);
-// background: var(--action-100);
-//}
-//.upload-item[data-status=cached] {
-// border-color: var(--secondary-0);
-// background: var(--secondary-200);
-//}
-//.upload-item[data-status=uploading] {
-// border-color: var(--contrast);
-// background-color: var(--base-50);
-//}
-//.upload-item .preview {
-// position: relative;
-// aspect-ratio: 1;
-//}
-//.upload-item .status {
-// position: absolute;
-// bottom: .25rem;
-// right: .25rem;
-// background-color: rgba(var(--base-rgb),var(--op-3));
-// box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw);
-// border-radius: 50%;
-//}
-//.upload-item img {
-// width: 100%;
-// height: 100%;
-// object-fit: cover;
-// transition: transform .3s ease;
-//}
-//.upload-item:hover img {
-// transform: scale(1.05);
-//}
-//
-//.upload-item .overlay {
-// position: absolute;
-// top: 0;
-// left: 0;
-// right: 0;
-// bottom: 0;
-// background: rgba(var(--base-rgb),var(--op-6));
-// display: flex;
-// flex-direction: column;
-// justify-content: space-between;
-// padding: .5rem;
-// opacity: 0;
-// transition: opacity .3s ease;
-//}
-//.upload-item:hover .overlay,
-//.upload-item[data-status=processing] .overlay,
-//.upload-item[data-status=uploading] .overlay {
-// opacity: 1;
-//}
-//
-//.upload-item .item-actions {
-// position: relative;
-//}
-//
-//.submit-uploads {
-// position: fixed;
-// bottom: calc(var(--btn) + 1rem);
-// right: 1rem;
-// background-color: var(--base);
-// height: var(--btn);
-// box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw);
-//}
-///*** UPLOADER GROUPS ***/
-//.group-display {
-// display: grid;
-// grid-template-columns: 1fr 250px;
-// gap: 2rem;
-//}
-//.preview-actions {
-// position: sticky;
-// padding: .5rem;
-// top: calc(var(--btn) + .25rem);
-// left: 0;
-// background-color: var(--base-50);
-// z-index: 5;
-//}
-//.preview-actions .selected {
-// display: flex;
-// justify-content: space-between;
-// padding-right: 2rem;
-// align-items: center;
-//}
-//.preview-actions .field {
-// margin: 0;
-//}
-//.preview-actions .field label {
-// margin-bottom: 0;
-//}
-//.selection-actions {
-// display: flex;
-// justify-content: space-between;
-// padding: 1rem 2rem;
-//}
-//[data-status=completed] .progress {
-// transform: scaleY(0);
-// transform-origin: top;
-//}
-//.preview-grid {
-// display: grid;
-// grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
-// gap: 1rem;
-// margin-top: 1rem;
-// padding: .5rem;
-// background: var(--base);
-// border: 1px solid var(--contrast-200);
-// border-radius: 8px;
-// min-height: 60px;
-//}
-//.preview-grid:empty {
-// display: none;
-//}
-//
-//
-//.upload-item .progress {
-// position: absolute;
-// top: 2px;
-// right: 2px;
-// left: 2px;
-//}
-//.upload-item .actions {
-// display: flex;
-// justify-content: space-between;
-// align-items: center;
-// position: absolute;
-// top: 0;
-// left: 0;
-// right: 0;
-//}
-//.upload-item .actions > label {
-// margin: 0 1rem;
-// padding-left: 0!important;
-//}
-//.upload-item input[type=text],
-//.upload-item textarea,
-//.group-fields input[type=text],
-//.group-fields textarea {
-// width: calc(100% - 1rem);
-//}
-///*.upload-item .actions > label {*/
-///* !*position: absolute;*!*/
-///* padding: 1rem!important;*/
-///* margin: 0;*/
-///* !*top: .5rem;*!*/
-///* !*left: 0;*!*/
-///*}*/
-///* .upload-item .actions > label::before {*/
-///* left: calc(50% - 9px)!important;*/
-///* top: calc(50% - 9px)!important;*/
-///* transform: none!important;*/
-///* }*/
-///* .upload-item .actions > label::after {*/
-///* left: 1.25rem!important;*/
-///* top: 1rem!important;*/
-///* }*/
-//.item-grid .upload-item summary {
-// display: flex;
-// align-items: center;
-// font-size: var(--txt-x-small);
-// gap: .5rem;
-// text-transform: uppercase;
-//}
-//.upload-item:has([type=checkbox]:checked) .preview {
-// padding: 1rem;
-// background-color: var(--secondary-200);
-//}
-//.upload-item:has([open]) {
-// grid-column: 1 / -1;
-//}
-//.upload-item summary .icon {
-// --w: 1.1em;
-//}
-//
-//[draggable="true"] {
-// cursor: grab;
-//}
-//[draggable="true"]:active {
-// cursor: grabbing;
-//}
-//
-//.dragging {
-// opacity: .5;
-// transform: rotate(5deg) scale(.95);
-// transition: all .2s ease;
-//}
-//
-//.dragover:not(.item-grid.groups) {
-// background-color: var(--overlay-action-medium);
-// border: 2px dashed var(--action-0);
-//}
-//
-//@keyframes dragHover {
-// 0% { transform: scale(1); }
-// 50% { transform: scale(1.02); }
-// 100% { transform: scale(1); }
-//}
-//
-//.drag-hover {
-// animation: dragHover .6s ease-in-out infinite;
-//}
-//
-///** GROUP **/
-//.empty-group {
-// padding: 20px;
-// text-align: center;
-// grid-column: 1 / -1;
-// display: flex;
-// justify-content: center;
-// align-items: center;
-// border: 2px dashed var(--action-200);
-// border-radius: var(--radius);
-// margin: 10px 0;
-// cursor: pointer;
-// transition: all .2s ease;
-// aspect-ratio: 16/9;
-//}
-//.item-grid.groups {
-// grid-template-columns: 1fr;
-//}
-//
-//.item-grid.group .item-grid.group {
-// min-height: 40px;
-// display: grid;
-// grid-template-columns: repeat(3, 1fr);
-// gap: .5rem;
-// border: 2px dashed var(--action-200);
-// border-radius: var(--radius);
-// margin: 10px 0;
-// cursor: pointer;
-// transition: all .2s ease;
-// aspect-ratio: 1;
-// text-align: center;
-//}
-//.empty-group:hover,
-//.empty-group.dragover,
-//.item-grid.group .item-grid.group:hover,
-//.item-grid.group .item-grid.group.dragover {
-// border-color: var(--action-0);
-// background-color: var(--overlay-action-light);
-// color: var(--action-50);
-//}
-//
-//.upload-group {
-// background-color: var(--base-100);
-// border-radius: var(--radius);
-// border: 1px solid var(--contrast-200);
-//}
-//.group-actions {
-// width: 100%;
-// gap: .5rem;
-//}
-//.group-actions button {
-// width: 100%;
-//}
-//
-//body:has(.selection-actions[hidden]) button.create-group,
-//body:has(.selection-actions[hidden]) button.add-selection-to-group {
-// display: none;
-//}
-//
-///** RESTORE FROM CACHE **/
-//.restore-notification {
-// border-radius: var(--radius);
-// box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw);
-// padding: 1rem;
-// background: var(--base-200);
-// border: 1px solid var(--contrast-200);
-// border-top-width: 4px;
-// border-bottom-width: 4px;
-//}
-//.restore-content {
-// display: flex;
-// align-items: center;
-//}
-//.restore-actions .selection-controls {
-// display: flex;
-// gap: .5rem;
-//}
-//
-//.restore-actions button {
-// white-space: nowrap;
-//}
-//.restore-actions .action-buttons {
-// margin-top: 1rem;
-// display: flex;
-// flex-direction: column;
-// gap: .5rem;
-//}
-//.restore-actions .action-buttons button {
-// width: 100%;
-//}
-//
-//label.restore-item {
-// border-radius: .5rem;
-// padding: 0;
-// background-color: transparent;
-// filter: grayscale(50);
-// opacity: .8;
-// transition: padding var(--trans-base);
-// transition-property: padding, background-color;
-// cursor: pointer;
-//}
-//label.restore-item:has(input:checked) {
-// filter: grayscale(0);
-// opacity: 1;
-// padding: 1rem;
-// background-color: var(--secondary-200);
-//}
-//
-//.upload-item .featured + label {
-// width: 2em;
-// height: 2em;
-// border-radius: var(--radius);
-// background-color: rgba(var(--base-rgb),var(--op-3));
-// display: flex;
-// justify-content: center;
-// align-items: center;
-// padding: 0!important;
-// border: none;
-//}
-//
-//input.featured + label .icon {
-// --w: 1.25em;
-//}
-//input.featured + label .icon + .icon {
-// position: absolute;
-//}
-//input.featured + label .star-fill,
-//input.featured:checked + label .star {
-// transform: scale(0);
-//}
-//input.featured:checked + label .star-fill,
-//input.featured + label .star {
-// transform: scale(1);
-//}
-///**TODO: Double check needed **/
-///*!* Centralized Upload Manager CSS *!*/
-//
-///*!* Global Upload Status Bar *!*/
-///*.all-uploads {*/
-///* position: fixed;*/
-///* top: 0;*/
-///* left: 0;*/
-///* right: 0;*/
-///* background: var(--surface);*/
-///* border-bottom: 1px solid var(--border);*/
-///* padding: .75rem 1rem;*/
-///* z-index: 1000;*/
-///* box-shadow: 0 2px 8px rgba(0, 0, 0, .1);*/
-///* backdrop-filter: blur(10px);*/
-///* display: none; !* Hidden by default *!*/
-///*}*/
-//
-///*.upload-summary {*/
-///* display: flex;*/
-///* justify-content: space-between;*/
-///* align-items: center;*/
-///* margin-bottom: .5rem;*/
-///*}*/
-//
-///*.all-uploads .active {*/
-///* font-weight: 500;*/
-///* color: var(--contrast);*/
-///* font-size: .9rem;*/
-///*}*/
-//
-///*.upload-summary button {*/
-///* padding: .5rem 1rem;*/
-///* border: 1px solid var(--border);*/
-///* border-radius: 4px;*/
-///* background: var(--surface);*/
-///* color: var(--contrast);*/
-///* cursor: pointer;*/
-///* transition: all .3s ease;*/
-///* font-size: .875rem;*/
-///* margin-left: .5rem;*/
-///*}*/
-//
-///*.upload-summary button:hover {*/
-///* background: var(--action-0);*/
-///* color: var(--action-contrast);*/
-///* border-color: var(--action-0);*/
-///*}*/
-//
-///*!* Upload Drop Zones *!*/
-///*.file-upload-container {*/
-///* position: relative;*/
-///* padding: .25rem;*/
-///* transition: border-color var(--trans-base),*/
-///* background-color var(--trans-base),*/
-///* padding var(--trans-base);*/
-///*}*/
-//
-///*.file-upload-container.dragover {*/
-///* background-color: var(--action-rgb-subtle);*/
-///* border-color: var(--action-0);*/
-///* padding: .75rem;*/
-///*}*/
-///* .dragover .file-upload-wrapper {*/
-///* transform: scale(1.02);*/
-///* }*/
-//
-//
-///*!* Upload Actions *!*/
-///*.upload .actions {*/
-///* display: flex;*/
-///* gap: .25rem;*/
-///* justify-content: flex-end;*/
-///*}*/
-//
-///*.upload .actions button {*/
-///* background: rgba(0, 0, 0, .7);*/
-///* color: white;*/
-///* border: none;*/
-///* padding: .25rem;*/
-///* border-radius: 4px;*/
-///* cursor: pointer;*/
-///* transition: background .3s ease;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///*}*/
-//
-///*.upload .actions button:hover {*/
-///* background: rgba(0, 0, 0, .9);*/
-///*}*/
-//
-///*.upload .actions button svg {*/
-///* width: 16px;*/
-///* height: 16px;*/
-///*}*/
-//
-///*!* Status Indicator *!*/
-///*.status {*/
-///* position: absolute;*/
-///* top: .5rem;*/
-///* right: .5rem;*/
-///* width: 24px;*/
-///* height: 24px;*/
-///* border-radius: 50%;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* font-size: .8rem;*/
-///* font-weight: bold;*/
-///* z-index: 2;*/
-///* box-shadow: 0 2px 4px rgba(0, 0, 0, .2);*/
-///*}*/
-//
-///*.status.processing {*/
-///* background: var(--warning);*/
-///* color: var(--warning-contrast);*/
-///*}*/
-//
-///*.status.processed {*/
-///* background: var(--info);*/
-///* color: var(--info-contrast);*/
-///*}*/
-//
-///*.status.cached {*/
-///* background: var(--info);*/
-///* color: var(--info-contrast);*/
-///*}*/
-//
-///*.status.uploading {*/
-///* background: var(--action-0);*/
-///* color: var(--action-contrast);*/
-///*}*/
-//
-///*.status.uploaded {*/
-///* background: var(--success);*/
-///* color: var(--success-contrast);*/
-///*}*/
-//
-///*.status.error {*/
-///* background: var(--error);*/
-///* color: var(--error-contrast);*/
-///*}*/
-//
-///*!* Upload Metadata *!*/
-///*.upload .metadata {*/
-///* padding: 1rem;*/
-///* border-top: 1px solid var(--border);*/
-///* background: var(--surface);*/
-///*}*/
-//
-///*.upload .metadata .metadata-field {*/
-///* margin-bottom: .75rem;*/
-///*}*/
-//
-///*.upload .metadata .metadata-field:last-child {*/
-///* margin-bottom: 0;*/
-///*}*/
-//
-///*.upload .metadata label {*/
-///* display: block;*/
-///* margin-bottom: .25rem;*/
-///* font-weight: 500;*/
-///* font-size: .9rem;*/
-///* color: var(--contrast);*/
-///*}*/
-//
-///*.upload .metadata input,*/
-///*.upload .metadata textarea {*/
-///* width: 100%;*/
-///* padding: .5rem;*/
-///* border: 1px solid var(--border);*/
-///* border-radius: 4px;*/
-///* font-size: .9rem;*/
-///* transition: border-color .3s ease, box-shadow .3s ease;*/
-///*}*/
-//
-///*.upload .metadata input:focus,*/
-///*.upload .metadata textarea:focus {*/
-///* outline: none;*/
-///* border-color: var(--action-0);*/
-///* box-shadow: 0 0 0 2px var(--action-rgb-subtle);*/
-///*}*/
-//
-///*.upload .metadata textarea {*/
-///* resize: vertical;*/
-///* min-height: 2.5rem;*/
-///*}*/
-//
-///*!* Field Type Specific Styles *!*/
-//
-///*!* Single Image Field *!*/
-///*.upload.field[data-upload-type="single"] .upload-preview-grid {*/
-///* grid-template-columns: 1fr;*/
-///* max-width: 400px;*/
-///*}*/
-//
-///*!* Gallery Field *!*/
-///*.upload.field[data-upload-type="gallery"] .upload-preview-grid {*/
-///* grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));*/
-///*}*/
-//
-///*!* Avatar Field *!*/
-///*.upload.field[data-upload-type="avatar"] .upload-preview-grid {*/
-///* grid-template-columns: 1fr;*/
-///* max-width: 200px;*/
-///*}*/
-//
-///*.upload.field[data-upload-type="avatar"] .upload-preview {*/
-///* aspect-ratio: 1;*/
-///* border-radius: 50%;*/
-///* overflow: hidden;*/
-///*}*/
-//
-///*!* Spinner Animation *!*/
-///*.spinner {*/
-///* width: 12px;*/
-///* height: 12px;*/
-///* border: 2px solid transparent;*/
-///* border-top: 2px solid currentColor;*/
-///* border-radius: 50%;*/
-///* animation: spin 1s linear infinite;*/
-///*}*/
-//
-///*@keyframes spin {*/
-///* 0% { transform: rotate(0deg); }*/
-///* 100% { transform: rotate(360deg); }*/
-///*}*/
-//
-///*@keyframes shimmer {*/
-///* 0% { transform: translateX(-100%); }*/
-///* 100% { transform: translateX(100%); }*/
-///*}*/
-//
-///*!* Responsive Design *!*/
-///*@media (max-width: 768px) {*/
-///* .all-uploads {*/
-///* position: relative;*/
-///* top: auto;*/
-///* left: auto;*/
-///* right: auto;*/
-///* }*/
-//
-///* .upload-summary {*/
-///* flex-direction: column;*/
-///* gap: .5rem;*/
-///* align-items: stretch;*/
-///* }*/
-//
-///* .upload-summary button {*/
-///* margin-left: 0;*/
-///* width: 100%;*/
-///* }*/
-//
-///* .upload-preview-grid {*/
-///* grid-template-columns: 1fr;*/
-///* gap: .75rem;*/
-///* }*/
-//
-///* .upload.field[data-upload-type="gallery"] .upload-preview-grid {*/
-///* grid-template-columns: repeat(2, 1fr);*/
-///* }*/
-///*}*/
-//
-///*!* Dark Mode Support *!*/
-///*@media (prefers-color-scheme: dark) {*/
-///* .upload-overlay {*/
-///* background: linear-gradient(to bottom, rgba(0,0,0,.3), rgba(0,0,0,.6));*/
-///* }*/
-//
-///*}*/
-//
-///*!* High Contrast Mode *!*/
-///*@media (prefers-contrast: high) {*/
-///* .upload-item {*/
-///* border-width: 2px;*/
-///* }*/
-//
-///* .status {*/
-///* border: 2px solid currentColor;*/
-///* }*/
-//
-///*}*/
-//
-///*!* Reduced Motion *!*/
-///*@media (prefers-reduced-motion: reduce) {*/
-///* .upload-item:hover {*/
-///* transform: none;*/
-///* }*/
-//
-///* .upload-item:hover .upload-preview img {*/
-///* transform: none;*/
-///* }*/
-//
-///* .spinner,*/
-///* .shimmer {*/
-///* animation: none;*/
-///* }*/
-///*}*/
-//
-///*!* Focus Management *!*/
-///*.upload-item:focus-within {*/
-///* outline: 2px solid var(--action-0);*/
-///* outline-offset: 2px;*/
-///*}*/
-//
-///*.upload .actions button:focus {*/
-///* outline: 2px solid var(--action-0);*/
-///* outline-offset: 2px;*/
-///*}*/
-//
-///*!* Loading States *!*/
-///*.upload.field.processing {*/
-///* pointer-events: none;*/
-///* opacity: .7;*/
-///*}*/
-//
-///*.upload.field.processing::after {*/
-///* content: '';*/
-///* position: absolute;*/
-///* top: 0;*/
-///* left: 0;*/
-///* right: 0;*/
-///* bottom: 0;*/
-///* background: rgba(255, 255, 255, .5);*/
-///* backdrop-filter: blur(2px);*/
-///* z-index: 10;*/
-///*}*/
-//
-///*!* Field Capacity Indicators *!*/
-///*.upload.field[data-at-capacity="true"] .file-upload-container {*/
-///* opacity: .5;*/
-///* pointer-events: none;*/
-///*}*/
-//
-///*.upload.field[data-at-capacity="true"]::before {*/
-///* content: 'Maximum files reached';*/
-///* position: absolute;*/
-///* top: 50%;*/
-///* left: 50%;*/
-///* transform: translate(-50%, -50%);*/
-///* background: var(--warning);*/
-///* color: var(--warning-contrast);*/
-///* padding: .5rem 1rem;*/
-///* border-radius: 4px;*/
-///* font-size: .875rem;*/
-///* z-index: 5;*/
-///*}*/
-//
-///*!* Integration with existing form styles *!*/
-///*.upload.field {*/
-///* position: relative;*/
-///*}*/
-//
-///*.upload.field .upload-preview-grid {*/
-///* margin-top: .5rem;*/
-///*}*/
-//
-///*!* Ensure global status doesn't interfere with fixed elements *!*/
-///*body.has-global-upload-status {*/
-///* padding-top: 80px;*/
-///*}*/
-//
-///*!* Animation for upload completion *!*/
-///*@keyframes uploadSuccess {*/
-///* 0% { transform: scale(1); }*/
-///* 50% { transform: scale(1.05); }*/
-///* 100% { transform: scale(1); }*/
-///*}*/
-//
-///*.upload-item[data-status="uploaded"] {*/
-///* animation: uploadSuccess .6s ease-out;*/
-///*}*/
-///*!* Batch Processing Progress Bar *!*/
-///*.batch-progress {*/
-///* margin: 1rem 0;*/
-///* padding: 1rem;*/
-///* background: var(--color-background-secondary, #f8f9fa);*/
-///* border: 1px solid var(--color-border, #e1e5e9);*/
-///* border-radius: 8px;*/
-///* transition: opacity .3s ease;*/
-///*}*/
-//
-///*.batch-progress.completed {*/
-///* background: var(--color-success-light, #d4edda);*/
-///* border-color: var(--color-success, #28a745);*/
-///*}*/
-//
-///*.progress-info {*/
-///* display: flex;*/
-///* justify-content: space-between;*/
-///* align-items: center;*/
-///* margin-bottom: .5rem;*/
-///* font-size: .9rem;*/
-///*}*/
-//
-///*.progress-message {*/
-///* color: var(--color-text-secondary, #6c757d);*/
-///* font-weight: 500;*/
-///*}*/
-//
-///*.progress-count {*/
-///* color: var(--color-text-primary, #212529);*/
-///* font-weight: 600;*/
-///* font-variant-numeric: tabular-nums;*/
-///*}*/
-//
-///*.progress .bar {*/
-///* width: 100%;*/
-///* height: 8px;*/
-///* background: var(--color-background-tertiary, #e9ecef);*/
-///* border-radius: 4px;*/
-///* overflow: hidden;*/
-///*}*/
-//
-///*.progress .fill {*/
-///* height: 100%;*/
-///* background: linear-gradient(90deg,*/
-///* var(--color-primary, #007cba) 0%,*/
-///* var(--color-primary-light, #0096dd) 100%*/
-///* );*/
-///* border-radius: 4px;*/
-///* transition: width .3s ease;*/
-///* position: relative;*/
-///*}*/
-//
-///*!* Animated progress indicator *!*/
-///*.progress .fill::after {*/
-///* content: '';*/
-///* position: absolute;*/
-///* top: 0;*/
-///* left: 0;*/
-///* right: 0;*/
-///* bottom: 0;*/
-///* background: linear-gradient(*/
-///* 90deg,*/
-///* transparent 0%,*/
-///* rgba(255,255,255,.3) 50%,*/
-///* transparent 100%*/
-///* );*/
-///* animation: progress-shine 2s ease-in-out infinite;*/
-///*}*/
-//
-///*@keyframes progress-shine {*/
-///* 0% { transform: translateX(-100%); }*/
-///* 100% { transform: translateX(100%); }*/
-///*}*/
-//
-///*!* Completed state styling *!*/
-///*.progress.completed .fill {*/
-///* background: linear-gradient(90deg,*/
-///* var(--color-success, #28a745) 0%,*/
-///* var(--color-success-light, #34ce57) 100%*/
-///* );*/
-///*}*/
-//
-///*.progress.completed .progress-message {*/
-///* color: var(--color-success-dark, #155724);*/
-///*}*/
-//
-///*!* Dark mode support *!*/
-///*@media (prefers-color-scheme: dark) {*/
-///* .progress {*/
-///* background: var(--color-background-secondary-dark, #2d3436);*/
-///* border-color: var(--color-border-dark, #636e72);*/
-///* }*/
-//
-///* .progress .bar {*/
-///* background: var(--color-background-tertiary-dark, #636e72);*/
-///* }*/
-///*}*/
-//
-///*!* ✅ Grouping System CSS *!*/
-//
-///*!* Upload field container with grouping support *!*/
-///*.upload.field.groupable {*/
-///* display: grid;*/
-///* grid-template-columns: 1fr 300px;*/
-///* gap: 20px;*/
-///* min-height: 400px;*/
-///*}*/
-//
-///*.upload.field.groupable .upload-main {*/
-///* display: flex;*/
-///* flex-direction: column;*/
-///*}*/
-//
-///*.upload.field.groupable .upload-sidebar {*/
-///* border-left: 2px solid #e0e0e0;*/
-///* padding-left: 20px;*/
-///*}*/
-//
-///*!* Preview grid for groupable uploads *!*/
-///*.upload-preview-grid.groupable {*/
-///* min-height: 200px;*/
-///* border: 2px dashed #ddd;*/
-///* border-radius: 8px;*/
-///* padding: 15px;*/
-///* background: #fafafa;*/
-///*}*/
-//
-///*.upload-preview-grid.groupable:empty::after {*/
-///* content: "Drop images here or click to upload";*/
-///* display: block;*/
-///* text-align: center;*/
-///* color: #666;*/
-///* font-style: italic;*/
-///* padding: 40px 20px;*/
-///*}*/
-//
-///*!* Upload items with drag support *!*/
-///*.upload-item[draggable] {*/
-///* cursor: grab;*/
-///* transition: transform .2s ease, box-shadow .2s ease;*/
-///*}*/
-//
-///*.upload-item[draggable]:hover {*/
-///* transform: translateY(-2px);*/
-///* box-shadow: 0 4px 8px rgba(0,0,0,.1);*/
-///*}*/
-//
-///*.upload-item.dragging {*/
-///* opacity: .5;*/
-///* transform: rotate(5deg);*/
-///* cursor: grabbing;*/
-///*}*/
-//
-///*!* Groups container *!*/
-///*.groups-container {*/
-///* display: flex;*/
-///* flex-direction: column;*/
-///* gap: 15px;*/
-///* max-height: 600px;*/
-///* overflow-y: auto;*/
-///* padding: 10px;*/
-///* background: #f9f9f9;*/
-///* border-radius: 8px;*/
-///*}*/
-//
-///*.groups-container .new-group-section {*/
-///* text-align: center;*/
-///* padding: 20px;*/
-///* border: 2px dashed #ccc;*/
-///* border-radius: 8px;*/
-///* background: white;*/
-///* cursor: pointer;*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.groups-container .new-group-section:hover {*/
-///* border-color: #2196F3;*/
-///* background: #f0f8ff;*/
-///*}*/
-//
-///*.new-group-btn {*/
-///* background: #2196F3;*/
-///* color: white;*/
-///* border: none;*/
-///* padding: 10px 20px;*/
-///* border-radius: 6px;*/
-///* cursor: pointer;*/
-///* font-size: 14px;*/
-///* display: inline-flex;*/
-///* align-items: center;*/
-///* gap: 8px;*/
-///*}*/
-//
-///*.new-group-btn:hover {*/
-///* background: #1976D2;*/
-///*}*/
-//
-///*!* Individual groups *!*/
-///*.upload-group {*/
-///* background: white;*/
-///* border: 1px solid #e0e0e0;*/
-///* border-radius: 8px;*/
-///* padding: 15px;*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.upload-group.drag-hover {*/
-///* border-color: #2196F3;*/
-///* background: #f0f8ff;*/
-///* box-shadow: 0 2px 8px rgba(33, 150, 243, .2);*/
-///*}*/
-//
-///*.group-header {*/
-///* display: flex;*/
-///* justify-content: space-between;*/
-///* align-items: center;*/
-///* margin-bottom: 15px;*/
-///* padding-bottom: 10px;*/
-///* border-bottom: 1px solid #f0f0f0;*/
-///*}*/
-//
-///*.group-info {*/
-///* flex: 1;*/
-///* display: flex;*/
-///* flex-direction: column;*/
-///* gap: 5px;*/
-///*}*/
-//
-///*.group-name {*/
-///* border: 1px solid #ddd;*/
-///* border-radius: 4px;*/
-///* padding: 6px 10px;*/
-///* font-size: 14px;*/
-///* font-weight: 500;*/
-///* background: white;*/
-///* transition: border-color .2s ease;*/
-///*}*/
-//
-///*.group-name:focus {*/
-///* outline: none;*/
-///* border-color: #2196F3;*/
-///* box-shadow: 0 0 0 2px rgba(33, 150, 243, .2);*/
-///*}*/
-//
-///*.group-name:empty::before {*/
-///* content: "Enter group name...";*/
-///* color: #999;*/
-///* font-style: italic;*/
-///*}*/
-//
-///*.group-count {*/
-///* font-size: 12px;*/
-///* color: #666;*/
-///* font-weight: normal;*/
-///*}*/
-//
-///*.group-actions {*/
-///* display: flex;*/
-///* gap: 8px;*/
-///*}*/
-//
-///*.delete-group {*/
-///* background: #f44336;*/
-///* color: white;*/
-///* border: none;*/
-///* padding: 6px 8px;*/
-///* border-radius: 4px;*/
-///* cursor: pointer;*/
-///* font-size: 12px;*/
-///* transition: background .2s ease;*/
-///*}*/
-//
-///*.delete-group:hover {*/
-///* background: #d32f2f;*/
-///*}*/
-//
-///*!* Group drop zone *!*/
-///*.group-drop-zone {*/
-///* min-height: 80px;*/
-///* border: 2px dashed #ccc;*/
-///* border-radius: 6px;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* margin-bottom: 15px;*/
-///* transition: all .2s ease;*/
-///* background: #fafafa;*/
-///*}*/
-//
-///*.group-drop-zone p {*/
-///* color: #666;*/
-///* font-style: italic;*/
-///* margin: 0;*/
-///* font-size: 14px;*/
-///*}*/
-//
-///*.group-drop-zone.drag-hover {*/
-///* border-color: #4CAF50;*/
-///* background: #f1f8e9;*/
-///*}*/
-//
-///*!* Group items *!*/
-///*.group-items {*/
-///* display: grid;*/
-///* grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));*/
-///* gap: 10px;*/
-///* margin-bottom: 15px;*/
-///*}*/
-//
-///*.group-item {*/
-///* position: relative;*/
-///* background: white;*/
-///* border: 1px solid #e0e0e0;*/
-///* border-radius: 6px;*/
-///* overflow: hidden;*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.group-item:hover {*/
-///* box-shadow: 0 2px 8px rgba(0,0,0,.1);*/
-///*}*/
-//
-///*.group-item-preview {*/
-///* position: relative;*/
-///* width: 100%;*/
-///* height: 80px;*/
-///* overflow: hidden;*/
-///*}*/
-//
-///*.group-item-preview img {*/
-///* width: 100%;*/
-///* height: 100%;*/
-///* object-fit: cover;*/
-///*}*/
-//
-///*.group-item-overlay {*/
-///* position: absolute;*/
-///* top: 0;*/
-///* right: 0;*/
-///* background: rgba(0,0,0,.7);*/
-///* padding: 4px;*/
-///* opacity: 0;*/
-///* transition: opacity .2s ease;*/
-///*}*/
-//
-///*.group-item:hover .group-item-overlay {*/
-///* opacity: 1;*/
-///*}*/
-//
-///*.remove-from-group {*/
-///* background: #f44336;*/
-///* color: white;*/
-///* border: none;*/
-///* padding: 4px 6px;*/
-///* border-radius: 3px;*/
-///* cursor: pointer;*/
-///* font-size: 10px;*/
-///*}*/
-//
-///*.remove-from-group:hover {*/
-///* background: #d32f2f;*/
-///*}*/
-//
-///*!* Group metadata fields *!*/
-///*.group-metadata {*/
-///* padding-top: 15px;*/
-///* border-top: 1px solid #f0f0f0;*/
-///*}*/
-//
-///*.group-metadata .metadata-field {*/
-///* margin-bottom: 12px;*/
-///*}*/
-//
-///*.group-metadata label {*/
-///* display: block;*/
-///* font-size: 12px;*/
-///* font-weight: 500;*/
-///* color: #333;*/
-///* margin-bottom: 4px;*/
-///*}*/
-//
-///*.group-metadata input,*/
-///*.group-metadata textarea {*/
-///* width: 100%;*/
-///* border: 1px solid #ddd;*/
-///* border-radius: 4px;*/
-///* padding: 6px 8px;*/
-///* font-size: 13px;*/
-///* transition: border-color .2s ease;*/
-///*}*/
-//
-///*.group-metadata input:focus,*/
-///*.group-metadata textarea:focus {*/
-///* outline: none;*/
-///* border-color: #2196F3;*/
-///* box-shadow: 0 0 0 2px rgba(33, 150, 243, .2);*/
-///*}*/
-//
-///*.group-metadata textarea {*/
-///* resize: vertical;*/
-///* min-height: 60px;*/
-///*}*/
-//
-///*!* Drag feedback animations *!*/
-///*@keyframes dragHover {*/
-///* 0% { transform: scale(1); }*/
-///* 50% { transform: scale(1.02); }*/
-///* 100% { transform: scale(1); }*/
-///*}*/
-//
-///*.drag-hover {*/
-///* animation: dragHover .6s ease-in-out infinite;*/
-///*}*/
-//
-///*!* Status indicators for grouped uploads *!*/
-///*.upload-group[data-status="uploading"] {*/
-///* border-color: #FF9800;*/
-///* background: #fff8f0;*/
-///*}*/
-//
-///*.upload-group[data-status="uploaded"] {*/
-///* border-color: #4CAF50;*/
-///* background: #f1f8e9;*/
-///*}*/
-//
-///*.upload-group[data-status="error"] {*/
-///* border-color: #f44336;*/
-///* background: #ffebee;*/
-///*}*/
-//
-///*!* Progress indicators for groups *!*/
-///*.group-progress {*/
-///* height: 3px;*/
-///* background: #f0f0f0;*/
-///* border-radius: 2px;*/
-///* overflow: hidden;*/
-///* margin-top: 10px;*/
-///*}*/
-//
-///*.group-progress-fill {*/
-///* height: 100%;*/
-///* background: #2196F3;*/
-///* transition: width .3s ease;*/
-///* border-radius: 2px;*/
-///*}*/
-//
-///*!* Responsive design *!*/
-///*@media (max-width: 768px) {*/
-///* .upload.field.groupable {*/
-///* grid-template-columns: 1fr;*/
-///* gap: 15px;*/
-///* }*/
-//
-///* .upload.field.groupable .upload-sidebar {*/
-///* border-left: none;*/
-///* border-top: 2px solid #e0e0e0;*/
-///* padding-left: 0;*/
-///* padding-top: 15px;*/
-///* }*/
-//
-///* .group-items {*/
-///* grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));*/
-///* gap: 8px;*/
-///* }*/
-//
-///* .groups-container {*/
-///* max-height: 400px;*/
-///* }*/
-///*}*/
-//
-///*!* Accessibility improvements *!*/
-///*.upload-item.groupable:focus {*/
-///* outline: 2px solid #2196F3;*/
-///* outline-offset: 2px;*/
-///*}*/
-//
-///*.group-drop-zone:focus-within {*/
-///* border-color: #2196F3;*/
-///* background: #f0f8ff;*/
-///*}*/
-//
-///*!* High contrast mode support *!*/
-///*@media (prefers-contrast: high) {*/
-///* .upload-group {*/
-///* border-width: 2px;*/
-///* }*/
-//
-///* .group-drop-zone {*/
-///* border-width: 3px;*/
-///* }*/
-//
-///* .upload-item.groupable.dragging {*/
-///* border: 3px solid #2196F3;*/
-///* }*/
-///*}*/
-//
-///*!* Selection Controls *!*/
-///*.selection-controls {*/
-///* display: flex;*/
-///* align-items: center;*/
-///* gap: 1rem;*/
-///* flex-wrap: wrap;*/
-///*}*/
-//
-///*.selection-actions {*/
-///* display: flex;*/
-///* align-items: center;*/
-///* gap: 1rem;*/
-///* padding: .5rem 1rem;*/
-///* background-color: var(--action-50);*/
-///* border-radius: var(--radius);*/
-///* color: var(--contrast);*/
-///* font-size: .9rem;*/
-///*}*/
-//
-///*.selection-actions button {*/
-///* background: rgba(255, 255, 255, .2);*/
-///* border: 1px solid rgba(255, 255, 255, .3);*/
-///* color: inherit;*/
-///* padding: .25rem .5rem;*/
-///* border-radius: var(--radius);*/
-///* display: flex;*/
-///* align-items: center;*/
-///* gap: .25rem;*/
-///*}*/
-//
-///*.selection-actions button:hover {*/
-///* background: rgba(255, 255, 255, .3);*/
-///*}*/
-//
-///*.selection-count {*/
-///* font-weight: bold;*/
-///*}*/
-//
-///*!* Preview Actions *!*/
-///*.preview-actions {*/
-///* display: flex;*/
-///* justify-content: space-between;*/
-///* align-items: center;*/
-///* padding: 1rem;*/
-///* background-color: var(--base-100);*/
-///* border-radius: var(--radius-outer);*/
-///* margin-bottom: 1rem;*/
-///*}*/
-//
-///*!* Upload Item Enhancements *!*/
-///*.upload-item {*/
-///* position: relative;*/
-///* background: var(--base);*/
-///* border-radius: var(--radius);*/
-///* overflow: hidden;*/
-///* cursor: pointer;*/
-///* transition: transform .2s ease, box-shadow .2s ease;*/
-///*}*/
-//
-///*.upload-item:hover {*/
-///* transform: translateY(-2px);*/
-///* box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw);*/
-///*}*/
-//
-///*.upload-item[draggable="true"] {*/
-///* cursor: grab;*/
-///*}*/
-//
-///*.upload-item.dragging {*/
-///* cursor: grabbing;*/
-///* opacity: .5;*/
-///* transform: rotate(5deg) scale(.95);*/
-///*}*/
-//
-///*.upload-item.selected {*/
-///* border: 2px solid var(--action-50);*/
-///* background: rgba(255, 0, 128, .05);*/
-///*}*/
-//
-///*.upload-item .preview {*/
-///* position: relative;*/
-///* aspect-ratio: 1;*/
-///*}*/
-//
-///*.upload-item img {*/
-///* width: 100%;*/
-///* height: 100%;*/
-///* object-fit: cover;*/
-///*}*/
-//
-///*.upload-item .overlay {*/
-///* position: absolute;*/
-///* top: 0;*/
-///* left: 0;*/
-///* right: 0;*/
-///* bottom: 0;*/
-///* background: rgba(0, 0, 0, .6);*/
-///* opacity: 0;*/
-///* transition: opacity .2s ease;*/
-///* display: flex;*/
-///* flex-direction: column;*/
-///* justify-content: space-between;*/
-///* padding: .5rem;*/
-///*}*/
-//
-///*.upload-item:hover .overlay,*/
-///*.upload-item.selected .overlay {*/
-///* opacity: 1;*/
-///*}*/
-//
-///*.upload-item .actions input[type="checkbox"] {*/
-///* margin: 0;*/
-///* transform: scale(1.2);*/
-///*}*/
-//
-///*.upload-item .actions label {*/
-///* margin: 0;*/
-///* padding: 0;*/
-///* cursor: pointer;*/
-///*}*/
-//
-///*!* Status Indicator *!*/
-///*.upload-item .status {*/
-///* position: absolute;*/
-///* top: .5rem;*/
-///* right: .5rem;*/
-///* width: 1.5rem;*/
-///* height: 1.5rem;*/
-///* border-radius: 50%;*/
-///* background: var(--base);*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* box-shadow: 0 2px 4px rgba(0, 0, 0, .2);*/
-///*}*/
-//
-///*.upload-item[data-status="processing"] .status {*/
-///* background: var(--warning);*/
-///*}*/
-//
-///*.upload-item[data-status="uploaded"] .status {*/
-///* background: var(--success);*/
-///*}*/
-//
-///*.upload-item[data-status="error"] .status {*/
-///* background: var(--danger);*/
-///*}*/
-//
-///*!* Group Enhancements *!*/
-///*.upload-group {*/
-///* background: var(--base-50);*/
-///* border-radius: var(--radius-outer);*/
-///* padding: 1rem;*/
-///* margin-bottom: 1rem;*/
-///* border: 2px solid transparent;*/
-///* transition: border-color .2s ease, background-color .2s ease;*/
-///*}*/
-//
-///*.upload-group.dragover {*/
-///* border-color: var(--action-50);*/
-///* background: rgba(255, 0, 128, .05);*/
-///*}*/
-//
-///*.group-header {*/
-///* display: flex;*/
-///* justify-content: space-between;*/
-///* align-items: center;*/
-///* margin-bottom: 1rem;*/
-///*}*/
-//
-///*.group-info {*/
-///* flex: 1;*/
-///* display: flex;*/
-///* flex-direction: column;*/
-///* gap: .25rem;*/
-///*}*/
-//
-///*.group-name {*/
-///* font-size: 1.1rem;*/
-///* font-weight: 500;*/
-///* border: none;*/
-///* background: transparent;*/
-///* color: var(--text);*/
-///* padding: .25rem 0;*/
-///* border-bottom: 1px solid transparent;*/
-///* transition: border-color .2s ease;*/
-///*}*/
-//
-///*.group-name:focus {*/
-///* outline: none;*/
-///* border-bottom-color: var(--action-50);*/
-///*}*/
-//
-///*.group-count {*/
-///* font-size: .85rem;*/
-///* color: var(--text-muted);*/
-///*}*/
-//
-///*.group-actions {*/
-///* display: flex;*/
-///* gap: .5rem;*/
-///*}*/
-//
-///*.group-actions button {*/
-///* background: var(--base);*/
-///* border: 1px solid var(--base-200);*/
-///* border-radius: var(--radius);*/
-///* padding: .5rem;*/
-///* cursor: pointer;*/
-///* transition: all .2s ease;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///*}*/
-//
-///*.group-actions button:hover {*/
-///* background: var(--action-50);*/
-///* color: var(--contrast);*/
-///* border-color: var(--action-50);*/
-///*}*/
-//
-///*.group-actions .delete-group:hover {*/
-///* background: var(--danger);*/
-///* border-color: var(--danger);*/
-///*}*/
-//
-///*!* Group Content *!*/
-///*.group-content {*/
-///* min-height: 120px;*/
-///*}*/
-//
-///*.group-drop-zone {*/
-///* border: 2px dashed var(--base-300);*/
-///* border-radius: var(--radius);*/
-///* padding: 2rem;*/
-///* text-align: center;*/
-///* color: var(--text-muted);*/
-///* transition: all .2s ease;*/
-///* cursor: pointer;*/
-///*}*/
-//
-///*.group-drop-zone:hover,*/
-///*.group-drop-zone.dragover {*/
-///* border-color: var(--action-50);*/
-///* background: rgba(255, 0, 128, .05);*/
-///* color: var(--action-50);*/
-///*}*/
-//
-///*.group-items {*/
-///* display: grid;*/
-///* grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));*/
-///* gap: 1rem;*/
-///* padding: 1rem 0;*/
-///*}*/
-//
-///*.group-item {*/
-///* position: relative;*/
-///* aspect-ratio: 1;*/
-///* border-radius: var(--radius);*/
-///* overflow: hidden;*/
-///* background: var(--base);*/
-///* transition: transform .2s ease;*/
-///*}*/
-//
-///*.group-item:hover {*/
-///* transform: scale(1.02);*/
-///*}*/
-//
-///*.group-item img {*/
-///* width: 100%;*/
-///* height: 100%;*/
-///* object-fit: cover;*/
-///*}*/
-//
-///*.group-item .overlay {*/
-///* position: absolute;*/
-///* top: 0;*/
-///* left: 0;*/
-///* right: 0;*/
-///* bottom: 0;*/
-///* background: rgba(0, 0, 0, .6);*/
-///* opacity: 0;*/
-///* transition: opacity .2s ease;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* gap: .5rem;*/
-///*}*/
-//
-///*.group-item:hover .overlay {*/
-///* opacity: 1;*/
-///*}*/
-//
-///*.group-item .overlay button {*/
-///* background: rgba(255, 255, 255, .9);*/
-///* border: none;*/
-///* border-radius: 50%;*/
-///* width: 2rem;*/
-///* height: 2rem;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* cursor: pointer;*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.group-item .set-featured {*/
-///* color: var(--warning);*/
-///*}*/
-//
-///*.group-item .set-featured:hover {*/
-///* background: var(--warning);*/
-///* color: white;*/
-///*}*/
-//
-///*.group-item .remove-from-group:hover {*/
-///* background: var(--danger);*/
-///* color: white;*/
-///*}*/
-//
-///*!* Empty Group State *!*/
-///*.empty-group {*/
-///* border: 4px dashed var(--base-200);*/
-///* border-radius: var(--radius);*/
-///* padding: 2rem;*/
-///* text-align: center;*/
-///* color: var(--text-muted);*/
-///* aspect-ratio: 1;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* cursor: pointer;*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.empty-group:hover,*/
-///*.empty-group.dragover {*/
-///* border-color: var(--action-50);*/
-///* background: rgba(255, 0, 128, .05);*/
-///* color: var(--action-50);*/
-///*}*/
-//
-///*!* Sidebar *!*/
-///*.sidebar {*/
-///* background: var(--base-50);*/
-///* border-radius: var(--radius-outer);*/
-///* padding: 1.5rem;*/
-///* min-height: 400px;*/
-///*}*/
-//
-///*.sidebar .header {*/
-///* margin-bottom: 1.5rem;*/
-///*}*/
-//
-///*.sidebar h4 {*/
-///* margin: 0 0 .5rem 0;*/
-///* color: var(--text);*/
-///*}*/
-//
-///*.sidebar .hint {*/
-///* font-size: .85rem;*/
-///* color: var(--text-muted);*/
-///* margin: .25rem 0;*/
-///*}*/
-//
-///*.new-group {*/
-///* width: 100%;*/
-///* background: var(--action-50);*/
-///* color: var(--contrast);*/
-///* border: none;*/
-///* border-radius: var(--radius);*/
-///* padding: .75rem;*/
-///* margin-bottom: 1rem;*/
-///* cursor: pointer;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* gap: .5rem;*/
-///* font-weight: 500;*/
-///* transition: background-color .2s ease;*/
-///*}*/
-//
-///*.new-group:hover {*/
-///* background: rgba(255, 0, 128, .8);*/
-///*}*/
-//
-///*!* Layout *!*/
-///*.group-display {*/
-///* display: grid;*/
-///* grid-template-columns: 2fr 1fr;*/
-///* gap: 2rem;*/
-///* margin-top: 2rem;*/
-///*}*/
-//
-///*.preview-wrap {*/
-///* min-height: 400px;*/
-///*}*/
-//
-///*.preview-grid {*/
-///* display: grid;*/
-///* grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));*/
-///* gap: 1rem;*/
-///* padding: 1rem;*/
-///* background: var(--base-100);*/
-///* border-radius: var(--radius-outer);*/
-///* min-height: 200px;*/
-///*}*/
-//
-///*.preview-grid:empty::after {*/
-///* content: "No images uploaded yet. Drag files here or click to upload.";*/
-///* grid-column: 1 / -1;*/
-///* text-align: center;*/
-///* color: var(--text-muted);*/
-///* padding: 2rem;*/
-///* border: 2px dashed var(--base-300);*/
-///* border-radius: var(--radius);*/
-///*}*/
-//
-///*!* File Upload Container *!*/
-///*.file-upload-container.dragover {*/
-///* border-color: var(--action-50);*/
-///* background: rgba(255, 0, 128, .05);*/
-///*}*/
-//
-///*!* Responsive Design *!*/
-///*@media (max-width: 768px) {*/
-///* .group-display {*/
-///* grid-template-columns: 1fr;*/
-///* gap: 1rem;*/
-///* }*/
-//
-///* .preview-actions {*/
-///* flex-direction: column;*/
-///* align-items: stretch;*/
-///* gap: 1rem;*/
-///* }*/
-//
-///* .selection-controls {*/
-///* justify-content: space-between;*/
-///* }*/
-//
-///* .preview-grid {*/
-///* grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));*/
-///* }*/
-//
-///* .group-items {*/
-///* grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));*/
-///* }*/
-///*}*/
-//
-///*!* Animation for upload progress *!*/
-///*@keyframes uploadProgress {*/
-///* 0% { transform: translateX(-100%); }*/
-///* 100% { transform: translateX(100%); }*/
-///*}*/
-//
-///*.upload-item[data-status="uploading"] .progress .bar::after {*/
-///* content: '';*/
-///* position: absolute;*/
-///* top: 0;*/
-///* left: 0;*/
-///* height: 100%;*/
-///* width: 20%;*/
-///* background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .5), transparent);*/
-///* animation: uploadProgress 1.5s infinite;*/
-///*}*/
-//
-///*!* Accessibility *!*/
-///*.upload-item:focus-within,*/
-///*.group-item:focus-within,*/
-///*.upload-group:focus-within {*/
-///* outline: 2px solid var(--action-50);*/
-///* outline-offset: 2px;*/
-///*}*/
-//
-///*!* High contrast mode support *!*/
-///*@media (prefers-contrast: high) {*/
-///* .upload-item.selected {*/
-///* border-width: 3px;*/
-///* }*/
-//
-///* .group-drop-zone.dragover {*/
-///* border-width: 3px;*/
-///* }*/
-///*}*/
-///*.restore-notification {*/
-///* background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);*/
-///* border: 1px solid #ffc107;*/
-///* border-left: 4px solid #ff6b35;*/
-///* border-radius: var(--radius-outer);*/
-///* padding: 1.5rem;*/
-///* margin-bottom: 1.5rem;*/
-///* box-shadow: 0 4px 12px rgba(255, 107, 53, .15);*/
-///* animation: slideInFromTop .4s ease-out;*/
-///* transition: opacity .3s ease, transform .3s ease;*/
-///*}*/
-//
-///*@keyframes slideInFromTop {*/
-///* from {*/
-///* transform: translateY(-20px);*/
-///* opacity: 0;*/
-///* }*/
-///* to {*/
-///* transform: translateY(0);*/
-///* opacity: 1;*/
-///* }*/
-///*}*/
-//
-///*.restore-content {*/
-///* display: grid;*/
-///* grid-template-columns: auto 1fr auto;*/
-///* gap: 1rem;*/
-///* align-items: start;*/
-///*}*/
-//
-///*.restore-icon {*/
-///* font-size: 1.5rem;*/
-///* color: #ff6b35;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* width: 2.5rem;*/
-///* height: 2.5rem;*/
-///* background: rgba(255, 107, 53, .1);*/
-///* border-radius: 50%;*/
-///* flex-shrink: 0;*/
-///*}*/
-//
-///*.restore-message {*/
-///* min-width: 0; !* Allow text to wrap *!*/
-///*}*/
-//
-///*.restore-message h4 {*/
-///* margin: 0 0 .5rem 0;*/
-///* color: #d63384;*/
-///* font-size: 1.1rem;*/
-///* font-weight: 600;*/
-///*}*/
-//
-///*.restore-message p {*/
-///* margin: 0 0 .5rem 0;*/
-///* color: #6c5419;*/
-///* line-height: 1.4;*/
-///*}*/
-//
-///*.restore-message p:last-child {*/
-///* margin-bottom: 0;*/
-///*}*/
-//
-///*.restore-message .warning {*/
-///* background: rgba(220, 53, 69, .1);*/
-///* border: 1px solid rgba(220, 53, 69, .2);*/
-///* border-radius: var(--radius);*/
-///* padding: .5rem .75rem;*/
-///* margin-top: .75rem;*/
-///* font-size: .9rem;*/
-///*}*/
-//
-///*.restore-message .warning strong {*/
-///* color: #dc3545;*/
-///*}*/
-//
-///*.restore-actions {*/
-///* display: flex;*/
-///* flex-direction: column;*/
-///* gap: .5rem;*/
-///* flex-shrink: 0;*/
-///*}*/
-//
-///*.start-over-btn {*/
-///* background: #dc3545;*/
-///* color: white;*/
-///* border: none;*/
-///* border-radius: var(--radius);*/
-///* padding: .5rem 1rem;*/
-///* font-size: .9rem;*/
-///* font-weight: 500;*/
-///* cursor: pointer;*/
-///* transition: all .2s ease;*/
-///* white-space: nowrap;*/
-///*}*/
-//
-///*.start-over-btn:hover {*/
-///* background: #c82333;*/
-///* transform: translateY(-1px);*/
-///* box-shadow: 0 2px 4px rgba(220, 53, 69, .3);*/
-///*}*/
-//
-///*.start-over-btn:active {*/
-///* transform: translateY(0);*/
-///*}*/
-//
-///*.dismiss-notification {*/
-///* background: transparent;*/
-///* border: 1px solid #6c5419;*/
-///* color: #6c5419;*/
-///* border-radius: var(--radius);*/
-///* padding: .5rem;*/
-///* cursor: pointer;*/
-///* transition: all .2s ease;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* width: 2rem;*/
-///* height: 2rem;*/
-///*}*/
-//
-///*.dismiss-notification:hover {*/
-///* background: #6c5419;*/
-///* color: white;*/
-///*}*/
-//
-///*!* Start Over Confirmation Dialog *!*/
-///*.start-over-confirmation {*/
-///* border: none;*/
-///* border-radius: var(--radius-outer);*/
-///* padding: 0;*/
-///* box-shadow: 0 10px 30px rgba(0, 0, 0, .3);*/
-///* max-width: 500px;*/
-///* width: 90vw;*/
-///*}*/
-//
-///*.start-over-confirmation::backdrop {*/
-///* background: rgba(0, 0, 0, .6);*/
-///* backdrop-filter: blur(4px);*/
-///*}*/
-//
-///*.confirmation-content {*/
-///* padding: 2rem;*/
-///* background: var(--base);*/
-///* border-radius: var(--radius-outer);*/
-///*}*/
-//
-///*.confirmation-content h3 {*/
-///* margin: 0 0 1rem 0;*/
-///* color: #dc3545;*/
-///* font-size: 1.25rem;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* gap: .5rem;*/
-///*}*/
-//
-///*.confirmation-content h3::before {*/
-///* content: "⚠️";*/
-///* font-size: 1.5rem;*/
-///*}*/
-//
-///*.confirmation-content p {*/
-///* margin: 0 0 1rem 0;*/
-///* color: var(--text);*/
-///* line-height: 1.5;*/
-///*}*/
-//
-///*.confirmation-content ul {*/
-///* margin: 0 0 1rem 1rem;*/
-///* color: var(--text);*/
-///*}*/
-//
-///*.confirmation-content li {*/
-///* margin-bottom: .25rem;*/
-///*}*/
-//
-///*.confirmation-actions {*/
-///* display: flex;*/
-///* gap: 1rem;*/
-///* justify-content: flex-end;*/
-///* margin-top: 1.5rem;*/
-///* padding-top: 1rem;*/
-///* border-top: 1px solid var(--base-200);*/
-///*}*/
-//
-///*.confirm-start-over {*/
-///* background: #dc3545;*/
-///* color: white;*/
-///* border: none;*/
-///* border-radius: var(--radius);*/
-///* padding: .75rem 1.5rem;*/
-///* font-weight: 500;*/
-///* cursor: pointer;*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.confirm-start-over:hover {*/
-///* background: #c82333;*/
-///*}*/
-//
-///*.cancel-start-over {*/
-///* background: var(--base-100);*/
-///* color: var(--text);*/
-///* border: 1px solid var(--base-300);*/
-///* border-radius: var(--radius);*/
-///* padding: .75rem 1.5rem;*/
-///* cursor: pointer;*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.cancel-start-over:hover {*/
-///* background: var(--base-200);*/
-///*}*/
-//
-///*!* Responsive Design *!*/
-///*@media (max-width: 768px) {*/
-///* .restore-content {*/
-///* grid-template-columns: 1fr;*/
-///* gap: 1rem;*/
-///* text-align: center;*/
-///* }*/
-//
-///* .restore-icon {*/
-///* margin: 0 auto;*/
-///* }*/
-//
-///* .restore-actions {*/
-///* flex-direction: row;*/
-///* justify-content: center;*/
-///* align-items: center;*/
-///* }*/
-//
-///* .start-over-btn {*/
-///* order: 2;*/
-///* }*/
-//
-///* .dismiss-notification {*/
-///* order: 1;*/
-///* }*/
-//
-///* .confirmation-actions {*/
-///* flex-direction: column-reverse;*/
-///* }*/
-//
-///* .confirmation-actions button {*/
-///* width: 100%;*/
-///* }*/
-///*}*/
-//
-///*!* Reduced motion preference *!*/
-///*@media (prefers-reduced-motion: reduce) {*/
-///* .restore-notification {*/
-///* animation: none;*/
-///* }*/
-//
-///* .start-over-btn:hover {*/
-///* transform: none;*/
-///* }*/
-///*}*/
-//
-///*!* High contrast mode *!*/
-///*@media (prefers-contrast: high) {*/
-///* .restore-notification {*/
-///* border-width: 2px;*/
-///* border-left-width: 6px;*/
-///* }*/
-//
-///* .restore-message .warning {*/
-///* border-width: 2px;*/
-///* }*/
-///*}*/
-//
-///*!* Dark mode adjustments *!*/
-///*@media (prefers-color-scheme: dark) {*/
-///* .restore-notification {*/
-///* background: linear-gradient(135deg, #3d3d00 0%, #4a4a00 100%);*/
-///* border-color: #ffc107;*/
-///* color: #fff3cd;*/
-///* }*/
-//
-///* .restore-message p {*/
-///* color: #fff3cd;*/
-///* }*/
-//
-///* .restore-message .warning {*/
-///* background: rgba(220, 53, 69, .2);*/
-///* border-color: rgba(220, 53, 69, .4);*/
-///* color: #ffb3ba;*/
-///* }*/
-//
-///* .dismiss-notification {*/
-///* border-color: #fff3cd;*/
-///* color: #fff3cd;*/
-///* }*/
-//
-///* .dismiss-notification:hover {*/
-///* background: #fff3cd;*/
-///* color: #3d3d00;*/
-///* }*/
-///*}*/
-//
-///*.dragover {*/
-///* background-color: rgba(var(--primary-rgb), .1);*/
-///* border: 2px dashed var(--primary-color);*/
-///* border-radius: 8px;*/
-///*}*/
-//
-///*.upload-item.dragging {*/
-///* opacity: .5;*/
-///* transform: scale(.95);*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.group-drop-zone {*/
-///* min-height: 60px;*/
-///* border: 2px dashed #ccc;*/
-///* border-radius: 8px;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* color: #666;*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.group-drop-zone.dragover {*/
-///* border-color: var(--primary-color);*/
-///* background-color: rgba(var(--primary-rgb), .05);*/
-///* color: var(--primary-color);*/
-///*}*/
-//
-///*.empty-group {*/
-///* padding: 20px;*/
-///* text-align: center;*/
-///* border: 2px dashed #ddd;*/
-///* border-radius: 8px;*/
-///* margin: 10px 0;*/
-///* cursor: pointer;*/
-///* transition: all .2s ease;*/
-///*}*/
-//
-///*.empty-group:hover {*/
-///* border-color: var(--primary-color);*/
-///* background-color: rgba(var(--primary-rgb), .02);*/
-///*}*/
-//
-///*.group-item.featured {*/
-///* border: 2px solid gold;*/
-///* position: relative;*/
-///*}*/
-//
-///*.group-item.featured::after {*/
-///* content: "⭐";*/
-///* position: absolute;*/
-///* top: 5px;*/
-///* right: 5px;*/
-///* background: gold;*/
-///* border-radius: 50%;*/
-///* width: 20px;*/
-///* height: 20px;*/
-///* display: flex;*/
-///* align-items: center;*/
-///* justify-content: center;*/
-///* font-size: 12px;*/
-///*}*/
-//
-///*.upload-item[draggable="true"] {*/
-///* cursor: grab;*/
-///*}*/
-//
-///*.upload-item[draggable="true"]:active {*/
-///* cursor: grabbing;*/
-///*}*/
-//
-///*.selection-actions {*/
-///* display: flex;*/
-///* align-items: center;*/
-///* gap: 10px;*/
-///* padding: 10px;*/
-///* background: rgba(var(--primary-rgb), .05);*/
-///* border-radius: 6px;*/
-///* border: 1px solid rgba(var(--primary-rgb), .2);*/
-///*}*/
-//
-///*.group-display .sidebar {*/
-///* min-width: 300px;*/
-///* max-width: 350px;*/
-///* background: #f9f9f9;*/
-///* border-left: 1px solid #eee;*/
-///* padding: 20px;*/
-///*}*/
-//
-///*.group-display .preview-wrap {*/
-///* flex: 1;*/
-///* padding: 20px;*/
-///*}*/
-//
-///*.group-display {*/
-///* display: flex;*/
-///* gap: 20px;*/
-///* margin-top: 20px;*/
-///* border: 1px solid #ddd;*/
-///* border-radius: 8px;*/
-///* overflow: hidden;*/
-///*}*/
-//
-///* Touch Drag & Drop Styles */
-//
-///* Original item being dragged */
-//.upload-item.dragging {
-// opacity: .5;
-// transform: scale(.95);
-// transition: opacity .2s ease, transform .2s ease;
-//}
-//
-///* Touch drag preview element */
-//.drag-preview {
-// background: var(--bg-elevated);
-// border: 2px solid var(--accent-primary);
-// cursor: grabbing;
-// user-select: none;
-// -webkit-user-select: none;
-// -webkit-user-drag: none;
-//}
-//
-//.drag-preview * {
-// pointer-events: none;
-//}
-//
-///* Drop target states for touch */
-//.dragover {
-// background-color: var(--accent-primary-alpha);
-// border: 2px dashed var(--accent-primary);
-// transform: scale(1.02);
-// transition: all .2s ease;
-//}
-//
-//.dragover::after {
-// content: "Drop here";
-// position: absolute;
-// top: 50%;
-// left: 50%;
-// transform: translate(-50%, -50%);
-// background: var(--accent-primary);
-// color: white;
-// padding: 8px 16px;
-// border-radius: 4px;
-// font-size: 14px;
-// font-weight: 500;
-// z-index: 1;
-// pointer-events: none;
-//}
-//
-///* Enhanced touch targets for mobile */
-//@media (hover: none) and (pointer: coarse) {
-// .upload-item {
-// min-height: 44px; /* Apple's recommended minimum touch target */
-// touch-action: manipulation;
-// }
-//
-// .upload-item [data-upload-id] {
-// cursor: grab;
-// -webkit-user-select: none;
-// user-select: none;
-// }
-//
-// .upload-item.dragging {
-// cursor: grabbing;
-// }
-//
-// /* Larger touch targets for action buttons */
-// .upload-item .actions button {
-// min-width: 44px;
-// min-height: 44px;
-// padding: 12px;
-// }
-//
-// /* Group containers more touch-friendly */
-// .item-grid.groups .empty-group {
-// padding: 20px;
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// font-size: 16px;
-// }
-//
-// .group-content {
-// padding: 0;
-// width: 100%;
-// }
-//
-// /* Better visual feedback for group areas */
-// .item-grid.group,
-// .item-grid.groups {
-// border: 2px solid transparent;
-// border-radius: 8px;
-// transition: border-color .2s ease, background-color .2s ease;
-// }
-//
-// .item-grid.group.dragover,
-// .item-grid.groups.dragover {
-// border-color: var(--accent-primary);
-// background-color: var(--accent-primary-alpha);
-// }
-//}
-//
-///* Haptic feedback animation */
-//@keyframes feedback {
-// 0% { transform: scale(1); }
-// 50% { transform: scale(1.05); }
-// 100% { transform: scale(1); }
-//}
-//
-//.feedback {
-// animation: feedback .2s ease;
-//}
-//
-///* Prevent text selection during touch drag */
-//.dragging,
-//.dragging * {
-// -webkit-user-select: none;
-// -moz-user-select: none;
-// -ms-user-select: none;
-// user-select: none;
-//}
-//
-///* Long press visual feedback */
-//.upload-item:active {
-// transform: scale(.98);
-// transition: transform .1s ease;
-//}
-//
-///* Better spacing for touch interfaces */
-//@media (hover: none) {
-//
-// .item-grid.groups {
-// gap: 16px;
-// }
-//
-// .group-item {
-// margin: 8px;
-// }
-//}
-//
-///* Accessibility improvements for touch */
-//.upload-item:focus-visible {
-// outline: 3px solid var(--accent-primary);
-// outline-offset: 2px;
-//}
-//
-///* High contrast mode support */
-//@media (prefers-contrast: high) {
-// .dragover {
-// border-width: 3px;
-// border-style: solid;
-// }
-//
-// .drag-preview {
-// border-width: 3px;
-// }
-//}
-///* Touch Drag & Drop Styles */
-//
-///* Original item being dragged */
-//.upload-item.dragging {
-// opacity: .5;
-// transform: scale(.95);
-// transition: opacity .2s ease, transform .2s ease;
-//}
-//
-///* Touch drag preview element */
-//.drag-preview {
-// background: var(--bg-elevated);
-// border: 2px solid var(--accent-primary);
-// cursor: grabbing;
-// user-select: none;
-// -webkit-user-select: none;
-// -webkit-user-drag: none;
-//}
-//
-//.drag-preview * {
-// pointer-events: none;
-//}
-//
-///* Multi-item drag preview */
-//.drag-preview.multi-item {
-// /* Container styles handled in JS for dynamic positioning */
-//}
-//.drag-preview img {
-// max-width: 200px;
-// width: 100%;
-// height: 100%;
-// aspect-ratio: 1;
-//}
-//
-//.drag-preview.multi-item .selection-count-badge {
-// background: var(--accent-primary);
-// color: white;
-// border: 2px solid white;
-// font-weight: bold;
-// text-shadow: 0 1px 2px rgba(0,0,0,.5);
-//}
-//
-///* Enhanced visual feedback for multiple selection */
-//.upload-item.selected.dragging {
-// opacity: .3;
-// transform: scale(.9);
-// border: 2px solid var(--accent-primary);
-//}
-//
-///* Stacked preview animation */
-//@keyframes stack-preview {
-// 0% {
-// transform: translateX(0) translateY(0) rotate(0deg) scale(1);
-// opacity: 1;
-// }
-// 100% {
-// transform: translateX(var(--stack-x, 0)) translateY(var(--stack-y, 0)) rotate(var(--stack-rotation, 0deg)) scale(1.05);
-// opacity: var(--stack-opacity, .8);
-// }
-//}
-//
-//.drag-preview.multi-item > * {
-// animation: stack-preview .2s ease-out forwards;
-//}
-//
-///* Drop target states for touch */
-//.dragover {
-// background-color: var(--accent-primary-alpha);
-// border: 2px dashed var(--accent-primary);
-// transform: scale(1.02);
-// transition: all .2s ease;
-//}
-//
-//.dragover::after {
-// content: "Drop here";
-// position: absolute;
-// top: 50%;
-// left: 50%;
-// transform: translate(-50%, -50%);
-// background: var(--accent-primary);
-// color: white;
-// padding: 8px 16px;
-// border-radius: 4px;
-// font-size: 14px;
-// font-weight: 500;
-// z-index: 1;
-// pointer-events: none;
-//}
-//
-///* Enhanced drop feedback for multiple items */
-//.dragover.multi-drop::after {
-// content: "Drop " attr(data-item-count) " items here";
-// padding: 10px 20px;
-// font-size: 16px;
-// border-radius: 6px;
-// box-shadow: 0 4px 12px rgba(0,0,0,.3);
-//}
-//
-///* Pulsing animation for multi-item drop zones */
-//.dragover.multi-drop {
-// animation: multi-drop-pulse 1s infinite alternate;
-//}
-//
-//@keyframes multi-drop-pulse {
-// 0% {
-// background-color: var(--accent-primary-alpha);
-// transform: scale(1.02);
-// }
-// 100% {
-// background-color: var(--accent-secondary-alpha, var(--accent-primary-alpha));
-// transform: scale(1.04);
-// }
-//}
-//
-///* Enhanced touch targets for mobile */
-//@media (hover: none) and (pointer: coarse) {
-// .upload-item {
-// min-height: 44px; /* Apple's recommended minimum touch target */
-// touch-action: manipulation;
-// }
-//
-// .upload-item [data-upload-id] {
-// cursor: grab;
-// -webkit-user-select: none;
-// user-select: none;
-// }
-//
-// .upload-item.dragging {
-// cursor: grabbing;
-// }
-//
-// /* Larger touch targets for action buttons */
-// .upload-item .actions button {
-// min-width: 44px;
-// min-height: 44px;
-// padding: 12px;
-// }
-//
-// /* Group containers more touch-friendly */
-// .item-grid.groups .empty-group {
-// padding: 1rem;
-// margin: 1rem;
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// font-size: 16px;
-// }
-//
-// /* Better visual feedback for group areas */
-// .item-grid.group,
-// .item-grid.groups {
-// border: 2px solid transparent;
-// border-radius: 8px;
-// transition: border-color .2s ease, background-color .2s ease;
-// }
-//
-// .item-grid.group.dragover,
-// .item-grid.groups.dragover {
-// border-color: var(--accent-primary);
-// background-color: var(--accent-primary-alpha);
-// }
-//}
-//
-///* Haptic feedback animation */
-//@keyframes feedback {
-// 0% { transform: scale(1); }
-// 50% { transform: scale(1.05); }
-// 100% { transform: scale(1); }
-//}
-//
-//.feedback {
-// animation: feedback .2s ease;
-//}
-//
-///* Prevent text selection during touch drag */
-//.dragging,
-//.dragging * {
-// -webkit-user-select: none;
-// -moz-user-select: none;
-// -ms-user-select: none;
-// user-select: none;
-//}
-//
-///* Long press visual feedback */
-//.upload-item:active {
-// transform: scale(.98);
-// transition: transform .1s ease;
-//}
-//
-///* Better spacing for touch interfaces */
-//@media (hover: none) {
-//
-// .item-grid.groups {
-// gap: 16px;
-// }
-//
-// .group-item {
-// margin: 8px;
-// }
-//}
-//
-///* Accessibility improvements for touch */
-//.upload-item:focus-visible {
-// outline: 3px solid var(--accent-primary);
-// outline-offset: 2px;
-//}
-//
-///* Enhanced selection states for touch */
-//.upload-item.selected {
-// border: 2px solid var(--accent-primary);
-// background-color: var(--accent-primary-alpha);
-//}
-//
-//.upload-item.selected.dragging {
-// border-color: var(--accent-secondary);
-// background-color: var(--accent-secondary-alpha);
-//}
-//
-///* Touch feedback animations */
-//.feedback-start {
-// animation: feedback-start .3s ease;
-//}
-//
-//.feedback-success {
-// animation: feedback-success .5s ease;
-//}
-//
-//.feedback-error {
-// animation: feedback-error .5s ease;
-//}
-//
-//@keyframes feedback-start {
-// 0% { transform: scale(1); }
-// 50% { transform: scale(1.05); background-color: var(--accent-primary-alpha); }
-// 100% { transform: scale(1); }
-//}
-//
-//@keyframes feedback-success {
-// 0% { transform: scale(1); }
-// 25% { transform: scale(1.1); background-color: var(--success-alpha); }
-// 50% { transform: scale(1.05); }
-// 100% { transform: scale(1); }
-//}
-//
-//@keyframes feedback-error {
-// 0% { transform: translateX(0); }
-// 25% { transform: translateX(-5px); background-color: var(--error-alpha); }
-// 50% { transform: translateX(5px); }
-// 75% { transform: translateX(-3px); }
-// 100% { transform: translateX(0); }
-//}
-//
-///* Multi-selection visual enhancements */
-//.upload-item.selected::after {
-// content: "✓";
-// position: absolute;
-// top: 8px;
-// right: 8px;
-// background: var(--accent-primary);
-// color: white;
-// border-radius: 50%;
-// width: 20px;
-// height: 20px;
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// font-size: 12px;
-// font-weight: bold;
-// z-index: 10;
-//}
-//
-///* Selection count in drag preview */
-//.selection-count-badge {
-// animation: badge-appear .3s ease;
-//}
-//
-//@keyframes badge-appear {
-// 0% {
-// transform: scale(0);
-// opacity: 0;
-// }
-// 50% {
-// transform: scale(1.2);
-// opacity: 1;
-// }
-// 100% {
-// transform: scale(1);
-// opacity: 1;
-// }
-//}
-//
-///* High contrast mode support */
-//@media (prefers-contrast: high) {
-// .dragover {
-// border-width: 3px;
-// border-style: solid;
-// }
-//
-// .drag-preview {
-// border-width: 3px;
-// }
-//
-// .upload-item.selected {
-// border-width: 3px;
-// }
-//}
-//
-//.drag-preview.multi-item {
-// position: relative;
-// width: 120px !important;
-// height: 120px !important;
-// /* Ensure consistent sizing for multi-item previews */
-//}
-//
-//.drag-preview.multi-item img {
-// max-width: 100%;
-// max-height: 100%;
-// width: 100%;
-// height: 100%;
-// object-fit: cover;
-// aspect-ratio: 1;
-// border-radius: 6px;
-//}
-//
-///* Fix stacked item positioning */
-//.drag-preview.multi-item > .upload-item {
-// position: absolute;
-// width: 100%;
-// height: 100%;
-// border-radius: 6px;
-// overflow: hidden;
-//}
-//
-//.drag-preview.multi-item > .upload-item:nth-child(1) {
-// top: 0;
-// left: 0;
-// z-index: 3;
-// opacity: 1;
-// transform: rotate(-2deg);
-//}
-//
-//.drag-preview.multi-item > .upload-item:nth-child(2) {
-// top: 4px;
-// left: 4px;
-// z-index: 2;
-// opacity: .85;
-// transform: rotate(1deg);
-//}
-//
-//.drag-preview.multi-item > .upload-item:nth-child(3) {
-// top: 8px;
-// left: 8px;
-// z-index: 1;
-// opacity: .7;
-// transform: rotate(-1deg);
-//}
-//
-///* Selection count badge positioning */
-//.drag-preview.multi-item .selection-count-badge {
-// position: absolute;
-// top: -8px;
-// right: -8px;
-// background: var(--accent-primary);
-// color: white;
-// border: 2px solid white;
-// border-radius: 50%;
-// width: 28px;
-// height: 28px;
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// font-size: 12px;
-// font-weight: bold;
-// font-family: system-ui, -apple-system, sans-serif;
-// box-shadow: 0 2px 8px rgba(0,0,0,.3);
-// z-index: 20;
-// line-height: 1;
-//}
-//
-///* Ensure drag preview is always visible */
-//.drag-preview {
-// pointer-events: none;
-// z-index: 10000 !important;
-//}
-//
-///* Remove any transforms that might interfere with positioning */
-//.drag-preview.multi-item .upload-item .progress,
-//.drag-preview.multi-item .upload-item .actions,
-//.drag-preview.multi-item .upload-item .status,
-//.drag-preview.multi-item .upload-item details {
-// display: none !important;
-//}
-//
-///* Clean up the visual during multi-drag */
-//.upload-item.selected.dragging {
-// opacity: .3;
-// transform: scale(.95);
-// border: 2px dashed var(--accent-primary);
-// background-color: var(--accent-primary-alpha);
-//}
-//
-///* Improved drop zone feedback for multiple items */
-//.dragover.multi-drop {
-// animation: multi-drop-pulse .8s infinite ease-in-out;
-// border-style: solid;
-//}
-//
-//@keyframes multi-drop-pulse {
-// 0%, 100% {
-// background-color: var(--accent-primary-alpha);
-// transform: scale(1.02);
-// border-color: var(--accent-primary);
-// }
-// 50% {
-// background-color: var(--accent-secondary-alpha, var(--accent-primary-alpha));
-// transform: scale(1.04);
-// border-color: var(--accent-secondary, var(--accent-primary));
-// }
-//}
-//
-///* Enhanced feedback for successful drops */
-//@keyframes drop-success {
-// 0% {
-// background-color: var(--success-alpha);
-// transform: scale(1.1);
-// }
-// 100% {
-// background-color: transparent;
-// transform: scale(1);
-// }
-//}
-//
-//.drop-success {
-// animation: drop-success .6s ease-out;
-//}
-//
-//.drag-preview {
-// border: 2px solid red !important;
-// background: rgba(255, 0, 0, .1) !important;
-//}
-//
-//.drag-preview.multi-item {
-// border: 2px solid blue !important;
-// background: rgba(0, 0, 255, .1) !important;
-//}
-//
-//.dragging {
-// opacity: .5 !important;
-// border: 2px dashed orange !important;
-//}
-//
-//.dragover {
-// background: rgba(0, 255, 0, .2) !important;
-// border: 2px dashed green !important;
-//}
-//
-//.dragover.multi-drop::before {
-// content: "Drop " attr(data-item-count) " items here";
-// position: absolute;
-// top: 50%;
-// left: 50%;
-// transform: translate(-50%, -50%);
-// background: rgba(0, 0, 0, .8);
-// color: white;
-// padding: 8px 16px;
-// border-radius: 4px;
-// font-size: 14px;
-// font-weight: bold;
-// z-index: 1000;
-// pointer-events: none;
-//}
-//
-//.drag-preview {
-// /* Ensure drag preview is always visible */
-// position: fixed !important;
-// z-index: 9999 !important;
-// pointer-events: none !important;
-//
-// /* Improve visual appearance */
-// box-shadow: 0 8px 25px rgba(0,0,0,.3);
-// border-radius: 8px;
-// overflow: hidden;
-//
-// /* Ensure it's not affected by parent transforms or overflow */
-// transform-style: preserve-3d;
-//}
-//
-//.drag-preview.multi-item {
-// /* Ensure proper stacking context for multi-item previews */
-// isolation: isolate;
-//}
-//
-//.drag-preview .upload-item {
-// /* Ensure child items are positioned correctly */
-// border-radius: 4px;
-// overflow: hidden;
-// box-shadow: 0 2px 8px rgba(0,0,0,.15);
-//}
-//
-//.drag-preview .upload-item img {
-// /* Prevent image layout issues during drag */
-// max-width: 100%;
-// height: auto;
-// display: block;
-//}
-//
-///* Debug styles - remove in production */
-//.drag-preview {
-// border: 2px solid red !important;
-//}
-//
-//.drag-preview.multi-item {
-// border: 2px solid blue !important;
-//}
-//
-///* Hide complex UI elements in drag preview */
-//.drag-preview details,
-//.drag-preview .actions,
-//.drag-preview .progress {
-// display: none !important;
-//}
-//
-///* Simplify the preview to just show the image */
-//.drag-preview .preview {
-// width: 100% !important;
-// height: 100% !important;
-//}
-//
-//.drag-preview .preview img {
-// width: 100% !important;
-// height: 100% !important;
-// object-fit: cover !important;
-//}
-//
-//
-//.upload-group summary {
-// padding: 0!important;
-//}
-//
-//
-///* Mobile-First Image Grouping Layout - Using Existing Templates */
-//
-///* Base mobile layout - stack everything vertically */
-//@media (max-width: 768px) {
-//
-// .item-grid.preview details,
-// .item-grid.group details {
-// display: none;
-// }
-// .group-display {
-// display: flex;
-// flex-direction: column;
-// gap: 0;
-// height: calc(100vh - var(--btn) - var(--btn));
-// position: fixed;
-// top: var(--btn);
-// bottom: var(--btn);
-// left: 0;
-// right: 0;
-// z-index:999;
-// }
-//
-// .preview-wrap,
-// .sidebar {
-// flex: 1;
-// display: flex;
-// flex-direction: column;
-// min-height: 0;
-// overflow: hidden;
-// overflow-y: auto;
-// }
-// /* Preview section - top half of screen */
-// .preview-wrap {
-// background: var(--base);
-// border-bottom: 2px solid var(--action-0);
-// }
-//
-// /* Preview actions - sticky at top */
-// .preview-actions {
-// position: sticky;
-// top: 0;
-// z-index: 10;
-// background: var(--base);
-// border-bottom: 1px solid var(--base-200);
-// flex-shrink: 0;
-// }
-//
-// /* Preview grid - scrollable area */
-// .item-grid.preview {
-// flex: 1;
-// grid-template-columns: repeat(3, 1fr);
-// gap: .75rem;
-// padding: .5rem;
-// margin-top: 0;
-// }
-// .item-grid + .hint {
-// margin-top: 0;
-// }
-//
-// /* Groups section - bottom half with sticky header */
-// .sidebar {
-// background: var(--base-200);
-// border-top: 3px solid var(--action-50);
-// }
-//
-// /* Groups header - sticky */
-// .sidebar .header {
-// border-bottom: 1px solid var(--base-200);
-// padding: 1rem;
-// flex-shrink: 0;
-// }
-//
-// /* Create group button */
-// .create-group-from-selection {
-// margin: 0 1rem 1rem 1rem;
-// flex-shrink: 0;
-// min-height: 44px; /* Touch target */
-// padding: .75rem 1rem;
-// font-size: .9rem;
-// }
-//
-// /* Groups container - scrollable */
-// .item-grid.groups {
-// flex: 1;
-// margin: 0;
-// grid-template-columns: repeat(2, 1fr); /* Single column on mobile */
-// gap: 1rem;
-// }
-//
-// /* Enhanced empty group for mobile */
-// .empty-group {
-// aspect-ratio: unset;
-// grid-column: 1/-1;
-// transition: all .2s ease;
-// }
-//
-// .item-grid.groups .item-grid {
-// grid-template-columns: repeat(3, 1fr);
-// }
-// .item-grid.groups .item-grid details {
-// display: none;
-// }
-//
-// .empty-group:hover,
-// .empty-group.dragover {
-// border-color: var(--action-0);
-// background: var(--action-rgb-subtle-hover);
-// transform: scale(1.02);
-// }
-//
-// /* Upload button - fixed at bottom */
-// .submit-uploads {
-// position: fixed !important;
-// bottom: calc(var(--btn) + .5rem);
-// right: .5rem;
-// z-index: 20;
-// height: 3rem;
-// font-size: 1.1rem;
-// font-weight: 600;
-// box-shadow: rgba(var(--base-rgb),var(--op-45)) var(--shdw);
-// border-radius: var(--radius-outer);
-// }
-//
-// .submit-uploads:hover {
-// transform: translateY(-2px);
-// box-shadow: 0 8px 25px rgba(0,0,0,.2);
-// }
-//
-// /* Enhanced upload items for mobile */
-// .upload-item {
-// border-radius: var(--radius);
-// overflow: hidden;
-// background: var(--base);
-// border: 1px solid var(--base-200);
-// transition: transform .2s ease;
-// }
-//
-// .upload-item:hover {
-// transform: scale(1.02);
-// }
-//
-// /* Mobile-friendly actions overlay */
-// .upload-item .actions {
-// padding: .5rem;
-// }
-//
-// .upload-item .actions button {
-// min-width: 44px;
-// min-height: 44px;
-// padding: .75rem;
-// border-radius: var(--radius);
-// }
-//
-// /* Better checkbox targets */
-// .upload-item .upload-select + label {
-// min-width: 44px;
-// min-height: 44px;
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// border-radius: var(--radius);
-// }
-//
-// /* Enhanced group styling for mobile */
-// .upload-group {
-// background: var(--base-100);
-// border-radius: var(--radius);
-// border: 1px solid var(--base-200);
-// padding: 1rem;
-// margin-bottom: 1rem;
-// }
-//
-// .upload-group .group-header {
-// display: flex;
-// justify-content: space-between;
-// align-items: center;
-// margin-bottom: 1rem;
-// padding-bottom: .5rem;
-// border-bottom: 1px solid var(--base-200);
-// flex-wrap: wrap;
-// gap: .5rem;
-// }
-//
-// .upload-group .group-actions {
-// display: flex;
-// gap: .5rem;
-// flex-wrap: wrap;
-// }
-//
-// .upload-group .group-actions button {
-// flex: 1;
-// min-width: 100px;
-// min-height: 44px;
-// padding: .5rem .75rem;
-// font-size: .9rem;
-// border-radius: var(--radius);
-// }
-//
-// .upload-group .item-grid.group {
-// grid-template-columns: repeat(2, 1fr);
-// gap: .5rem;
-// margin-bottom: 1rem;
-// }
-//
-// .upload-group .group-item {
-// aspect-ratio: 1;
-// border-radius: 4px;
-// overflow: hidden;
-// border: 1px solid var(--base-200);
-// }
-//
-// /* Hide file upload container on mobile when in grouping mode */
-// .group-display:not([hidden]) ~ .file-upload-container {
-// display: none;
-// }
-//
-// /* Enhanced selection controls for mobile */
-// .selection-controls {
-// display: flex;
-// flex-direction: column;
-// gap: 0;
-// }
-//
-// .selection-controls .selected {
-// display: flex;
-// justify-content: space-between;
-// align-items: center;
-// gap: .5rem;
-// }
-//
-// .selection-actions {
-// display: flex;
-// padding: 0;
-// }
-//
-// .selection-actions button {
-// flex: 1;
-// padding: .25rem;
-// font-size: .9rem;
-// border-radius: var(--radius);
-// }
-//
-// /* Enhanced dragging states for mobile */
-// .upload-item.dragging {
-// opacity: .7;
-// transform: scale(.95) rotate(3deg);
-// z-index: 1000;
-// box-shadow: 0 8px 25px rgba(0,0,0,.3);
-// }
-//
-// .dragover {
-// background: var(--action-rgb-subtle) !important;
-// border-color: var(--action-0) !important;
-// transform: scale(1.05);
-// animation: mobile-drop-pulse .8s infinite ease-in-out;
-// }
-//
-// @keyframes mobile-drop-pulse {
-// 0%, 100% {
-// background-color: var(--action-rgb-subtle);
-// transform: scale(1.02);
-// }
-// 50% {
-// background-color: var(--action-rgb-subtle-hover);
-// transform: scale(1.04);
-// }
-// }
-//
-// /* Enhanced selection states */
-// .upload-item.selected {
-// border: 2px solid var(--action-0);
-// background-color: var(--action-rgb-subtle);
-// }
-//
-// .upload-item.selected::after {
-// content: '✓';
-// position: absolute;
-// top: .5rem;
-// right: .5rem;
-// background: var(--action-0);
-// color: white;
-// border-radius: 50%;
-// width: 24px;
-// height: 24px;
-// display: flex;
-// align-items: center;
-// justify-content: center;
-// font-size: 12px;
-// font-weight: bold;
-// z-index: 10;
-// animation: selection-pop .3s ease;
-// }
-//
-// @keyframes selection-pop {
-// 0% {
-// transform: scale(0);
-// opacity: 0;
-// }
-// 70% {
-// transform: scale(1.2);
-// opacity: 1;
-// }
-// 100% {
-// transform: scale(1);
-// opacity: 1;
-// }
-// }
-//
-// /* Enhanced details/summary for mobile */
-// .upload-item details summary {
-// padding: .75rem;
-// background: var(--base-100);
-// border-radius: var(--radius);
-// cursor: pointer;
-// display: flex;
-// align-items: center;
-// gap: .5rem;
-// font-size: .9rem;
-// font-weight: 500;
-// min-height: 44px; /* Touch target */
-// }
-//
-// .upload-item details[open] summary {
-// border-radius: var(--radius) var(--radius) 0 0;
-// border-bottom: 1px solid var(--base-200);
-// }
-//
-// /* Enhanced forms for mobile */
-// .upload-meta input,
-// .upload-meta textarea {
-// padding: .75rem;
-// font-size: 16px; /* Prevents zoom on iOS */
-// border-radius: var(--radius);
-// border: 2px solid var(--base-200);
-// transition: border-color .2s ease;
-// }
-//
-// .upload-meta input:focus,
-// .upload-meta textarea:focus {
-// border-color: var(--action-0);
-// outline: none;
-// box-shadow: 0 0 0 3px var(--action-rgb-subtle);
-// }
-//}
-//
-///* Tablet adjustments */
-//@media (min-width: 769px) and (max-width: 1024px) {
-// .group-display {
-// grid-template-columns: 1fr;
-// gap: 1.5rem;
-// }
-//
-// .item-grid.preview {
-// grid-template-columns: repeat(4, 1fr);
-// }
-//
-// .item-grid.groups {
-// grid-template-columns: repeat(2, 1fr);
-// }
-//}
-//
-//
-//body:has(.group-display:not([hidden])) {
-// overflow:hidden;
-//}
-//
-//.today_hours .group-fields {
-// width: 100%;
-// display: flex;
-// justify-content: center;
-// gap: 3rem;
-//}
-//.today_hours .field {
-// margin: 0;
-//}
-//
-//
-//.jvb-integration-connection {
-// background: var(--bg-white, #EFEFEF);
-// border: 2px solid #ddd;
-// border-radius: 8px;
-// padding: 1.5rem;
-// margin-bottom: 1.5rem;
-// position: relative;
-// transition: all 0.3s ease;
-//}
-//
-//.jvb-integration-connection.connected {
-// border-color: #22c55e;
-// box-shadow: 0 2px 8px rgba(34, 197, 94, 0.1);
-//}
-//
-//.jvb-integration-connection.disconnected {
-// border-color: #ef4444;
-// box-shadow: 0 2px 8px rgba(239, 68, 68, 0.1);
-//}
-//
-///* Integration Grid */
-//.jvb-integrations-grid {
-// display: grid;
-// grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
-// gap: 1.5rem;
-// margin-top: 1rem;
-//}
-//
-///* Connection Header */
-//.jvb-connection-header {
-// display: flex;
-// justify-content: space-between;
-// align-items: flex-start;
-// margin-bottom: 1.5rem;
-// padding-bottom: 1rem;
-// border-bottom: 2px solid #ddd;
-//}
-//
-//.jvb-service-info h3.jvb-service-name {
-// margin: 0 0 0.25rem 0;
-// font-size: 1.4rem;
-// font-weight: bold;
-// text-transform: uppercase;
-// letter-spacing: 1px;
-// color: var(--bg-black, #1B1B1B);
-// font-family: 'Courier New', monospace;
-//}
-//
-//.jvb-service-description {
-// margin: 0;
-// color: #666;
-// font-size: 0.9rem;
-// line-height: 1.4;
-//}
-//
-//.jvb-connection-status {
-// text-align: right;
-// min-width: 150px;
-//}
-//
-//.jvb-status-indicator {
-// font-size: 1.2rem;
-// margin-right: 0.5rem;
-//}
-//
-//.jvb-connection-status.connected .jvb-status-indicator {
-// color: #22c55e;
-//}
-//
-//.jvb-connection-status.disconnected .jvb-status-indicator {
-// color: #ef4444;
-//}
-//
-//.jvb-status-text {
-// font-weight: bold;
-// font-size: 0.9rem;
-// text-transform: uppercase;
-// letter-spacing: 0.5px;
-// font-family: 'Courier New', monospace;
-//}
-//
-//.jvb-last-tested {
-// display: block;
-// color: #888;
-// font-size: 0.75rem;
-// margin-top: 0.25rem;
-//}
-//
-///* Settings Section */
-//.jvb-settings-section,
-//.jvb-integration-settings {
-// margin-bottom: 1.5rem;
-//}
-//
-//.jvb-integration-settings h4 {
-// margin: 0 0 1rem 0;
-// font-size: 1.1rem;
-// font-weight: bold;
-// color: var(--bg-black, #1B1B1B);
-// font-family: 'Courier New', monospace;
-// text-transform: uppercase;
-// letter-spacing: 0.5px;
-//}
-//
-///* Form Fields */
-//.jvb-form-field {
-// margin-bottom: 1rem;
-//}
-//
-//.jvb-form-field label {
-// display: block;
-// font-weight: bold;
-// margin-bottom: 0.5rem;
-// color: var(--bg-black, #1B1B1B);
-// font-family: 'Courier New', monospace;
-// text-transform: uppercase;
-// font-size: 0.85rem;
-// letter-spacing: 0.5px;
-//}
-//
-//.jvb-form-field input,
-//.jvb-form-field select {
-// width: 100%;
-// padding: 0.75rem;
-// border: 2px solid #ddd;
-// border-radius: 4px;
-// font-size: 0.9rem;
-// background: white;
-// transition: border-color 0.3s ease;
-//}
-//
-//.jvb-form-field input:focus,
-//.jvb-form-field select:focus {
-// outline: none;
-// border-color: var(--action-color, #FF0080);
-// box-shadow: 0 0 0 3px rgba(255, 0, 128, 0.1);
-//}
-//
-//.jvb-form-field .description {
-// font-size: 0.8rem;
-// color: #666;
-// margin-top: 0.5rem;
-// line-height: 1.4;
-//}
-//
-///* Checkbox Labels */
-//.jvb-checkbox-label {
-// display: flex !important;
-// align-items: center;
-// gap: 0.5rem;
-// text-transform: none !important;
-// font-weight: normal !important;
-// cursor: pointer;
-//}
-//
-//.jvb-checkbox-label input[type="checkbox"] {
-// width: auto !important;
-// margin: 0;
-//}
-//
-///* Advanced Settings */
-//.jvb-advanced-settings {
-// margin-top: 1rem;
-// padding: 1rem;
-// background: #f9f9f9;
-// border-radius: 4px;
-// border: 1px solid #e5e5e5;
-//}
-//
-//.jvb-advanced-settings summary {
-// font-weight: bold;
-// cursor: pointer;
-// margin-bottom: 1rem;
-// color: var(--action-color, #FF0080);
-// font-family: 'Courier New', monospace;
-// text-transform: uppercase;
-// font-size: 0.85rem;
-// letter-spacing: 0.5px;
-//}
-//
-///* Settings Help */
-//.jvb-settings-help {
-// background: #f0f8ff;
-// border: 1px solid #b3d9ff;
-// border-radius: 4px;
-// padding: 1rem;
-// margin-top: 1rem;
-//}
-//
-//.jvb-settings-help ol {
-// margin: 0.5rem 0 0 1.5rem;
-// padding: 0;
-//}
-//
-//.jvb-settings-help a {
-// color: var(--action-color, #FF0080);
-// text-decoration: none;
-// font-weight: bold;
-//}
-//
-//.jvb-settings-help a:hover {
-// text-decoration: underline;
-//}
-//
-///* Connection Actions */
-//.jvb-connection-actions {
-// display: flex;
-// gap: 0.75rem;
-// flex-wrap: wrap;
-// border-top: 2px solid #ddd;
-// padding-top: 1rem;
-// align-items: center;
-//}
-//
-//.jvb-connection-actions .button {
-// font-family: 'Courier New', monospace;
-// font-weight: bold;
-// text-transform: uppercase;
-// letter-spacing: 0.5px;
-// border-radius: 4px;
-// padding: 0.6rem 1.2rem;
-// font-size: 0.8rem;
-// cursor: pointer;
-// transition: all 0.2s ease;
-//}
-//
-//.jvb-connect-button.button-primary {
-// background: var(--action-color, #FF0080);
-// border-color: var(--action-color, #FF0080);
-// color: white;
-// box-shadow: 0 2px 4px rgba(255, 0, 128, 0.2);
-//}
-//
-//.jvb-connect-button.button-primary:hover {
-// background: #e6007a;
-// border-color: #e6007a;
-// transform: translateY(-1px);
-// box-shadow: 0 4px 8px rgba(255, 0, 128, 0.3);
-//}
-//
-//.jvb-test-button {
-// background: white;
-// border-color: #22c55e;
-// color: #22c55e;
-//}
-//
-//.jvb-test-button:hover {
-// background: #22c55e;
-// color: white;
-//}
-//
-//.jvb-sync-button {
-// background: white;
-// border-color: var(--action-color, #FF0080);
-// color: var(--action-color, #FF0080);
-//}
-//
-//.jvb-sync-button:hover {
-// background: var(--action-color, #FF0080);
-// color: white;
-//}
-//
-//.jvb-disconnect-button {
-// color: #ef4444 !important;
-// border: none !important;
-// background: none !important;
-// text-decoration: underline;
-// margin-left: auto;
-//}
-//
-//.jvb-disconnect-button:hover {
-// color: #dc2626 !important;
-// background: none !important;
-//}
-//
-///* Loading States */
-//.jvb-integration-form.loading .button {
-// opacity: 0.7;
-// pointer-events: none;
-//}
-//
-//.jvb-integration-form.loading .jvb-connect-button::after {
-// content: ' (Saving...)';
-//}
-//
-//.jvb-integration-form.loading .jvb-test-button::after {
-// content: ' (Testing...)';
-//}
-//
-///* Responsive Design */
-//@media (max-width: 768px) {
-// .jvb-integrations-grid {
-// grid-template-columns: 1fr;
-// }
-//
-// .jvb-connection-header {
-// flex-direction: column;
-// gap: 1rem;
-// }
-//
-// .jvb-connection-status {
-// text-align: left;
-// min-width: auto;
-// }
-//
-// .jvb-connection-actions {
-// flex-direction: column;
-// }
-//
-// .jvb-connection-actions .button {
-// width: 100%;
-// text-align: center;
-// }
-//
-// .jvb-disconnect-button {
-// margin-left: 0 !important;
-// }
-//}
-//
-//form .tabs {
-// position: sticky;
-// top: calc(var(--btn) + 2rem);
-// left: 0;
-// right: 0;
-// z-index: 50;
-// background: var(--base);
-//}
-//
-//.form-actions {
-// display: flex;
-//}
-//
-//.spinner {
-// width: 12px;
-// height: 12px;
-// border: 2px solid transparent;
-// border-top: 2px solid var(--action-50);
-// border-radius: 50%;
-// animation: spin 1s linear infinite;
-//}
-//
-//@keyframes spin {
-// 0% { transform: rotate(0deg); }
-// 100% { transform: rotate(360deg); }
-//}
diff --git a/src/forms/view.js b/src/forms/view.js
deleted file mode 100644
index 788e929..0000000
--- a/src/forms/view.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * view.js
- * Frontend JavaScript for the Form Block
- * Handles form validation and submission
- */
-/**
- * view.js
- * Frontend JavaScript for the Form Block
- */
-class FormBlock {
- constructor() {
- this.controller = window.jvbForm;
-
- document.querySelectorAll('.jvb-form-block form').forEach(form => {
- this.controller.registerForm(form, {
- cache: true,
- autoUpload: false,
- imageMeta: false,
- });
- });
-
- this.controller.subscribe((event, data) => {
- if (event === 'form-submit') {
- this.handleFormSubmission(data).then(()=>{});
- }
- });
- }
-
- async handleFormSubmission(eventData) {
- const { config, data } = eventData;
- const form = config.element;
-
- const submitData = new FormData();
-
- // Add regular form fields
- for (const [key, value] of Object.entries(data)) {
- if (Array.isArray(value)) {
- value.forEach(v => submitData.append(`${key}[]`, v));
- } else if (typeof value === 'object' && value !== null) {
- submitData.append(key, JSON.stringify(value));
- } else {
- submitData.append(key, value);
- }
- }
- config.element.querySelectorAll('[name="form_id"],[name="form_type"],[name="timestamp"],[name="cf-turnstile-response"]').forEach(input => {
- submitData.append(input.name, input.value);
- });
-
- // Add uploaded files
- if (window.jvbUploads) {
- try {
- const files = await window.jvbUploads.getFilesForForm(form);
-
- files.forEach(({ file, fieldName }) => {
- submitData.append(`${fieldName}[]`, file);
- });
- } catch (error) {
- console.error('Error getting files:', error);
- }
- }
-
- this.controller.showFormStatus(config.id, 'uploading');
-
- try {
- const response = await fetch(`${jvbSettings.api}forms`, {
- method: 'POST',
- credentials: 'same-origin',
- body: submitData
- });
-
- const result = await response.json();
-
- if (!response.ok) {
- this.controller.showFormStatus(config.id, 'error');
- this.controller.handleFormError(form, result);
- return;
- }
-
- this.controller.showFormStatus(config.id, 'submitted');
- // this.controller.handleFormSuccess(form, result);
- this.controller.showSummary({ changes: data, config: config });
- window.jvbA11y.announce('Form successfully submitted!');
-
- // Clean up uploaded files
- if (window.jvbUploads) {
- const uploadFields = form.querySelectorAll('[data-upload-field]');
- for (const field of uploadFields) {
- const fieldId = window.jvbUploads.determineFieldId(field);
- await window.jvbUploads.clearFieldFromStores(fieldId);
- }
- }
-
- } catch (error) {
- console.error('Form submission error:', error);
- this.controller.showFormStatus(config.id, 'error');
- this.controller.handleFormError(form, {
- message: 'Network error. Please check your connection and try again.',
- code: 'network_error'
- });
- } finally {
- await this.controller.store.delete(config.id);
- }
- }
-}
-
-document.addEventListener('DOMContentLoaded', async function() {
- window.auth.subscribe(event => {
- if (event === 'auth-loaded') {
- new FormBlock();
- }
- });
-});
diff --git a/src/glossary/block.json b/src/glossary/block.json
deleted file mode 100644
index 2705831..0000000
--- a/src/glossary/block.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/glossary",
- "version": "0.1.0",
- "title": "Glossary of Terms",
- "category": "jvb",
- "icon": "excerpt-view",
- "description": "Outputs the terms",
- "example": {},
- "supports": {
- "html": false,
- "align": ["wide", "full"]
- },
- "textdomain": "jvb",
- "selectors": {
- "root": ".glossary"
- },
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css",
- "render": "file:./render.php",
- "viewScript": "file:./view.js"
-}
diff --git a/src/glossary/edit.js b/src/glossary/edit.js
deleted file mode 100644
index 4a115af..0000000
--- a/src/glossary/edit.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Retrieves the translation of text.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
- */
-import { __ } from '@wordpress/i18n';
-
-/**
- * React hook that is used to mark the block wrapper element.
- * It provides all the necessary props like the class name.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
- */
-import { useBlockProps } from '@wordpress/block-editor';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * Those files can contain any CSS code that gets applied to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './editor.scss';
-
-/**
- * The edit function describes the structure of your block in the context of the
- * editor. This represents what the editor will render when the block is used.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
- *
- * @return {Element} Element to render.
- */
-export default function Edit() {
- return (
- <p { ...useBlockProps() }>
- { __( 'Will output the glossary', 'jvb' ) }
- </p>
- );
-}
diff --git a/src/glossary/editor.scss b/src/glossary/editor.scss
deleted file mode 100644
index e69de29..0000000
--- a/src/glossary/editor.scss
+++ /dev/null
diff --git a/src/glossary/index.js b/src/glossary/index.js
deleted file mode 100644
index d82621b..0000000
--- a/src/glossary/index.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Registers a new block provided a unique name and an object defining its behavior.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-import { registerBlockType } from '@wordpress/blocks';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * All files containing `style` keyword are bundled together. The code used
- * gets applied both to the front of your site and to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './style.scss';
-
-/**
- * Internal dependencies
- */
-import Edit from './edit';
-import metadata from './block.json';
-
-/**
- * Every block starts by registering a new block type definition.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-registerBlockType( metadata.name, {
- /**
- * @see ./edit.js
- */
- edit: Edit,
-} );
diff --git a/src/glossary/index.php b/src/glossary/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/glossary/index.php
+++ /dev/null
diff --git a/src/glossary/render.php b/src/glossary/render.php
deleted file mode 100644
index 52aded4..0000000
--- a/src/glossary/render.php
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php
-/**
- * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
- */
-?>
-<p <?php echo get_block_wrapper_attributes(); ?>>
- <?php esc_html_e( 'Menu – hello from a dynamic block!', 'menu' ); ?>
-</p>
diff --git a/src/glossary/style.scss b/src/glossary/style.scss
deleted file mode 100644
index a367991..0000000
--- a/src/glossary/style.scss
+++ /dev/null
@@ -1,109 +0,0 @@
-:root {
- --navWidth: 40vw;
- @media (min-width: 768px) {
- --navWidth: 22vw;
- }
-}
-
-nav.glossary-index {
- position: fixed;
- top: 50%;
- transform: translateY(-50%);
- width: var(--navWidth);
- right: -8px;
- height: 60vh;
- z-index: var(--z-3);
-
- > ul {
- --dir: column;
- --align: flex-start;
- --justify: flex-start;
- --gap: 1px;
- touch-action: pan-y;
- max-height: 100%;
- height: 100%;
- width: 100%;
- overflow: hidden auto;
- scroll-behavior: smooth;
- }
- li, a {
- flex: 1;
- width: 100%;
- height: max-content;
- min-height: max(var(--chipchip), max-content);
- }
- a {
- --justify: center;
- padding: .25rem .5rem;
- hyphens: auto;
- background-color: rgba(var(--base-rgb),var(--op-45));
- word-wrap: anywhere;
- white-space: wrap;
- }
- a:hover,
- a:focus,
- a.active {
- background-color: rgba(var(--action-rgb), var(--op-6));
- color: var(--action-contrast);
- }
-}
-.glossary dd {
- margin-left: .5rem;
- width: calc(100% + .75rem);
-}
-.glossary dd,
-.glossary dt {
- position: relative;
- left: 0;
- transition: margin var(--trans-base),
- left var(--trans-base),
- width var(--trans-base);
-}
-.glossary dt:target,
-.glossary dt.active {
- outline: none;
- left: -1.5rem;
- padding: 0;
- color: var(--action-0);
-}
- .glossary dt:target + dd,
- .glossary dt.active + dd {
- left: -1.5rem;
- }
-
-main header,
-dl.glossary {
- grid-column: full;
- padding: 0 var(--navWidth) 0 2rem;
- @media (min-width:768px) {
- margin-left: auto;
- max-width: var(--content);
- margin-right: var(--navWidth);
- padding-right: var(--btn);
- }
-}
-
-@media (max-width: 768px) {
- .glossary {
- h2 {
- font-size: var(--txt-medium);
- }
- p {
- font-size: var(--txt-x-small);
- }
- }
- .glossary-index {
- li,a {
- height: fit-content;
- }
- a {
- font-size: var(--txt-x-small);
- padding: .25rem;
- min-height: 2em;
- }
- }
-
- body:has(.glossary) h1 {
- font-size: var(--txt-xx-large);
- }
-}
diff --git a/src/glossary/view.js b/src/glossary/view.js
deleted file mode 100644
index f68d59a..0000000
--- a/src/glossary/view.js
+++ /dev/null
@@ -1,184 +0,0 @@
-/**
- * Glossary Navigation Active State Manager
- * Handles highlighting active terms as they scroll into view
- * and syncing navigation with scroll position
- */
-class GlossaryNavigator {
- constructor(glossarySelector = 'dl.glossary', navSelector = 'nav.glossary-index') {
- this.glossary = document.querySelector(glossarySelector);
- this.nav = document.querySelector(navSelector);
-
- if (!this.glossary || !this.nav) return;
-
- this.terms = this.glossary.querySelectorAll('dt[id]');
- this.navList = this.nav.querySelector('ul');
- this.activeClass = 'active';
- this.currentActive = null;
-
- this.init();
- this.setupResizeHandler();
- }
-
- init() {
- // Set up Intersection Observer with screen-size appropriate margins
- const observerOptions = {
- root: null, // viewport
- rootMargin: this.getRootMargin(),
- threshold: 0
- };
-
- this.observer = new IntersectionObserver(
- (entries) => this.handleIntersection(entries),
- observerOptions
- );
-
- // Observe all terms
- this.terms.forEach(term => this.observer.observe(term));
-
- // Also handle manual scroll for edge cases
- this.handleScroll = this.debounce(() => this.checkActiveTerm(), 100);
- window.addEventListener('scroll', this.handleScroll, { passive: true });
- }
-
- getRootMargin() {
- // On larger screens: centered (50% from top and bottom)
- return '-50% 0px -50% 0px';
- }
-
- setupResizeHandler() {
- let resizeTimer;
- window.addEventListener('resize', () => {
- clearTimeout(resizeTimer);
- resizeTimer = setTimeout(() => {
- // Reinitialize observer with new margins on resize
- this.reinitialize();
- }, 250);
- });
- }
-
- reinitialize() {
- // Disconnect old observer
- if (this.observer) {
- this.observer.disconnect();
- }
-
- // Create new observer with updated margins
- this.init();
- }
-
- handleIntersection(entries) {
- // Find the entry that's intersecting
- const intersecting = entries.find(entry => entry.isIntersecting);
-
- if (intersecting) {
- this.setActive(intersecting.target);
- }
- }
-
- checkActiveTerm() {
- // Fallback method to find which term is in the trigger zone
- const remInPixels = parseFloat(getComputedStyle(document.documentElement).fontSize);
- const margin = remInPixels * 4;
-
- let closestTerm = null;
- let closestDistance = Infinity;
-
- this.terms.forEach(term => {
- const rect = term.getBoundingClientRect();
-
- const isInZone = rect.top + rect.height / 2 >= 0 && rect.top + rect.height / 2 <= window.innerHeight;
-
- if (isInZone) {
- // Find closest to the trigger point
- const triggerPoint = window.innerHeight / 2;
-
- const distance = Math.abs(rect.top - triggerPoint);
-
- if (distance < closestDistance) {
- closestDistance = distance;
- closestTerm = term;
- }
- }
- });
-
- if (closestTerm) {
- this.setActive(closestTerm);
- }
- }
-
- setActive(term) {
- if (this.currentActive === term) return;
-
- // Remove active class from previous term
- if (this.currentActive) {
- this.currentActive.classList.remove(this.activeClass);
- }
-
- // Add active class to current term
- term.classList.add(this.activeClass);
- this.currentActive = term;
-
- // Update navigation
- this.updateNavigation(term.id);
- }
-
- updateNavigation(termId) {
- // Remove active from all nav links
- const navLinks = this.nav.querySelectorAll('a');
- navLinks.forEach(link => link.classList.remove(this.activeClass));
-
- // Find and activate corresponding nav link
- const activeLink = this.nav.querySelector(`a[href="#${termId}"]`);
-
- if (activeLink) {
- activeLink.classList.add(this.activeClass);
-
- // Scroll the nav list to center the active link
- this.centerNavItem(activeLink);
- }
- }
-
- centerNavItem(link) {
- const listRect = this.navList.getBoundingClientRect();
- const linkRect = link.getBoundingClientRect();
-
- // Calculate position to center the link in the nav container
- const scrollTop = this.navList.scrollTop;
- const linkOffset = linkRect.top - listRect.top;
- const centerOffset = (listRect.height / 2) - (linkRect.height / 2);
-
- this.navList.scrollTo({
- top: scrollTop + linkOffset - centerOffset,
- behavior: 'smooth'
- });
- }
-
- debounce(func, wait) {
- let timeout;
- return function executedFunction(...args) {
- const later = () => {
- clearTimeout(timeout);
- func(...args);
- };
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- };
- }
-
- destroy() {
- if (this.observer) {
- this.observer.disconnect();
- }
- window.removeEventListener('scroll', this.handleScroll);
- }
-}
-
-
-// Initialize when DOM is ready
-if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', () => {
- new GlossaryNavigator();
- });
-} else {
- new GlossaryNavigator();
-}
diff --git a/src/gmbreviews/block.json b/src/gmbreviews/block.json
deleted file mode 100644
index 78634fd..0000000
--- a/src/gmbreviews/block.json
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/gmbreviews",
- "title": "GMB Reviews",
- "category": "jvb",
- "description": "Display top-rated Google My Business reviews with statistics and action buttons",
- "keywords": ["reviews", "google", "testimonials", "gmb", "ratings"],
- "textdomain": "jvb",
- "attributes": {
- "inheritUser": {
- "type": "boolean",
- "default": false
- },
- "count": {
- "type": "number",
- "default": 5
- },
- "showRating": {
- "type": "boolean",
- "default": true
- },
- "showDate": {
- "type": "boolean",
- "default": true
- },
- "showReviewLink": {
- "type": "boolean",
- "default": true
- },
- "showViewAllLink": {
- "type": "boolean",
- "default": true
- },
- "showStats": {
- "type": "boolean",
- "default": true
- },
- "minStars": {
- "type": "number",
- "default": 4,
- "minimum": 1,
- "maximum": 5
- }
- },
- "supports": {
- "html": false,
- "align": true,
- "color": {
- "text": true,
- "background": true,
- "link": true
- },
- "spacing": {
- "margin": true,
- "padding": true
- },
- "typography": {
- "fontSize": true,
- "lineHeight": true
- }
- },
- "render": "file:./render.php",
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css",
- "viewScript": "file:./view.js"
-}
diff --git a/src/gmbreviews/edit.js b/src/gmbreviews/edit.js
deleted file mode 100644
index 7f7e09f..0000000
--- a/src/gmbreviews/edit.js
+++ /dev/null
@@ -1,69 +0,0 @@
-// src/gmbreviews/edit.js
-import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
-import { PanelBody, RangeControl, ToggleControl } from '@wordpress/components';
-import { __ } from '@wordpress/i18n';
-import ServerSideRender from '@wordpress/server-side-render';
-
-export default function Edit({ attributes, setAttributes }) {
- const blockProps = useBlockProps();
- const { count, inheritUser, showStats, minStars, showViewAllLink, showRating, showDate, showReviewLink } = attributes;
-
- return (
- <>
- <InspectorControls>
- <PanelBody title={__('Review Settings', 'jvb')}>
- <ToggleControl
- label={__('Inherit User', 'jvb')}
- checked={inheritUser}
- onChange={(value) => setAttributes({ inheritUser: value })}
- />
- <RangeControl
- label={__('Number of Reviews', 'jvb')}
- value={count}
- onChange={(value) => setAttributes({ count: value })}
- min={1}
- max={20}
- />
- <ToggleControl
- label={__('Show Rating', 'jvb')}
- checked={showRating}
- onChange={(value) => setAttributes({ showRating: value })}
- />
- <ToggleControl
- label={__('Show Date', 'jvb')}
- checked={showDate}
- onChange={(value) => setAttributes({ showDate: value })}
- />
- <ToggleControl
- label={__('Show Review Link', 'jvb')}
- checked={showReviewLink}
- onChange={(value) => setAttributes({ showReviewLink: value })}
- />
- <ToggleControl
- label={__('Show Stats', 'jvb')}
- checked={showStats}
- onChange={(value) => setAttributes({ showStats: value })}
- />
- <ToggleControl
- label={__('Show All Reviews Link', 'jvb')}
- checked={showViewAllLink}
- onChange={(value) => setAttributes({ showViewAllLink: value })}
- />
- <RangeControl
- label={__('Minimum Rating', 'jvb')}
- value={minStars}
- onChange={(value) => setAttributes({ minStars: value })}
- min={1}
- max={5}
- />
- </PanelBody>
- </InspectorControls>
- <div {...blockProps}>
- <ServerSideRender
- block="jvb/gmbreviews"
- attributes={attributes}
- />
- </div>
- </>
- );
-}
diff --git a/src/gmbreviews/editor.scss b/src/gmbreviews/editor.scss
deleted file mode 100644
index e69de29..0000000
--- a/src/gmbreviews/editor.scss
+++ /dev/null
diff --git a/src/gmbreviews/index.js b/src/gmbreviews/index.js
deleted file mode 100644
index 2f2dd15..0000000
--- a/src/gmbreviews/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { registerBlockType } from '@wordpress/blocks';
-import Edit from './edit';
-import './style.scss';
-import './editor.scss';
-import metadata from './block.json';
-
-registerBlockType(metadata.name, {
- edit: Edit,
- // No save function - dynamic block rendered on server
- save: () => null,
-});
diff --git a/src/gmbreviews/index.php b/src/gmbreviews/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/gmbreviews/index.php
+++ /dev/null
diff --git a/src/gmbreviews/render.php b/src/gmbreviews/render.php
deleted file mode 100644
index ee519f8..0000000
--- a/src/gmbreviews/render.php
+++ /dev/null
@@ -1,207 +0,0 @@
-<?php
-/**
- * GMB Reviews Block - Render Template
- *
- * Displays recent Google My Business reviews with a link to leave a review
- */
-function jvbRenderGMBReviewsBlock(array $attributes): string
-{
- $count = $attributes['count'] ?? 5;
- $showRating = $attributes['showRating'] ?? true;
- $showDate = $attributes['showDate'] ?? true;
- $showReviewLink = $attributes['showReviewLink'] ?? true;
- $showViewAllLink = $attributes['showViewAllLink'] ?? true;
- $showStats = $attributes['showStats'] ?? true;
- $minStars = $attributes['minStars'] ?? 4; // Only show 4+ star reviews
- $inheritUser = $attributes['inheritUser']??null;
- if ($inheritUser) {
- global $post;
- $inheritUser = $post->post_author;
- }else {
- $inheritUser = null;
- }
- try {
- $gmb = JVB()->connect('gmb', $inheritUser);
- if (!$gmb->isSetUp()) {
- error_log('GMB Not set up for: '.(int)$inheritUser);
- return '';
- }
- $gotReviews = $gmb->getReviews();
- // Get all data
- $allReviews = $gotReviews['reviews']??[];
- $reviewUrl = $gmb->getReviewUrl();
- $viewAllUrl = $gmb->getReviewsViewUrl();
-
- $average = $gotReviews['averageRating']??null;
- $total = $gotReviews['totalReviewCount']??null;
-
- // Filter reviews by minimum stars
- $reviews = [];
- if (!empty($allReviews)) {
- foreach ($allReviews as $review) {
- $rating = $review['starRating'] ?? 0;
- if ($rating >= $minStars) {
- $reviews[] = $review;
- if (count($reviews) >= $count) {
- break; // Got enough reviews
- }
- }
- }
- }
-
- if (empty($reviews) && empty($reviewUrl) && empty($stats)) {
- error_log('No reviews to display...');
- return '';
- }
-
- ob_start();
- ?>
- <div class="gmb-reviews">
- <div class="row center">
- <?php
- if ($showStats && !empty($average) && !empty($total)) {
- ?>
- <p>
- <span class="stars" title="<?= $average ?> out of 5 stars">
- <?php
- $fullStars = floor($average);
- $hasHalfStar = ($average - $fullStars) >= 0.5;
-
- for ($i = 1; $i <= 5; $i++) {
- if ($i <= $fullStars) {
- echo jvbIcon('star', ['style' => 'fill']);
- } elseif ($i == $fullStars + 1 && $hasHalfStar) {
- echo jvbIcon('star-half', ['style'=> 'fill']);
- } else {
- echo jvbIcon('star', ['style' => 'light']);
- }
- }
- ?>
- </span>
- <i>Average</i>
- </p>
- <?php
- if ($total > 0) {
- ?>
- <p><i>{ <?= number_format($total ) . ' ' . _n('Review', 'Reviews', $total, 'jvb')?> Total }</i></p>
- <?php
- }
- ?>
- <?php
- }
- ?>
-
-
- </div>
- <?php
- if ($showReviewLink && !empty($reviewUrl)) {
- ?>
- <a href="<?=esc_url($reviewUrl)?>"
- class="button"
- target="_blank"
- rel="noopener noreferrer">
- <?= jvbIcon('star', ['style' => 'fill']) ?>
- Leave Your Review
- </a>
- <?php
- }
- ?>
-
- <ul>
- <?php
- foreach ($reviews as $review) {
- $reviewer = $review['reviewer']['displayName'] ?? 'Anonymous';
- $reviewer = strtok($reviewer, ' ');
- $profilePhoto = $review['reviewer']['profilePhotoUrl'] ?? '';
- $rating = $review['starRating'] ?? 0;
- $rating = match($rating) {
- 'FIVE' => 5,
- 'FOUR' => 4,
- 'THREE' => 3,
- 'TWO' => 2,
- 'ONE' => 1,
- default => $rating
- };
- $comment = $review['comment'] ?? '';
- $date = $review['updateTime'] ?? '';
- ?>
- <li>
- <blockquote class="review">
- <?php
- // Review text
- if (!empty($comment)) { ?>
- <div class="content review">
- <?= apply_filters('wpautop', $comment) ?>
- </div>
- <?php } ?>
- <cite class="row start nowrap">
- <?php if (!empty($profilePhoto)) { ?>
- <img src="<?=esc_url($profilePhoto)?>"
- alt="<?=esc_attr($reviewer)?>"
- 'loading="lazy">
- <?php } else { ?>
- <div class="avatar">
- <?= jvbIcon('user-circle')?>
- </div>
- <?php } ?>
-
- <div class="row start wrap">
- <?php if ($showRating && $rating > 0) { ?>
- <div class="stars" title="<?= $rating ?> out of 5 stars">
- <?php
- for ($i = 1; $i <= 5; $i++) {
- echo ($i <= $rating) ? jvbIcon('star', ['style' => 'fill']) : jvbIcon('star', ['style' => 'light']);
- } ?>
- </div>
- <?php } ?>
- <p><?= esc_html($reviewer)?></p>
- <?php
- // Date
- if ($showDate && !empty($date)) {
- $formatted_date = human_time_diff(strtotime($date), current_time('timestamp')) . ' ago';
- ?>
- <time datetime="<?=esc_attr($date)?>">
- <?= esc_html($formatted_date) ?>
- </time>
- <?php } ?>
-
- </div>
- </cite>
- </blockquote>
- </li>
- <?php
- }
- ?>
- </ul>
- <?php
- // Footer with "See All Reviews" button
- if ($showViewAllLink && !empty($viewAllUrl)) {
- ?>
- <div class="footer">
- <a href=" <?= esc_url($viewAllUrl) ?>"
- class="button"
- target="_blank"
- rel="noopener noreferrer">
-
- <?php
- if ($showStats ) {
- echo 'See All ' . number_format($total) . ' Reviews';
- } else {
- echo ' See All Reviews';
- }
- ?>
- <?= jvbIcon('arrow-square-out') ?>
- </a>
- </div>
- <?php
- }
- ?>
- </div>
- <?php
- return ob_get_clean();
-
- } catch (\Exception $e) {
- error_log('[GMB Reviews Block] Error: ' . $e->getMessage());
- return '';
- }
-}
diff --git a/src/gmbreviews/style.scss b/src/gmbreviews/style.scss
deleted file mode 100644
index 19f3abb..0000000
--- a/src/gmbreviews/style.scss
+++ /dev/null
@@ -1,122 +0,0 @@
-.gmb-reviews {
- max-width: none;
- > .row.center {
- max-width:var(--content);
- margin: 0 auto;
- --gap: .5rem 6rem;
-
- p {
- width: fit-content;
- }
- }
- .button {
- width: 66.6%;
- margin: 0 auto 2rem;
- display: flex;
- height: max-content;
- }
- .stars {
- display: inline-flex;
- align-items: center;
- justify-content: flex-start;
- flex-wrap: nowrap;
- }
- ul {
- list-style: none;
- margin: 0;
- padding: 0;
- max-width: var(--full);
- li {
- width: 100%;
- max-width: none;
- padding: 4rem 1rem;
- &:nth-of-type(odd) {
- background-color: var(--base-50);
- blockquote {
- --background: var(--base-50);
- }
- }
- &:nth-of-type(even) {
- background-color: var(--base-100);
- blockquote {
- --background: var(--base-100);
- }
- }
- }
- }
- blockquote {
- margin:0 auto;
- padding: 0;
- max-width: var(--content);
- .content {
- border-width: 4px 1px;
- &::after {
- border-width: 4px 1px;
- }
- &::before {
- border-width: 8px;
- bottom: -4px;
- }
- }
- cite {
- position: relative;
-
- img {
- width: 4.5rem;
- position: absolute;
- left: -8rem;
- top: 0;
- }
-
- p {
- margin: 0;
- }
-
- .wrap {
- --wrap: wrap;
-
- p, time {
- max-width: 49%;
- }
-
- .stars {
- width: 100%;
- }
- }
- }
- time {
- white-space: nowrap;
- }
- }
- .stars .icon {
- background-color: var(--action-0);
- }
- article {
- padding: 1rem;
- border-radius: var(--radius-outer);
- background-color: var(--base);
- header {
- --align: center;
- >img {
- position: relative;
- left: 0;
- }
- }
- time {
- font-style: italic;
- }
- .review {
- padding: 1.5rem;
- }
-
- h4 {
- width: max-content;
- }
- .icon {
- color: var(--action-0);
- }
- }
- .footer .button {
- width: 100%;
- }
-}
diff --git a/src/gmbreviews/view.js b/src/gmbreviews/view.js
deleted file mode 100644
index e69de29..0000000
--- a/src/gmbreviews/view.js
+++ /dev/null
diff --git a/src/index.php b/src/index.php
deleted file mode 100644
index 4f2541d..0000000
--- a/src/index.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-//Nothing to see here
-
diff --git a/src/menu/block.json b/src/menu/block.json
deleted file mode 100644
index 2f198c3..0000000
--- a/src/menu/block.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/menu",
- "version": "0.1.0",
- "title": "Our Menu",
- "category": "jvb",
- "icon": "food",
- "description": "Outputs our menu, organized by categories",
- "example": {},
- "supports": {
- "html": false,
- "align": ["wide", "full"]
- },
- "textdomain": "jvb",
- "selectors": {
- "root": ".menu-block"
- },
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css",
- "render": "file:./render.php",
- "viewScript": "file:./view.js"
-}
diff --git a/src/menu/edit.js b/src/menu/edit.js
deleted file mode 100644
index cbb91c1..0000000
--- a/src/menu/edit.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Retrieves the translation of text.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
- */
-import { __ } from '@wordpress/i18n';
-
-/**
- * React hook that is used to mark the block wrapper element.
- * It provides all the necessary props like the class name.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
- */
-import { useBlockProps } from '@wordpress/block-editor';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * Those files can contain any CSS code that gets applied to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './editor.scss';
-
-/**
- * The edit function describes the structure of your block in the context of the
- * editor. This represents what the editor will render when the block is used.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
- *
- * @return {Element} Element to render.
- */
-export default function Edit() {
- return (
- <p { ...useBlockProps() }>
- { __( 'Will output the menu', 'jvb' ) }
- </p>
- );
-}
diff --git a/src/menu/editor.scss b/src/menu/editor.scss
deleted file mode 100644
index e69de29..0000000
--- a/src/menu/editor.scss
+++ /dev/null
diff --git a/src/menu/index.js b/src/menu/index.js
deleted file mode 100644
index d82621b..0000000
--- a/src/menu/index.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Registers a new block provided a unique name and an object defining its behavior.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-import { registerBlockType } from '@wordpress/blocks';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * All files containing `style` keyword are bundled together. The code used
- * gets applied both to the front of your site and to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './style.scss';
-
-/**
- * Internal dependencies
- */
-import Edit from './edit';
-import metadata from './block.json';
-
-/**
- * Every block starts by registering a new block type definition.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-registerBlockType( metadata.name, {
- /**
- * @see ./edit.js
- */
- edit: Edit,
-} );
diff --git a/src/menu/index.php b/src/menu/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/menu/index.php
+++ /dev/null
diff --git a/src/menu/render.php b/src/menu/render.php
deleted file mode 100644
index 52aded4..0000000
--- a/src/menu/render.php
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php
-/**
- * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
- */
-?>
-<p <?php echo get_block_wrapper_attributes(); ?>>
- <?php esc_html_e( 'Menu – hello from a dynamic block!', 'menu' ); ?>
-</p>
diff --git a/src/menu/style.scss b/src/menu/style.scss
deleted file mode 100644
index e69de29..0000000
--- a/src/menu/style.scss
+++ /dev/null
diff --git a/src/menu/view.js b/src/menu/view.js
deleted file mode 100644
index 153b0df..0000000
--- a/src/menu/view.js
+++ /dev/null
@@ -1,43 +0,0 @@
-window.details = document.querySelectorAll('details');
-window.toggles = document.querySelectorAll('.toggle-details');
-
-document.addEventListener('click', (e) => {
- if (e.target.classList.contains('toggle-details')) {
- e.target.classList.toggle('open');
- let on = e.target.classList.contains('open');
- let section = e.target.dataset.toggle;
- if (section === 'all') {
- toggleToggles(on);
- }
-
- let span = e.target.querySelector('span');
- span.textContent = (on) ? 'Close': 'Open';
- toggleDetails(section, on);
- }
-});
-
-
-console.log(window.details);
-function toggleDetails(name, toggle) {
- if (name === 'all') {
- console.log('Toggling all!');
- window.details.forEach(detail => {
- console.log(detail);
- detail.open = toggle;
- });
- } else {
- for (let detail of window.details) {
- if (detail.dataset.section === name) {
- detail.open = toggle;
- }
- }
- }
-}
-
-function toggleToggles(on) {
- window.toggles.forEach(toggle => {
- if (toggle.dataset.toggle !== 'all') {
- toggle.querySelector('span').textContent = (on) ? 'Close' : 'Open';
- }
- });
-}
diff --git a/src/summary/block.json b/src/summary/block.json
deleted file mode 100644
index 2cca715..0000000
--- a/src/summary/block.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/summary",
- "title": "Archive Summary",
- "category": "jvb",
- "icon": "align-center",
- "description": "Outputs the information for the given archive page, or the bio for a profile. Pairs well with the feed block.",
- "keywords": [ "summary", "bio", "style", "term" ],
- "version": "0.9.0",
- "textdomain": "jvb",
- "supports": {
- "html": false,
- "align": ["wide", "full"]
- },
- "selectors": {
- "root": ".summary-block"
- },
- "styles": [
- { "name": "default", "label": "Default", "isDefault": true }
- ],
- "example": {
- "attributes": {
- "listType": "tattoo"
- }
- },
- "render": "file:./render.php",
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css",
- "viewScript": "file:./view.js"
-}
diff --git a/src/summary/edit.js b/src/summary/edit.js
deleted file mode 100644
index 6741773..0000000
--- a/src/summary/edit.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __ } from '@wordpress/i18n';
-import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
-import { SelectControl, ToggleControl, PanelBody } from '@wordpress/components';
-
-/**
- * Styles
- */
-import './editor.scss';
-
-/**
- * Edit function for Summary Block
- */
-export default function Edit({ attributes, setAttributes }) {
- const blockProps = useBlockProps();
-
- return (
- <div {...blockProps}>
- <div className="jvb-summary-preview">
- <h3>{__('Summary', 'jvb')}</h3>
- <p className="jvb-list-preview-note">
- {__('This will inherit the current query to build the information from our custom meta on the front end.', 'jvb')}
- </p>
- </div>
- </div>
- );
-}
diff --git a/src/summary/editor.scss b/src/summary/editor.scss
deleted file mode 100644
index bdf5776..0000000
--- a/src/summary/editor.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Directory List Block Editor Styles
- */
-.jvb-summary-preview {
- padding: 20px;
- background-color: #f8f9fa;
- border: 1px solid #e2e4e7;
- border-radius: 4px;
-
- h3 {
- margin-top: 0;
- padding-bottom: 10px;
- border-bottom: 1px solid #ff0080;
- }
- &-note {
- font-style: italic;
- color: #555d66;
- margin-bottom: 0;
- }
-}
diff --git a/src/summary/index.js b/src/summary/index.js
deleted file mode 100644
index c477d23..0000000
--- a/src/summary/index.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Registers a new block provided a unique name and an object defining its behavior.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-import { registerBlockType } from '@wordpress/blocks';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * All files containing `style` keyword are bundled together. The code used
- * gets applied both to the front of your site and to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './style.scss';
-
-/**
- * Internal dependencies
- */
-import Edit from './edit';
-import save from './save';
-import metadata from './block.json';
-
-/**
- * Every block starts by registering a new block type definition.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-registerBlockType( metadata.name, {
- /**
- * @see ./edit.js
- */
- edit: Edit,
-
- /**
- * @see ./save.js
- */
- save,
-} );
diff --git a/src/summary/index.php b/src/summary/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/summary/index.php
+++ /dev/null
diff --git a/src/summary/render.php b/src/summary/render.php
deleted file mode 100644
index 05636a1..0000000
--- a/src/summary/render.php
+++ /dev/null
@@ -1,320 +0,0 @@
-<?php
-
-use JVBase\managers\Cache;
-use JVBase\meta\Meta;
-use JVBase\meta\Render;
-use JVBase\registrar\Registrar;
-
-if (!defined('ABSPATH')) {
- exit; // Exit if accessed directly
-}
-/**
- * Summary Block Render
- *
- * @package Edmonton_Ink
- */
-
-function jvbRenderSummaryBlock(array $attributes):string
-{
-
- // Buffer output
- if (is_tax()) {
- switch (get_queried_object()->taxonomy) {
- case BASE.'shop':
- return jvbRenderShopSummary();
- default:
- return jvbRenderTermSummary();
- }
- } elseif (is_singular()) {
- return jvbRenderArtistSummary();
- }
- return '';
-}
-
-function jvbRenderArtistSummary():string
-{
- $current = get_queried_object();
- $cache = Cache::for('artistSummary', WEEK_IN_SECONDS);
- $key = $current->ID;
- $cached = $cache->get($key);
- if ($cached !== false) {
- return $cached;
- }
-
- ob_start();
- $meta = Meta::forPost($current->ID);
- $artist = jvbContentFromUser((int)$current->post_author);
-
- $registrar = Registrar::getInstance($current->post_type));
- $sections = [];
- if ($registrar) {
- $sections = $registrar->getSections();
- }
-
-
-
-
-// $handler = JVB()->getContent(str_replace(BASE,'', $current->post_type));
- ?>
- <nav id="artist" class="on-this-page index">
- <label>Jump to:
- <button type="button" aria-label="Show Index" title="Show Index" class="toggle" aria-expanded="false">
- <?= jvbIcon('plus-square')?>
- </button>
- </label>
- <ul>
- <li><a href="#top" title="Back to Top"><?=jvbIcon('caret-circle-up')?></a></li>
- <li><a href="#about">About</a></li>
- <li><a href="#styles">Styles</a></li>
- <li><a href="#contact">Contact</a></li>
- <li><a href="#work">Work</a></li>
- </ul>
- </nav>
- <header id="top">
- <h1><small><?=(!empty($artist['city'])) ? $artist['city']['name'] :'Edmonton'?>'s Best <?= (!empty($artist['type'])) ?
- $artist['type']['name']:'Tattoo Artists'?>:
- </small><?=$artist['display_name']?></h1>
- <div>
- <?php if (!empty($artist['shop'])) : ?>
- <ul class="term-list shop">
- <li>
- <a href="<?=$artist['shop']['url']?>" title="Learn more about <?=$artist['shop']['name']?>">
- <?= strtolower($artist['shop']['name'])?>
- </a>
- </li>
- </ul>
- <?php endif; ?>
- <?php if (!empty($artist['city'])): ?>
- <ul class="term-list city">
- <li>
- <a href="<?=$artist['city']['url']?>" title="See who else is rocking out of <?=$artist['city']['name']?>">
- <?= strtolower($artist['city']['name'])?>
- </a>
- </li>
- </ul>
- <?php endif; ?>
- <?php $styles = $meta->get('top_styles');
- if (!empty($styles)) {
- ?>
- <ul class="term-list style">
- <?php
- foreach ($styles as $style) {
- $term = get_term((int)$style, BASE.'style');
- if ($term && !is_wp_error($term)) {
- $link = get_term_link((int)$style, BASE.'style');
- ?>
- <li>
- <a href="<?=$link?>" title="Learn more about <?=html_entity_decode($term->name)?>">
- <?=strtolower(html_entity_decode($term->name))?>
- </a>
- </li>
- <?php
- }
- }
- ?>
- </ul>
- <?php
- }
- ?>
- </div>
- </header>
- <section>
- <details class="bio-info">
- <summary class="row btw">
- <h2>About <?= ($artist['name'] !== '') ? $artist['name'] : strtok($artist['display_name'], ' ')?></h2>
- </summary>
- <div class="columns stack-small">
- <div class="column">
- <?= Render::renderFrom($meta, 'image_portrait'); ?>
- </div>
- <div class="column">
- <?= Render::renderFrom($meta, 'short_bio'); ?>
- </div>
- </div>
- <div id="styles">
- <h3>Works In</h3>
- <?= jvbGetTheTerms('style', $current->ID) ?>
- </div>
- <div class="contact">
- <h3>Contact:</h3>
- <?php
- echo jvbRenderContactInfo($current->ID, $meta);
- echo jvbRenderLinks($current->ID, $meta);
- ?>
- </div>
-
- <div id="about">
- <?= Render::renderFrom($meta, 'bio')?>
- </div>
- </details>
- </section>
- <section id="contact" class="">
- <h2>Contact <?=$artist['name']?></h2>
- <?php
- echo jvbRenderContactInfo($current->ID, 'post');
- echo jvbRenderLinks($current->ID, 'post');
- ?>
- </section>
- <?php
- $finished = ob_get_clean();
- $cache->set($key, $finished);
- return $finished;
-}
-
-function jvbRenderShopSummary()
-{
- $current = get_queried_object();
-
- $cache = Cache::for('shop_bio', WEEK_IN_SECONDS)->connect('taxonomy');
- $key = $current->term_id;
- $cached = $cache->get($key);
- if ($cached !== false) {
- return $cached;
- }
-
- ob_start();
-
- $meta = Meta::forTerm($current->term_id);
- $fields = $meta->getAll(['average_rating', 'established', 'bio','location','hours','specialties','awards','reviews']);
- ?>
- <nav id="shop" class="on-this-page index">
- <label>Jump to:
- <button type="button" aria-label="Show Index" title="Show Index" class="toggle" aria-expanded="false">
- <?= jvbIcon('plus-square')?>
- </button>
- </label>
- <ul>
- <li><a href="#top" title="Back to Top"><?=jvbIcon('caret-circle-up')?></a></li> <?php
- if ($fields['rating'] !== 'none') {
- ?>
- <li><a href="#rating">Rating</a></li>
- <?php
- } elseif ($fields['opened'] !== '') {
- ?>
- <li><a href="#opened">Opened</a></li>
- <?php
- } elseif ($fields['location'] !== '') {
- ?>
- <li><a href="#location">Location</a></li>
- <?php
- } elseif ($fields['about'] !== '') {
- ?>
- <li><a href="#about">About</a></li>
- <?php
- } elseif ($fields['hours'] !== '') {
- ?>
- <li><a href="#hours">Hours</a></li>
- <?php
- } elseif ($fields['specialties'] !== '') {
- ?>
- <li><a href="#specialties">Specialties</a></li>
- <?php
- } elseif ($fields['awards'] !== '') {
- ?>
- <li><a href="#awards">Awards</a></li>
- <?php
- } elseif ($fields['reviews'] !== '') {
- ?>
- <li><a href="#reviews">Reviews</a></li>
- <?php
- }
- ?>
- <li><a href="#contact">Contact</a></li>
- <li><a href="#artists">Artists</a></li>
- </ul>
- </nav>
- <header id="top">
- <div class="columns stack-small">
- <div class="column">
- <?=jvbFormatImage($meta->get('image'))?>
- </div>
- <div class="column">
- <h1>
- <small><?= (get_term((int)$meta->get('city'), BASE.'city')) ?
- get_term((int)$meta->get('city'), BASE.'city')->name :
- 'Edmonton'?>'s Best Tattoo Shops</small>
- <?=$current->name?>
- </h1>
- <?= jvbFormatRating($current->term_id, 'term') ?>
- <?= Render::renderFrom($meta, 'slogan'); ?>
- </div>
- </div>
- </header>
- <section>
- <details class="bio-info">
- <summary class="row btw">
- <h2>Learn More About <?=$current->name?></h2>
- </summary>
- <div class="map">
- <?= Render::renderFrom($meta, 'location'); ?>
- </div>
- <div class="short-bio">
- <?= Render::renderFrom($meta, 'short_bio'); ?>
- </div>
-
- <div class="contact">
- <h3>Contact:</h3>
- <?php
- echo jvbRenderContactInfo($current->term_id, 'term');
- echo jvbRenderLinks($current->term_id, 'term');
- ?>
- </div>
-
- <div id="about">
- <?= Render::renderFrom($meta, 'bio')?>
- </div>
- </details>
- </section>
- <section id="contact" class="">
- <h2>Contact </h2>
- <?php
- echo jvbRenderContactInfo($current->term_id, 'term');
- echo jvbRenderLinks($current->term_id, 'term');
- ?>
- </section>
- <?= jvbRenderHours($current->term_id, 'term')?>
-
-
- <?php
- $finished = ob_get_clean();
- $cache->set($key, $finished);
- return $finished;
-}
-
-
-function jvbRenderTermSummary()
-{
- $current = get_queried_object();
- $cache = Cache::for('term_summary', WEEK_IN_SECONDS)->connect('taxonomy');
- $key = $current->ID;
- $cached = $cache->get($key);
- if ($cached !== false) {
- return $cached;
- }
-
- ob_start();
- $tax = jvbNoBase($current->taxonomy);
- switch ($tax) {
- case 'style':
- $title = 'Tattoo Artists';
- break;
- case 'theme':
- $title = 'Tattoos';
- break;
- default:
- $title = '';
- }
-
- $meta = Meta::forTerm($current->ID);
- $fields = $meta->getAll();
-
- ?>
- <header id="top">
- <h1><?= get_the_archive_title() ?></h1>
- </header>
-
- <?php
- $finished = ob_get_clean();
- $cache->set($key, $finished);
- return $finished;
-}
diff --git a/src/summary/save.js b/src/summary/save.js
deleted file mode 100644
index 8169594..0000000
--- a/src/summary/save.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function save() {
- return null; // Dynamic block rendered by PHP
-}
diff --git a/src/summary/style.scss b/src/summary/style.scss
deleted file mode 100644
index b182fe9..0000000
--- a/src/summary/style.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-details > div {
- margin: 1rem 0;
-}
-
-main > header:not(:has(img)) {
- margin-top: 3rem!important;
-}
-
-header a::before {
- display: none!important;
-}
-
-header + details {
- margin: 1.5rem auto 3rem!important;
- max-width: var(--wide);
-}
-
-main {
- padding-top: 0!important;
-}
diff --git a/src/summary/view.js b/src/summary/view.js
deleted file mode 100644
index 8b13789..0000000
--- a/src/summary/view.js
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/timeline/block.json b/src/timeline/block.json
deleted file mode 100644
index 9e219a1..0000000
--- a/src/timeline/block.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/timeline",
- "version": "0.1.0",
- "title": "Timeline",
- "category": "jvb",
- "icon": "shortcode",
- "description": "Outputs a single timeline post in a cool way",
- "example": {},
- "supports": {
- "html": false,
- "align": ["wide", "full"]
- },
- "textdomain": "jvb",
- "selectors": {
- "root": ".timeline"
- },
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css",
- "viewScript": "file:./view.js"
-}
diff --git a/src/timeline/edit.js b/src/timeline/edit.js
deleted file mode 100644
index 14f0ce6..0000000
--- a/src/timeline/edit.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Retrieves the translation of text.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
- */
-import { __ } from '@wordpress/i18n';
-
-/**
- * React hook that is used to mark the block wrapper element.
- * It provides all the necessary props like the class name.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
- */
-import { useBlockProps } from '@wordpress/block-editor';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * Those files can contain any CSS code that gets applied to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './editor.scss';
-
-/**
- * The edit function describes the structure of your block in the context of the
- * editor. This represents what the editor will render when the block is used.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
- *
- * @return {Element} Element to render.
- */
-export default function Edit() {
- return (
- <p { ...useBlockProps() }>
- { __( 'Will output the timeline', 'jvb' ) }
- </p>
- );
-}
diff --git a/src/timeline/editor.scss b/src/timeline/editor.scss
deleted file mode 100644
index e69de29..0000000
--- a/src/timeline/editor.scss
+++ /dev/null
diff --git a/src/timeline/index.js b/src/timeline/index.js
deleted file mode 100644
index d82621b..0000000
--- a/src/timeline/index.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Registers a new block provided a unique name and an object defining its behavior.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-import { registerBlockType } from '@wordpress/blocks';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * All files containing `style` keyword are bundled together. The code used
- * gets applied both to the front of your site and to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './style.scss';
-
-/**
- * Internal dependencies
- */
-import Edit from './edit';
-import metadata from './block.json';
-
-/**
- * Every block starts by registering a new block type definition.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-registerBlockType( metadata.name, {
- /**
- * @see ./edit.js
- */
- edit: Edit,
-} );
diff --git a/src/timeline/index.php b/src/timeline/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/timeline/index.php
+++ /dev/null
diff --git a/src/timeline/render.php b/src/timeline/render.php
deleted file mode 100644
index 52aded4..0000000
--- a/src/timeline/render.php
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php
-/**
- * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
- */
-?>
-<p <?php echo get_block_wrapper_attributes(); ?>>
- <?php esc_html_e( 'Menu – hello from a dynamic block!', 'menu' ); ?>
-</p>
diff --git a/src/timeline/style.scss b/src/timeline/style.scss
deleted file mode 100644
index ea20261..0000000
--- a/src/timeline/style.scss
+++ /dev/null
@@ -1,135 +0,0 @@
-main {
- --gap: 0;
- section:last-of-type {
- margin-bottom: 0;
- }
-}
-
-#at-a-glance {
- padding: 0 10vw;
- --gap: 0;
- img {
- width: 100%;
- height: auto;
- border: 2px solid var(--action-0);
- }
- h3 {
- font-size: var(--txt-x-small);
- }
- .before {
- img {
- border-right-width: 1px;
- border-left: 0;
- border-top: 0;
- }
- }
- .after {
- img {
- border-left-width: 1px;
- border-right: 0;
- border-bottom: 0;
- }
- }
-}
-
-.timeline-point.timeline-point {
- --lineWidth: 1px;
- --gap: 2rem;
- padding: 0;
- margin:0;
- background-color: var(--base);
- max-width: 100vw;
- position: relative;
- overflow: hidden;
- img {
- width: 40%;
- border-radius: 4px;
- position: sticky;
- padding: .5rem;
- }
- .info {
- padding: 1rem .5rem .5rem;
- width: 60%;
- position: relative;
- h2 {
- margin: 0 0 .5rem;
- font-size: var(--txt-medium);
- position: relative;
- .icon {
- --w: 2.5rem;
- transform: rotate(-90deg);
- position: absolute;
- left: -2.5rem;
- top: .25rem;
- background-color: var(--action-100);
- }
- }
-
- }
- &::before,
- &::after {
- content: '';
- display: block;
- position: absolute;
- left: 45%;
- height: 100%;
- width: var(--lineWidth);
- background-color: var(--action-0);
- //box-shadow: var(--action-shadow);
- }
- &::before {
- height: 1rem;
- }
- &::after {
- top: 4rem;
- }
- &#before-treatment::before,
- &:last-of-type::after {
- display: none;
- }
-}
-@media (min-width:768px) {
- #at-a-glance {
- h3 {
- font-size: var(--txt-x-large);
- }
- }
- .timeline-point.timeline-point {
- --gap: 4rem;
- img {
- width: 50%;
- }
- .info {
- width: 50%;
- padding: 25vh 1rem 1rem;
- h2 {
- .icon {
- --w: 4rem;
- left: -6.15rem;
- top: 0;
- }
- }
- a {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- }
-
- time {
- text-transform: uppercase;
- font-size: var(--txt-x-small);
- }
- }
- &::before,
- &::after {
- left: calc(50% + 2rem);
- }
-
- &::before {
- height: calc(25vh - 2rem);
- }
- &::after {
- top: calc(25vh + 6rem);
- }
- }
-}
diff --git a/src/timeline/view.js b/src/timeline/view.js
deleted file mode 100644
index e69de29..0000000
--- a/src/timeline/view.js
+++ /dev/null
diff --git a/src/video/block.json b/src/video/block.json
deleted file mode 100644
index 29149bf..0000000
--- a/src/video/block.json
+++ /dev/null
@@ -1,79 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "jvb/video",
- "version": "1.0.0",
- "title": "Video Cover",
- "category": "jvb",
- "icon": "video-alt3",
- "description": "Self-hosted video cover with poster and multiple format support",
- "supports": {
- "html": false,
- "align": ["wide", "full"],
- "spacing": {
- "margin": true,
- "padding": true
- },
- "color": {
- "background": true,
- "text": true
- }
- },
- "attributes": {
- "title": {
- "type": "string",
- "default": ""
- },
- "description": {
- "type": "rich-text",
- "default": ""
- },
- "posterId": {
- "type": "number",
- "default": 0
- },
- "posterUrl": {
- "type": "string",
- "default": ""
- },
- "videoSources": {
- "type": "array",
- "default": [],
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "number"
- },
- "url": {
- "type": "string"
- },
- "mime": {
- "type": "string"
- }
- }
- }
- },
- "fadeEffect": {
- "type": "boolean",
- "default": false
- },
- "overlayOpacity": {
- "type": "number",
- "default": 0
- },
- "contentAlignment": {
- "type": "string",
- "default": "center"
- },
- "minHeight": {
- "type": "number",
- "default": 0
- }
- },
- "textdomain": "jvb",
- "editorScript": "file:./index.js",
- "viewScript": "file:./view.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css"
-}
diff --git a/src/video/edit.js b/src/video/edit.js
deleted file mode 100644
index 9830e65..0000000
--- a/src/video/edit.js
+++ /dev/null
@@ -1,276 +0,0 @@
-//edit.js
-import { __ } from '@wordpress/i18n';
-import {
- useBlockProps,
- InspectorControls,
- MediaUpload,
- MediaUploadCheck,
- InnerBlocks,
- useInnerBlocksProps
-} from '@wordpress/block-editor';
-import {
- PanelBody,
- Button,
- ToggleControl,
- BaseControl,
- RangeControl,
- SelectControl
-} from '@wordpress/components';
-import './editor.scss';
-
-const ALLOWED_VIDEO_TYPES = ['video'];
-
-const INNER_BLOCKS_TEMPLATE = [
- ['core/heading', {
- level: 1,
- placeholder: 'Add heading...',
- textAlign: 'center'
- }],
- ['core/paragraph', {
- placeholder: 'Add description...',
- align: 'center'
- }],
- ['core/buttons', {
- layout: { type: 'flex', justifyContent: 'center' }
- }]
-];
-
-
-
-export default function Edit({ attributes, setAttributes }) {
- const {
- posterId,
- posterUrl,
- videoSources,
- fadeEffect,
- overlayOpacity,
- contentAlignment,
- minHeight
- } = attributes;
-
- const blockProps = useBlockProps({
- className: 'video-cover-editor',
- style: {
- minHeight: minHeight ? `${minHeight}px` : undefined
- }
- });
-
- const innerBlocksProps = useInnerBlocksProps(
- { className: 'video-cover-content' },
- {
- template: INNER_BLOCKS_TEMPLATE,
- templateLock: false
- }
- );
-
- const onSelectPoster = (media) => {
- setAttributes({
- posterId: media.id,
- posterUrl: media.url
- });
- };
-
- const onSelectVideos = (mediaItems) => {
- // multiple=true returns an array
- const items = Array.isArray(mediaItems) ? mediaItems : [mediaItems];
- const newSources = items
- .filter(media => !videoSources.some(s => s.id === media.id))
- .map(media => ({
- id: media.id,
- url: media.url,
- mime: media.mime
- }));
-
- if (newSources.length) {
- setAttributes({
- videoSources: [...videoSources, ...newSources]
- });
- }
- };
-
- const removeVideoSource = (index) => {
- const updated = [...videoSources];
- updated.splice(index, 1);
- setAttributes({ videoSources: updated });
- };
-
- const renderVideoSourceList = (sources, isMobile = false) => {
- if (sources.length === 0) return null;
-
- return (
- <ul className="video-source-list">
- {sources.map((source, index) => (
- <li key={index} className="video-source-item">
- <span className="video-source-mime">{source.mime}</span>
- <Button
- isDestructive
- isSmall
- onClick={() => removeVideoSource(index, isMobile)}
- >
- {__('Remove', 'jvb')}
- </Button>
- </li>
- ))}
- </ul>
- );
- };
-
- return (
- <>
- <InspectorControls>
- <PanelBody title={__('Video Settings', 'jvb')} initialOpen={true}>
- <BaseControl
- label={__('Poster Image', 'jvb')}
- help={__('Image shown while video loads', 'jvb')}
- >
- <MediaUploadCheck>
- <MediaUpload
- onSelect={onSelectPoster}
- allowedTypes={['image']}
- value={posterId}
- render={({ open }) => (
- <>
- {posterUrl && (
- <img
- src={posterUrl}
- alt={__('Poster preview', 'jvb')}
- style={{ maxWidth: '100%', marginBottom: '10px' }}
- />
- )}
- <Button
- onClick={open}
- variant={posterUrl ? 'secondary' : 'primary'}
- >
- {posterUrl
- ? __('Change Poster', 'jvb')
- : __('Select Poster', 'jvb')}
- </Button>
- {posterUrl && (
- <Button
- isDestructive
- onClick={() => setAttributes({ posterId: 0, posterUrl: '' })}
- style={{ marginLeft: '10px' }}
- >
- {__('Remove', 'jvb')}
- </Button>
- )}
- </>
- )}
- />
- </MediaUploadCheck>
- </BaseControl>
-
- <BaseControl
- label={__('Video Sources', 'jvb')}
- help={__('Add multiple formats for better browser support (mp4, webm, etc.)', 'jvb')}
- >
- {videoSources.length > 0 && (
- <ul className="video-source-list">
- {videoSources.map((source, index) => (
- <li key={index} className="video-source-item">
- <span className="video-source-mime">{source.mime}</span>
- <Button
- isDestructive
- isSmall
- onClick={() => removeVideoSource(index)}
- >
- {__('Remove', 'jvb')}
- </Button>
- </li>
- ))}
- </ul>
- )}
- <MediaUploadCheck>
- <MediaUpload
- multiple={true}
- onSelect={onSelectVideos}
- allowedTypes={ALLOWED_VIDEO_TYPES}
- render={({ open }) => (
- <Button onClick={open} variant="secondary">
- {__('Add Video', 'jvb')}
- </Button>
- )}
- />
- </MediaUploadCheck>
- </BaseControl>
-
- <ToggleControl
- label={__('Fade Effect', 'jvb')}
- help={__('Add fade class to video element', 'jvb')}
- checked={fadeEffect}
- onChange={(value) => setAttributes({ fadeEffect: value })}
- />
- </PanelBody>
-
- <PanelBody title={__('Overlay Settings', 'jvb')} initialOpen={true}>
- <RangeControl
- label={__('Overlay Opacity', 'jvb')}
- help={__('Darken video for better text readability', 'jvb')}
- value={overlayOpacity}
- onChange={(value) => setAttributes({ overlayOpacity: value })}
- min={0}
- max={100}
- step={5}
- />
-
- <SelectControl
- label={__('Content Alignment', 'jvb')}
- value={contentAlignment}
- options={[
- { label: __('Top Left', 'jvb'), value: 'top-left' },
- { label: __('Top Center', 'jvb'), value: 'top-center' },
- { label: __('Top Right', 'jvb'), value: 'top-right' },
- { label: __('Center Left', 'jvb'), value: 'center-left' },
- { label: __('Center', 'jvb'), value: 'center' },
- { label: __('Center Right', 'jvb'), value: 'center-right' },
- { label: __('Bottom Left', 'jvb'), value: 'bottom-left' },
- { label: __('Bottom Center', 'jvb'), value: 'bottom-center' },
- { label: __('Bottom Right', 'jvb'), value: 'bottom-right' }
- ]}
- onChange={(value) => setAttributes({ contentAlignment: value })}
- />
-
- <RangeControl
- label={__('Minimum Height', 'jvb')}
- help={__('Minimum height in pixels (leave 0 for auto)', 'jvb')}
- value={minHeight}
- onChange={(value) => setAttributes({ minHeight: value })}
- min={0}
- max={1000}
- step={50}
- />
- </PanelBody>
- </InspectorControls>
-
- <div {...blockProps}>
- {posterUrl || videoSources.length > 0 ? (
- <div className="video-cover-preview">
- {posterUrl && (
- <>
- <img src={posterUrl} alt={__('Video poster', 'jvb')} />
- {overlayOpacity > 0 && (
- <div
- className="video-overlay-preview"
- style={{ opacity: overlayOpacity / 100 }}
- />
- )}
- </>
- )}
- <div className={`video-cover-content-preview align-${contentAlignment}`}>
- <div {...innerBlocksProps} />
- </div>
- <div className="video-info">
- <p>
- {videoSources.length} {__('desktop source(s)', 'jvb')}
- </p>
- </div>
- </div>
- ) : (
- <div className="video-cover-placeholder">
- <p>{__('Configure video sources in the sidebar →', 'jvb')}</p>
- </div>
- )}
- </div>
- </>
- );
-}
diff --git a/src/video/editor.scss b/src/video/editor.scss
deleted file mode 100644
index afc9f13..0000000
--- a/src/video/editor.scss
+++ /dev/null
@@ -1,141 +0,0 @@
-/* editor.scss */
-.video-cover-editor {
- position: relative;
- min-height: 200px;
- background: #f0f0f0;
- border: 2px dashed #ccc;
- border-radius: 4px;
-
- .video-cover-preview {
- position: relative;
- width: 100%;
- min-height: 300px;
-
- img {
- width: 100%;
- height: auto;
- display: block;
- }
-
- .video-overlay-preview {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 1);
- pointer-events: none;
- }
-
- .video-cover-content-preview {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- display: flex;
- z-index: 2;
- padding: 2rem;
-
- // Content alignment classes
- &.align-top-left {
- align-items: flex-start;
- justify-content: flex-start;
- }
- &.align-top-center {
- align-items: flex-start;
- justify-content: center;
- }
- &.align-top-right {
- align-items: flex-start;
- justify-content: flex-end;
- }
- &.align-center-left {
- align-items: center;
- justify-content: flex-start;
- }
- &.align-center {
- align-items: center;
- justify-content: center;
- }
- &.align-center-right {
- align-items: center;
- justify-content: flex-end;
- }
- &.align-bottom-left {
- align-items: flex-end;
- justify-content: flex-start;
- }
- &.align-bottom-center {
- align-items: flex-end;
- justify-content: center;
- }
- &.align-bottom-right {
- align-items: flex-end;
- justify-content: flex-end;
- }
-
- .video-cover-content {
- width: 100%;
- max-width: 1200px;
- color: white;
- text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
-
- // Style inner blocks for better visibility in editor
- h1, h2, h3, h4, h5, h6 {
- color: white;
- }
-
- p {
- color: white;
- }
- }
- }
-
- .video-info {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- background: rgba(0, 0, 0, 0.7);
- color: white;
- padding: 10px;
- font-size: 14px;
- z-index: 3;
-
- p {
- margin: 0;
- }
- }
- }
-
- .video-cover-placeholder {
- display: flex;
- align-items: center;
- justify-content: center;
- min-height: 200px;
- color: #666;
- font-size: 16px;
- }
-}
-
-.video-source-list {
- list-style: none;
- margin: 10px 0;
- padding: 0;
-
- .video-source-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 12px;
- background: #f5f5f5;
- border-radius: 4px;
- margin-bottom: 5px;
-
- .video-source-mime {
- font-family: monospace;
- font-size: 13px;
- }
- }
-}
diff --git a/src/video/index.js b/src/video/index.js
deleted file mode 100644
index 2f9cf8b..0000000
--- a/src/video/index.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* index.js */
-import { registerBlockType } from '@wordpress/blocks';
-import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
-import './style.scss';
-import Edit from './edit';
-import metadata from './block.json';
-
-registerBlockType(metadata.name, {
- edit: Edit,
- save: ({ attributes }) => {
- const blockProps = useBlockProps.save({
- className: 'video-cover-wrapper-placeholder'
- });
-
- return (
- <div {...blockProps}>
- <InnerBlocks.Content />
- </div>
- );
- }
-});
diff --git a/src/video/index.php b/src/video/index.php
deleted file mode 100644
index e69de29..0000000
--- a/src/video/index.php
+++ /dev/null
diff --git a/src/video/render.php b/src/video/render.php
deleted file mode 100644
index b3d9bbc..0000000
--- a/src/video/render.php
+++ /dev/null
@@ -1 +0,0 @@
-<?php
diff --git a/src/video/save.js b/src/video/save.js
deleted file mode 100644
index da66ed1..0000000
--- a/src/video/save.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * save.js
- * React hook that is used to mark the block wrapper element.
- * It provides all the necessary props like the class name.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
- */
-import { useBlockProps } from '@wordpress/block-editor';
-
-/**
- * The save function defines the way in which the different attributes should
- * be combined into the final markup, which is then serialized by the block
- * editor into `post_content`.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save
- *
- * @return {WPElement} Element to render.
- */
-export default function save() {
- // This is a dynamic block that is rendered on the server side
- // Return null to let WordPress handle the saving and rendering
- return null;
-}
diff --git a/src/video/style.scss b/src/video/style.scss
deleted file mode 100644
index d16e7d9..0000000
--- a/src/video/style.scss
+++ /dev/null
@@ -1,178 +0,0 @@
-/** style.scss **/
-.video-cover {
- position: relative;
- width: 100%;
- min-height: 75vh;
- overflow: hidden;
- display: flex;
- .wrap {
- background-color: var(--contrast-200);
- //&::before {
- // position: absolute;
- // top: 0;
- // bottom: 0;
- // left: 0;
- // right: 0;
- // background-color: var(--base);
- // mix-blend-mode: lighten;
- // content: '';
- // z-index: 1;
- //}
- }
- /* Video background */
- .video-container {
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- min-width: 100%;
- min-height: 100%;
- z-index: 0;
- display: flex;
- background-color: var(--action-50);
-
- &.fade {
- animation: fadeIn 1s ease-in;
- }
-
- video {
- pointer-events: none;
- opacity: .85;
- mix-blend-mode: multiply;
- filter: grayscale(100%) contrast(1);
- flex: 1 0 100%;
- object-fit: cover;
- }
- }
-
- .inner-wrap {
- position: relative;
- z-index: 2;
- width: 100%;
- padding: 2rem;
- color: var(--action-contrast);
-
- /* Better text readability */
- h1, h2, h3, h4, h5, h6 {
- word-spacing: 100vw;
- color: var(--action-contrast);
- text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
- margin: 2rem 0 0;
- }
-
- p {
- text-transform: uppercase;
- letter-spacing: 2px;
- margin: 0;
- color: var(--action-contrast);
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
- }
-
- .media-text {
- }
- .media-text figure {
- max-width: 50%;
- }
- @media (min-width: 768px) {
- .media-text {
- --align: flex-start;
- gap: 3rem;
- max-width: var(--content);
- }
- }
- .media-text > div {
- width: fit-content;
- }
- .buttons a {
- font-weight: var(--fw-h-bold);
- color: var(--action-contrast);
- border-color: var(--action-contrast);
- &:visited {
- color: var(--action-contrast);
- &:hover {
- color: var(--action-contrast);
- }
- }
- &:hover {
- background-color: var(--action-0);
- color: var(--action-contrast);
- }
- }
-
- .outline a {
- background-color: rgba(var(--base-rgb), rgba(var(--base-rgb),var(--op-3)));
- }
- .buttons {
- margin: 3rem 0;
- li {
- background-color: rgba(var(--action-rgb), var(--op-4));
- }
- }
- /* Button styles */
- .wp-block-button__link {
- text-shadow: none;
- }
- }
-
- /* Alignment classes */
- &.align-top-left {
- align-items: flex-start;
- justify-content: flex-start;
- }
- &.align-top-center {
- align-items: flex-start;
- justify-content: center;
- }
- &.align-top-right {
- align-items: flex-start;
- justify-content: flex-end;
- }
- &.align-center-left {
- align-items: center;
- justify-content: flex-start;
- }
- &.align-center {
- align-items: center;
- justify-content: center;
- }
- &.align-center-right {
- align-items: center;
- justify-content: flex-end;
- }
- &.align-bottom-left {
- align-items: flex-end;
- justify-content: flex-start;
- }
- &.align-bottom-center {
- align-items: flex-end;
- justify-content: center;
- }
- &.align-bottom-right {
- align-items: flex-end;
- justify-content: flex-end;
- }
-
- /* Full-width alignment */
- &.alignfull {
- width: 100vw;
- max-width: none;
- margin-left: calc(50% - 50vw);
- margin-right: calc(50% - 50vw);
- }
-
- /* Wide alignment */
- &.alignwide {
- max-width: 1200px;
- }
-}
-
-@keyframes fadeIn {
- from {
- opacity: 0;
- }
- to {
- opacity: 1;
- }
-}
-
diff --git a/src/video/view.js b/src/video/view.js
deleted file mode 100644
index 76bcd1a..0000000
--- a/src/video/view.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/** view.js **/
-document.addEventListener("DOMContentLoaded", function () {
- const lazyVideos = [].slice.call(
- document.querySelectorAll(".video-container video")
- );
-
- // Build a helper to actually set sources + load
- function loadVideo(video) {
- const sources = video.querySelectorAll("source[data-src]");
- sources.forEach(source => {
- source.src = source.dataset.src;
- });
- video.load();
- }
-
- // --- 1. IntersectionObserver (best case) ---
- if ("IntersectionObserver" in window) {
- const lazyVideoObserver = new IntersectionObserver(
- function (entries, observer) {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- loadVideo(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- {
- rootMargin: "200px 0px",
- threshold: 0.1,
- }
- );
-
- lazyVideos.forEach(video => lazyVideoObserver.observe(video));
- return;
- }
-
- // --- 2. Fallback: requestIdleCallback ---
- if ("requestIdleCallback" in window) {
- requestIdleCallback(() => {
- lazyVideos.forEach(video => loadVideo(video));
- });
- return;
- }
-
- // --- 3. Final fallback: load immediately ---
- lazyVideos.forEach(video => loadVideo(video));
-});
diff --git a/wp-config.php b/wp-config.php
deleted file mode 100644
index 5fe4c60..0000000
--- a/wp-config.php
+++ /dev/null
@@ -1,128 +0,0 @@
-<?php
-/**
- * The base configuration for WordPress
- *
- * The wp-config.php creation script uses this file during the installation.
- * You don't have to use the web site, you can copy this file to "wp-config.php"
- * and fill in the values.
- *
- * This file contains the following configurations:
- *
- * * Database settings
- * * Secret keys
- * * Database table prefix
- * * Localized language
- * * ABSPATH
- *
- * @link https://wordpress.org/support/article/editing-wp-config-php/
- *
- * @package WordPress
- */
-
-// ** Database settings - You can get this info from your web host ** //
-/** The name of the database for WordPress */
-define( 'DB_NAME', 'wp_9090417' );
-
-/** Database username */
-define( 'DB_USER', 'user-8484095' );
-
-/** Database password */
-define( 'DB_PASSWORD', 'CZvHXJ0wd2' );
-
-/** Database hostname */
-define( 'DB_HOST', '127.0.0.1' );
-
-/** Database charset to use in creating database tables. */
-define( 'DB_CHARSET', 'utf8' );
-
-/** The database collate type. Don't change this if in doubt. */
-define( 'DB_COLLATE', '' );
-
-/**#@+
- * Authentication unique keys and salts.
- *
- * Change these to different unique phrases! You can generate these using
- * the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}.
- *
- * You can change these at any point in time to invalidate all existing cookies.
- * This will force all users to have to log in again.
- *
- * @since 2.6.0
- */
-define( 'AUTH_KEY', 'qxPk61Jf2tn|mz0Dx$9(ikHT4)v[[uagWf3T3`x.Ds2NU[0y7*C3)Ci=ex+A~kp^' );
-define( 'SECURE_AUTH_KEY', 'kK7n[x_}ClcUw2^ow~%WR9aP.!O}kq7FXxbKuAwtBg@.2=r1~!x<R12Y_g10[P^{' );
-define( 'LOGGED_IN_KEY', '-0tgnq P _y8~m95C9E.[S+o]Z-GC/(|wyyn/}yO5 de51AM#)y#$i4=OQ!Q(2k=' );
-define( 'NONCE_KEY', '?V-na8MDy-n`S2>3<[i.D#27H/Qlt|v{=R/dSC}Km%p}r){aE?LB=-k:v2ip2#r)' );
-define( 'AUTH_SALT', '4L0JSr]Z!27na~D?P];2TEz*BVVG~jG;1Od|r28H;$CgWP:J~NUx>$ng.gke2s<5' );
-define( 'SECURE_AUTH_SALT', '8{LtR_$P;gi=c7j!w+>Pt6KS(T-]F2Wlf~Ca~4-_}A8=~]k3~6):06$;leK_?a G' );
-define( 'LOGGED_IN_SALT', 'hp6]B8`a? w^):H+|c!`UR[T5$rDjHQ$~X)^lZ~N>|swQ8h.1,c:Ut^K|~Rhe-kA' );
-define( 'NONCE_SALT', 'MnNfXD+<Ibe0C&<u]3xmawWV4~>#ckfXV#9@t<;gqr8>o$]D}NQZ6dXTY.L$by,G' );
-define( 'WP_CACHE_KEY_SALT', 'pU95(>.]6vpDh~9!F1C`T/ 02JB[EEMj*)W_pNDD{PRzEm`Oc+5iu?q6}9;F2[ F' );
-
-
-/**#@-*/
-
-/**
- * WordPress database table prefix.
- *
- * You can have multiple installations in one database if you give each
- * a unique prefix. Only numbers, letters, and underscores please!
- */
-$table_prefix = 'wp_';
-define( 'WP_AUTO_UPDATE_CORE', false );
-
-
-/* Add any custom values between this line and the "stop editing" line. */
-// Redis Object Cache Configuration
-define('WP_REDIS_HOST', '10.3.2.169');
-define('WP_REDIS_PORT', 6379);
-define('WP_REDIS_PASSWORD', 'CFKceq00762');
-define('WP_REDIS_TIMEOUT', 1);
-define('WP_REDIS_READ_TIMEOUT', 1);
-
-define('WP_REDIS_DATABASE', 0);
-define('WP_REDIS_PREFIX', 'jakevan_');
-define('WP_REDIS_MAXTTL', 86400 * 7);
-
-
-/**
- * For developers: WordPress debugging mode.
- *
- * Change this to true to enable the display of notices during development.
- * It is strongly recommended that plugin and theme developers use WP_DEBUG
- * in their development environments.
- *
- * For information on other constants that can be used for debugging,
- * visit the documentation.
- *
- * @link https://wordpress.org/support/article/debugging-in-wordpress/
- */
-define( 'WP_DEBUG', true );
-define( 'WP_DEBUG_DISPLAY', true );
-@ini_set( 'display_errors', 1 );
-
-// Use dev versions of core JS and CSS files (only needed if you are modifying these core files)
-define('SCRIPT_DEBUG', true);
-
-
-
-define( 'WP_ALLOW_MULTISITE', true );
-define( 'MULTISITE', true );
-define( 'SUBDOMAIN_INSTALL', true );
-$base = '/';
-define( 'DOMAIN_CURRENT_SITE', 'jakevan.ca' );
-define( 'PATH_CURRENT_SITE', '/' );
-define( 'SITE_ID_CURRENT_SITE', 1 );
-define( 'BLOG_ID_CURRENT_SITE', 1 );
-
-/* That's all, stop editing! Happy publishing. */
-
-/** Absolute path to the WordPress directory. */
-if ( ! defined( 'ABSPATH' ) ) {
- define( 'ABSPATH', __DIR__ . '/' );
-}
-
-/** Sets up WordPress vars and included files. */
-require_once ABSPATH . 'wp-jelastic.php';
-require_once ABSPATH . 'wp-settings.php';
-
--
Gitblit v1.10.0