From 4d04de8c1b6e5dbf9d48e329c5ef7b04c10a97be Mon Sep 17 00:00:00 2001 From: John Cox Date: Mon, 29 Dec 2025 13:48:09 -0700 Subject: [PATCH 01/18] v3.0 - block manifest, build and dev update --- package-lock.json | 11 ++++------- package.json | 4 +++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 12d97b01..9e503793 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "unityblocks", - "version": "2.5.1", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "unityblocks", - "version": "2.5.1", + "version": "3.0.0", "license": "GPL-2.0-or-later", "dependencies": { "@asu/component-carousel": "^1.2.1", @@ -27,6 +27,7 @@ "graphql-request": "^4.1.0", "lodash-es": "^4.17.21", "react-select": "^3.2.0", + "react-share": "^4.4.1", "styled-components": "^5.3.3", "swr": "^1.3.0" }, @@ -27348,7 +27349,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", "integrity": "sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", - "peer": true, "dependencies": { "debug": "^2.1.3" } @@ -27358,7 +27358,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -27367,8 +27366,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jsx-ast-utils": { "version": "3.3.5", @@ -31557,7 +31555,6 @@ "resolved": "https://registry.npmjs.org/react-share/-/react-share-4.4.1.tgz", "integrity": "sha512-AJ9m9RiJssqvYg7MoJUc9J0D7b/liWrsfQ99ndKc5vJ4oVHHd4Fy87jBlKEQPibT40oYA3AQ/a9/oQY6/yaigw==", "license": "MIT", - "peer": true, "dependencies": { "classnames": "^2.3.2", "jsonp": "^0.2.1" diff --git a/package.json b/package.json index 96596c0b..4fb73f54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unityblocks", - "version": "2.5.1", + "version": "3.0.0", "description": "UnityBlocks is a suite of page building content blocks for the ASU Web Standards Unity (UDS) WordPress theme.", "author": "ASU KE Web Services", "license": "GPL-2.0-or-later", @@ -16,6 +16,7 @@ "main": "build/index.js", "scripts": { "build": "wp-scripts build", + "manifest": "wp-scripts build-blocks-manifest", "format": "wp-scripts format", "lint:css": "wp-scripts lint-style", "lint:js": "wp-scripts lint-js", @@ -42,6 +43,7 @@ "graphql-request": "^4.1.0", "lodash-es": "^4.17.21", "react-select": "^3.2.0", + "react-share": "^4.4.1", "styled-components": "^5.3.3", "swr": "^1.3.0" }, From f4e438d49dca64d1b543f2b5eebe94ba80ac7e33 Mon Sep 17 00:00:00 2001 From: John Cox Date: Mon, 29 Dec 2025 13:49:29 -0700 Subject: [PATCH 02/18] v3 - update requirements, register via block manifest --- unityblocks.php | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/unityblocks.php b/unityblocks.php index 1f2388b4..fe433178 100644 --- a/unityblocks.php +++ b/unityblocks.php @@ -3,9 +3,9 @@ /** * Plugin Name: UnityBlocks * Description: UnityBlocks is a suite of page building content blocks for the ASU Web Standards Unity (UDS) WordPress theme. - * Requires at least: 6.1 - * Requires PHP: 7.0 - * Version: 2.5.1 + * Requires at least: 6.8 + * Requires PHP: 7.4 + * Version: 3.0.0 * Author: ASU KE Web Services * Author URI: https://rto.asu.edu/web-services * License: GPL-2.0-or-later @@ -35,23 +35,20 @@ function unityblocks_block_init() /** * Load current theme and check if it is Pitchfork or a Pitchfork Child */ - $theme_data = wp_get_theme(); - $pitchfork_theme = ('pitchfork' === $theme_data->get('TextDomain') || 'pitchfork' === $theme_data->get('Template')); + //$theme_data = wp_get_theme(); + //$pitchfork_theme = ('pitchfork' === $theme_data->get('TextDomain') || 'pitchfork' === $theme_data->get('Template')); // Register these blocks only if not using Pitchfork // Pitchfork already has these blocks - if (!$pitchfork_theme) { - register_block_type(__DIR__ . '/build/hero'); - } + // if (!$pitchfork_theme) { + // register_block_type(__DIR__ . '/build/hero'); + // } find new solution that works with block manifest + + wp_register_block_types_from_metadata_collection( + plugin_dir_path( __FILE__ ) . 'build', + plugin_dir_path( __FILE__ ) . 'build/blocks-manifest.php' +); - register_block_type(__DIR__ . '/build/anchor-menu'); - register_block_type(__DIR__ . '/build/asu-careers'); - register_block_type(__DIR__ . '/build/asu-events'); - register_block_type(__DIR__ . '/build/events-grid'); - register_block_type(__DIR__ . '/build/image-gallery'); - register_block_type(__DIR__ . '/build/news-grid'); - register_block_type(__DIR__ . '/build/testimonial'); - register_block_type(__DIR__ . '/build/wchm'); } add_action('init', 'unityblocks_block_init'); From 95467543e11e1beb4d3b45a8567f71aab55528ce Mon Sep 17 00:00:00 2001 From: John Cox Date: Mon, 29 Dec 2025 13:49:49 -0700 Subject: [PATCH 03/18] manifest init --- build/blocks-manifest.php | 806 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 806 insertions(+) create mode 100644 build/blocks-manifest.php diff --git a/build/blocks-manifest.php b/build/blocks-manifest.php new file mode 100644 index 00000000..90fbfba0 --- /dev/null +++ b/build/blocks-manifest.php @@ -0,0 +1,806 @@ + array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 2, + 'name' => 'unityblocks/anchor-menu', + 'version' => '2.1.0', + 'title' => 'Unity Anchor Menu', + 'category' => 'unityblocks', + 'keywords' => array( + 'unityblocks', + 'menu', + 'navigation' + ), + 'icon' => 'menu', + 'description' => 'Provide sticky menu bar to jump to anchor points on the current page.', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'textdomain' => 'unityblocks', + 'viewScript' => array( + 'file:./frontend.js', + 'unityblocks-anchor-menu-view-script' + ), + 'editorScript' => 'file:./index.js', + 'attributes' => array( + 'itemIcons' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'itemTexts' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'itemTargets' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'firstElementId' => array( + 'type' => 'string', + 'default' => '' + ), + 'focusFirstFocusableElement' => array( + 'type' => 'boolean', + 'default' => false + ) + ) + ), + 'asu-careers' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 2, + 'name' => 'unityblocks/asu-careers', + 'version' => '1.0.0', + 'title' => 'ASU Careers', + 'category' => 'unityblocks', + 'keywords' => array( + 'asu', + 'unityblocks', + 'careers', + 'jobs', + 'brassring' + ), + 'icon' => 'database-import', + 'description' => 'Display an interactive job listings blocks for ASU Careers, organized by departments.', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'textdomain' => 'unityblocks', + 'viewScript' => array( + 'file:./frontend.js', + 'unityblocks-careers-view-script' + ), + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'editor-styles-asu-career', + 'style' => 'styles-asu-career', + 'attributes' => array( + 'listType' => array( + 'type' => 'string', + 'enum' => array( + 'user-choice', + 'staff', + 'students' + ), + 'default' => 'user-choice' + ), + 'deptList' => array( + 'type' => 'array', + 'default' => array( + array( + 'label' => 'All departments', + 'value' => 'Advanced Materials Initiative, Advncd Electronics & Photonics, ASU Banner NDRC, ASU International Development, ASU Wrigley Inst Development, ASU Wrigley Inst Outreach, Biodesign Admin Support Ops, Biodesign Administration, Biodesign ASD, Biodesign BB, Biodesign BE, Biodesign Beus CXFEL Lab, Biodesign BSS, Biodesign CTL, Biodesign CXLS Operations, Biodesign EHE, Biodesign Facilities, Biodesign FAM, Biodesign HTM, Biodesign IM, Biodesign Institute, Biodesign ITS, Biodesign IVV, Biodesign MDB, Biodesign ME, Biodesign PC, Biodesign Resrch Opp Adv & Dev, Biodesign SM3, Biodesign SMB, Biodesign Swette EB, Biodesign Virginia G Piper PD, Biosciences, Business Services, CAP LTER, Center Biodiversity Outcomes, Center for Engagement Science, CGF Admin Support, CGF Ambassador SW, CGF Career & Alumni Services, CGF Executive & Prof Education, CGF Instructional Support, CGF Recrt Admissions Outreach, CGF Student Services SW, CGF Undergrad Student Services, Clinical Initiatives Dignity, College of Global Futures, Collg Research Eval Serv Team, Communications & Marketing, Complex Adaptive Systems, Cores Sales & Marketing, Corp Engagemt & Strat Partnshp, CSPO Grad Assists Assocs, Ctr Accelerating Op Efficiency, Ctr Cybersec Digital Forensics, Ctr Human AI & Robot Teaming, Ctr Narr, Decision Center Desert City, Decision Theater, Dept Animal Care Technologies, Disinfo & Strat Infl, E+I Venture Mentors, Economic Development, Education for Humanity, Engagement & Prof Development, Entrepreneurship + Innovation, Events, Eyring Materials Center, Flexible Electronics Display, Future H2O, Global Consort Sustain Outcome, Global Drylands Center, Global Futures Laboratory, Global Futures Staff, Global Inst of Sustain & Innov, Global Locust Initiative, Global Operations, Global Partnerships, Global Security Initiative, Health & Clinical Partnerships, Health and Clinical Services, Healthy Urban Environments, Industry Contracts, Instrument Design Fabrication, International Projects & Mgmt, Interplanetary Initiative, KE Biodesign Institute Finance, KE Business Units Finance, KE Core Facilities, KE Finance Process Development, KE Financial Services, KE Global Futures Finance, KE Initiatives Finance, KE Luminosity Lab, KE Planning Budget, KE Procurement, KE Serv & Rechg Ctrs Finan, KE Staff Support, KE Web Services, Knowldg Enterprise Initiatives, Knowldg Enterprise Operations, Knowledge Enterprise Analytics, Knowledge Enterprise Events, Knowledge Enterprise HR, LightWorks, Materials of the Universe Ctr, McCain Inst Intl Leadership, NanoFabrication, Ofc EVP Knowldge Enterprise, Ofc Research/Sponsored Project, Office of VP Research Develop, Operations PMO, ORSPA Award Management, ORSPA Fiscal Oversight, ORSPA Proposals & Negotiations, R&M Walton Sust Solutions Svc, Research Advancement Services, Research Development, Research Integrity & Assurance, Research Project Management, Research Tech Development, Research Tech Support, Research Technology Office, RTO Business Intelligence, RTO Enterprise Architecture, RTO Information Security, RTO Research Computing, RTO Research Editing, RTO Scientific Software Engrng, RTO Strategic Solutions, RTO Training, Sch Complex Adaptive Systems, Sch Future of Innov in Society, School of Sustainability, Sci & Imag-Grad Assist/Assocs, SFAz Center for STEM, SkySong M+E Logistics, Solar Fab, SOS Faculty & Researchers, SOS Grad Student Services, SOS Graduate Assistants Assocs, SOS Instructional Support, SOS Instructional SW, SOS International Programs, SOS Outreach & Relations, SOS Student Services, SOS Student Workers, Strategic Marketing and Comms, Sustainable Cities Network, Sustainbl Phosphorus Alliance, Swette Ctr Sust Food Systems, The Global KAITEKI Center, The Sustainability Consortium, Univ Research Space Planning, University Innovation Alliance, UREx Sustainability Research, Walton Sust Teachers Academics, Wetland Ecosystem Ecology Lab' + ) + ) + ), + 'titleText' => array( + 'type' => 'string', + 'default' => 'Current open positions' + ) + ) + ), + 'asu-events' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'unityblocks/asu-events', + 'version' => '1.2.0', + 'title' => 'ASU Events', + 'category' => 'unityblocks', + 'keywords' => array( + 'unityblocks', + 'events', + 'grid' + ), + 'icon' => 'grid-view', + 'description' => 'Dsiplay a grid layout for ASU Events.', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'textdomain' => 'unityblocks', + 'viewScript' => array( + 'file:./frontend.js', + 'unityblocks-asu-events-view-script' + ), + 'editorScript' => 'file:./index.js', + 'attributes' => array( + 'enableHeader' => array( + 'type' => 'boolean', + 'default' => true + ), + 'headerText' => array( + 'type' => 'string', + 'default' => 'Events Grid' + ), + 'headerColor' => array( + 'type' => 'string', + 'enum' => array( + 'white', + 'dark' + ), + 'default' => 'dark' + ), + 'ctaColor' => array( + 'type' => 'string', + 'enum' => array( + 'gold', + 'maroon', + 'gray', + 'dark' + ), + 'default' => 'maroon' + ), + 'ctaText' => array( + 'type' => 'string', + 'default' => 'Click to see more events' + ), + 'ctaUrl' => array( + 'type' => 'string', + 'default' => 'https://asuevents.asu.edu/' + ), + 'dataSourceUrl' => array( + 'type' => 'string', + 'default' => 'https://cors.api.rtd.asu.edu/asuevents.asu.edu/feed-json/' + ), + 'dataSourceFilters' => array( + 'type' => 'string', + 'default' => '' + ), + 'noResultsText' => array( + 'type' => 'string', + 'default' => 'No events found.' + ), + 'maxItems' => array( + 'type' => 'number', + 'default' => 6 + ) + ) + ), + 'events-grid' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 2, + 'name' => 'unityblocks/events-grid', + 'version' => '1.0.0', + 'title' => 'KE Events Grid (DEPRECATED)', + 'category' => 'unityblocks', + 'keywords' => array( + 'unityblocks', + 'events', + 'grid' + ), + 'icon' => 'grid-view', + 'description' => 'Provide a grid layout for ASU and KE Events.', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'textdomain' => 'unityblocks', + 'viewScript' => array( + 'file:./frontend.js', + 'unityblocks-events-grid-view-script' + ), + 'editorScript' => 'file:./index.js', + 'attributes' => array( + 'enableHeader' => array( + 'type' => 'boolean', + 'default' => true + ), + 'headerText' => array( + 'type' => 'string', + 'default' => 'Events Grid' + ), + 'headerColor' => array( + 'type' => 'string', + 'enum' => array( + 'white', + 'dark' + ), + 'default' => 'dark' + ), + 'ctaText' => array( + 'type' => 'string', + 'default' => 'Click to see more events' + ), + 'ctaUrl' => array( + 'type' => 'string', + 'default' => 'https://asuevents.asu.edu/' + ), + 'ctaColor' => array( + 'type' => 'string', + 'enum' => array( + 'gold', + 'maroon', + 'gray', + 'dark' + ), + 'default' => 'maroon' + ), + 'dataSourceType' => array( + 'type' => 'string', + 'enum' => array( + 'asuDrupal', + 'keGraphql' + ), + 'default' => 'keGraphql' + ), + 'dataSourceAsuUrl' => array( + 'type' => 'string', + 'default' => 'https://cors.api.rtd.asu.edu/asuevents.asu.edu/feed-json/' + ), + 'dataSourceKeUrl' => array( + 'type' => 'string', + 'default' => 'https://infonet.api.rtd.asu.edu/graphql/' + ), + 'keEventBasePath' => array( + 'type' => 'string', + 'default' => 'events/view' + ), + 'dataSourceFeed' => array( + 'type' => 'string', + 'default' => 'online' + ), + 'asuFilterUnits' => array( + 'type' => 'string', + 'default' => '' + ), + 'keFilterUnits' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'keSortEvents' => array( + 'type' => 'string', + 'enum' => array( + 'ASC', + 'DESC' + ), + 'default' => 'ASC' + ), + 'keShowPastEvents' => array( + 'type' => 'boolean', + 'default' => false + ), + 'keShowFutureEvents' => array( + 'type' => 'boolean', + 'default' => true + ), + 'noResultsText' => array( + 'type' => 'string', + 'default' => 'No events found.' + ), + 'maxItems' => array( + 'type' => 'number', + 'default' => 6 + ) + ) + ), + 'hero' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'unityblocks/hero', + 'version' => '2.0.0', + 'title' => 'Unity Hero', + 'category' => 'unityblocks', + 'keywords' => array( + 'unityblocks', + 'images', + 'layout' + ), + 'icon' => 'cover-image', + 'description' => 'Provide Hero for page headers.', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'textdomain' => 'unityblocks', + 'viewScript' => array( + 'file:./frontend.js', + 'unityblocks-hero-view-script' + ), + 'editorScript' => 'file:./index.js', + 'attributes' => array( + 'heroType' => array( + 'type' => 'string', + 'enum' => array( + 'heading-hero', + 'story-hero' + ), + 'default' => 'heading-hero' + ), + 'mediaId' => array( + 'type' => 'number', + 'default' => 0 + ), + 'mediaUrl' => array( + 'type' => 'string', + 'default' => '' + ), + 'mediaAltText' => array( + 'type' => 'string', + 'default' => '' + ), + 'mediaCssClass' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'mediaSize' => array( + 'type' => 'string', + 'enum' => array( + 'small', + 'medium', + 'large' + ), + 'default' => 'medium' + ), + 'subTitleText' => array( + 'type' => 'string', + 'default' => '' + ), + 'subTitleHighlightColor' => array( + 'type' => 'string', + 'enum' => array( + 'gold', + 'white', + 'black', + 'none' + ), + 'default' => 'white' + ), + 'subTitleMaxWidth' => array( + 'type' => 'string', + 'default' => '' + ), + 'subTitleCssClass' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'titleText' => array( + 'type' => 'string', + 'default' => '' + ), + 'titleHighlightColor' => array( + 'type' => 'string', + 'enum' => array( + 'gold', + 'white', + 'black', + 'none' + ), + 'default' => 'gold' + ), + 'titleMaxWidth' => array( + 'type' => 'string', + 'default' => '' + ), + 'titleCssClass' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'contentsText' => array( + 'type' => 'string', + 'default' => '' + ), + 'contentsMaxWidth' => array( + 'type' => 'string', + 'default' => '' + ), + 'contentsCssClass' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'contentsHighlightColor' => array( + 'type' => 'string', + 'enum' => array( + 'gold', + 'white', + 'black' + ), + 'default' => 'white' + ), + 'contentsColor' => array( + 'type' => 'string', + 'enum' => array( + 'white', + 'black' + ), + 'default' => 'white' + ) + ) + ), + 'image-gallery' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 2, + 'name' => 'unityblocks/image-gallery', + 'version' => '1.0.0', + 'title' => 'Unity Image Gallery (UNFINISHED)', + 'category' => 'unityblocks', + 'keywords' => array( + 'unityblocks', + 'images', + 'gallery' + ), + 'icon' => 'cover-image', + 'description' => 'Renders an ASU-branded image gallery.', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'textdomain' => 'unityblocks', + 'viewScript' => array( + 'file:./frontend.js', + 'unityblocks-image-gallery-view-script' + ), + 'editorScript' => 'file:./index.js', + 'attributes' => array( + 'images' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'hasContent' => array( + 'type' => 'boolean', + 'default' => true + ), + 'imageAutoSize' => array( + 'type' => 'boolean', + 'default' => true + ), + 'width' => array( + 'type' => 'string', + 'default' => '' + ), + 'maxWidth' => array( + 'type' => 'string', + 'default' => '' + ) + ) + ), + 'news-grid' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'unityblocks/news-grid', + 'version' => '2.1.0', + 'title' => 'Unity News Grid', + 'category' => 'unityblocks', + 'keywords' => array( + 'unityblocks', + 'news', + 'grid' + ), + 'icon' => 'grid-view', + 'description' => 'Provide a grid layout for ASU and KE News.', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'textdomain' => 'unityblocks', + 'viewScript' => array( + 'file:./frontend.js', + 'unityblocks-news-grid-view-script' + ), + 'editorScript' => 'file:./index.js', + 'example' => array( + 'attributes' => array( + 'enableHeader' => true, + 'headerText' => 'News Grid', + 'enableAsuDataSource' => true, + 'maxItems' => 6, + 'numberColumns' => '3' + ) + ), + 'attributes' => array( + 'enableHeader' => array( + 'type' => 'boolean', + 'default' => true + ), + 'headerText' => array( + 'type' => 'string', + 'default' => 'News Grid' + ), + 'headerColor' => array( + 'type' => 'string', + 'enum' => array( + 'dark', + 'white' + ), + 'default' => 'dark' + ), + 'ctaText' => array( + 'type' => 'string', + 'default' => 'Click to see more news' + ), + 'ctaUrl' => array( + 'type' => 'string', + 'default' => 'https://news.asu.edu/' + ), + 'ctaColor' => array( + 'type' => 'string', + 'enum' => array( + 'gold', + 'maroon', + 'gray', + 'dark' + ), + 'default' => 'maroon' + ), + 'cardButtonText' => array( + 'type' => 'string', + 'default' => 'Read' + ), + 'cardButtonColor' => array( + 'type' => 'string', + 'enum' => array( + 'gold', + 'maroon', + 'gray', + 'dark' + ), + 'default' => 'gold' + ), + 'cardButtonSize' => array( + 'type' => 'string', + 'enum' => array( + 'default', + 'small', + 'medium', + 'large' + ), + 'default' => 'default' + ), + 'useCardButton' => array( + 'type' => 'boolean', + 'default' => true + ), + 'cardLinkText' => array( + 'type' => 'string', + 'default' => 'Read' + ), + 'enableStoryDate' => array( + 'type' => 'boolean', + 'default' => true + ), + 'enableStoryAuthor' => array( + 'type' => 'boolean', + 'default' => false + ), + 'enableCardTags' => array( + 'type' => 'boolean', + 'default' => false + ), + 'enableAsuDataSource' => array( + 'type' => 'boolean', + 'default' => true + ), + 'asuDataSourceUrl' => array( + 'type' => 'string', + 'default' => 'https://cors.api.rtd.asu.edu/news.asu.edu:443/feeds-json/' + ), + 'asuDataSourceFeed' => array( + 'type' => 'string', + 'default' => 'biodesign_institute' + ), + 'asuDataSourceFilters' => array( + 'type' => 'string', + 'default' => '' + ), + 'enableKeDataSource' => array( + 'type' => 'boolean', + 'default' => false + ), + 'keDataSourceUrl' => array( + 'type' => 'string', + 'default' => 'https://ke.news.prod.rtd.asu.edu/wp-json/wp/v2/' + ), + 'keStoryBasePath' => array( + 'type' => 'string', + 'default' => 'news/story' + ), + 'keDataSourceUnits' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'keDataSourceInterests' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'keDataSourceLocations' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'keDataSourcePage' => array( + 'type' => 'string', + 'default' => '1' + ), + 'keDataSourcePerPage' => array( + 'type' => 'string', + 'default' => '100' + ), + 'maxItems' => array( + 'type' => 'number', + 'default' => 6 + ), + 'numberColumns' => array( + 'type' => 'string', + 'enum' => array( + '2', + '3' + ), + 'default' => '3' + ) + ) + ), + 'testimonial' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 2, + 'name' => 'unityblocks/testimonial', + 'version' => '1.0.0', + 'title' => 'Unity Testimonial', + 'category' => 'unityblocks', + 'keywords' => array( + 'unityblocks', + 'quotes', + 'testimonials' + ), + 'icon' => 'format-quote', + 'description' => 'Provide Testimonial block.', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'textdomain' => 'unityblocks', + 'viewScript' => array( + 'file:./frontend.js', + 'unityblocks-testimonial-view-script' + ), + 'editorScript' => 'file:./index.js', + 'attributes' => array( + 'quoteTitle' => array( + 'type' => 'string', + 'default' => '' + ), + 'quoteContent' => array( + 'type' => 'string', + 'default' => '' + ), + 'quoteCiteName' => array( + 'type' => 'string', + 'default' => '' + ), + 'quoteCiteDescription' => array( + 'type' => 'string', + 'default' => '' + ), + 'mediaId' => array( + 'type' => 'number', + 'default' => 0 + ), + 'mediaUrl' => array( + 'type' => 'string', + 'default' => '' + ), + 'mediaAltText' => array( + 'type' => 'string', + 'default' => '' + ), + 'containerCssClass' => array( + 'type' => 'string', + 'default' => '' + ), + 'titleCssClass' => array( + 'type' => 'string', + 'default' => '' + ), + 'contentCssClass' => array( + 'type' => 'string', + 'default' => '' + ) + ) + ), + 'wchm' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 2, + 'name' => 'unityblocks/wchm', + 'version' => '1.0.0', + 'title' => 'WCHM', + 'category' => 'unityblocks', + 'keywords' => array( + 'asu', + 'unityblocks', + 'ho', + 'can', + 'help', + 'me', + 'wchm' + ), + 'icon' => 'database-import', + 'description' => 'Display a contact list for WCHM', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'textdomain' => 'unityblocks', + 'viewScript' => array( + 'file:./frontend.js', + 'unityblocks-careers-view-script' + ), + 'editorScript' => 'file:./index.js', + 'style' => 'styles-wchm', + 'attributes' => array( + 'useProdApi' => array( + 'type' => 'boolean', + 'default' => true + ) + ) + ) +); From a4d7db299f9a956980a99a7ed349f8e63529073a Mon Sep 17 00:00:00 2001 From: John Cox Date: Tue, 30 Dec 2025 14:50:55 -0700 Subject: [PATCH 04/18] Update block registration logic for compatibility Refactor block registration to support WP version checks and use block manifest. --- unityblocks.php | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/unityblocks.php b/unityblocks.php index fe433178..33894bc7 100644 --- a/unityblocks.php +++ b/unityblocks.php @@ -32,22 +32,18 @@ */ function unityblocks_block_init() { - /** - * Load current theme and check if it is Pitchfork or a Pitchfork Child - */ - //$theme_data = wp_get_theme(); - //$pitchfork_theme = ('pitchfork' === $theme_data->get('TextDomain') || 'pitchfork' === $theme_data->get('Template')); - - // Register these blocks only if not using Pitchfork - // Pitchfork already has these blocks - // if (!$pitchfork_theme) { - // register_block_type(__DIR__ . '/build/hero'); - // } find new solution that works with block manifest - - wp_register_block_types_from_metadata_collection( - plugin_dir_path( __FILE__ ) . 'build', - plugin_dir_path( __FILE__ ) . 'build/blocks-manifest.php' -); + // check WP version: v6.8+, v6.7, pre 6.7 + if ( function_exists( 'wp_register_block_types_from_metadata_collection' ) ) { + wp_register_block_types_from_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' ); + } else { + if ( function_exists( 'wp_register_block_metadata_collection' ) ) { + wp_register_block_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' ); + } + $manifest_data = require __DIR__ . '/build/blocks-manifest.php'; + foreach ( array_keys( $manifest_data ) as $block_type ) { + register_block_type( __DIR__ . "/build/{$block_type}" ); + } +} } add_action('init', 'unityblocks_block_init'); From a6a2e53947553c86241dae633e773e581fff4ae9 Mon Sep 17 00:00:00 2001 From: John Cox Date: Tue, 30 Dec 2025 14:56:46 -0700 Subject: [PATCH 05/18] Update required WordPress version to 6.1 --- unityblocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unityblocks.php b/unityblocks.php index 33894bc7..8d3019d9 100644 --- a/unityblocks.php +++ b/unityblocks.php @@ -3,7 +3,7 @@ /** * Plugin Name: UnityBlocks * Description: UnityBlocks is a suite of page building content blocks for the ASU Web Standards Unity (UDS) WordPress theme. - * Requires at least: 6.8 + * Requires at least: 6.1 * Requires PHP: 7.4 * Version: 3.0.0 * Author: ASU KE Web Services From d3f71d984a2ef8080038100b1ef925bb56f28e2d Mon Sep 17 00:00:00 2001 From: John Cox Date: Tue, 30 Dec 2025 14:57:20 -0700 Subject: [PATCH 06/18] Update unityblocks.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- unityblocks.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/unityblocks.php b/unityblocks.php index 8d3019d9..16906965 100644 --- a/unityblocks.php +++ b/unityblocks.php @@ -34,16 +34,16 @@ function unityblocks_block_init() { // check WP version: v6.8+, v6.7, pre 6.7 if ( function_exists( 'wp_register_block_types_from_metadata_collection' ) ) { - wp_register_block_types_from_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' ); + wp_register_block_types_from_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' ); } else { - if ( function_exists( 'wp_register_block_metadata_collection' ) ) { - wp_register_block_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' ); - } - $manifest_data = require __DIR__ . '/build/blocks-manifest.php'; - foreach ( array_keys( $manifest_data ) as $block_type ) { - register_block_type( __DIR__ . "/build/{$block_type}" ); - } -} + if ( function_exists( 'wp_register_block_metadata_collection' ) ) { + wp_register_block_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' ); + } + $manifest_data = require __DIR__ . '/build/blocks-manifest.php'; + foreach ( array_keys( $manifest_data ) as $block_type ) { + register_block_type( __DIR__ . "/build/{$block_type}" ); + } + } } add_action('init', 'unityblocks_block_init'); From 8e1d450f42f1c04f8ee946d1daecde0eaa906f7c Mon Sep 17 00:00:00 2001 From: John Cox Date: Tue, 30 Dec 2025 16:33:50 -0700 Subject: [PATCH 07/18] v3.1 ranking card --- unityblocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unityblocks.php b/unityblocks.php index 16906965..9e64a961 100644 --- a/unityblocks.php +++ b/unityblocks.php @@ -5,7 +5,7 @@ * Description: UnityBlocks is a suite of page building content blocks for the ASU Web Standards Unity (UDS) WordPress theme. * Requires at least: 6.1 * Requires PHP: 7.4 - * Version: 3.0.0 + * Version: 3.1.0 * Author: ASU KE Web Services * Author URI: https://rto.asu.edu/web-services * License: GPL-2.0-or-later From a921e693d0e0603f2175b1ae5970712a482893f1 Mon Sep 17 00:00:00 2001 From: John Cox Date: Tue, 30 Dec 2025 16:34:26 -0700 Subject: [PATCH 08/18] npm i asu/react-core --- package-lock.json | 7 ++++--- package.json | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9e503793..bfb3ea69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@asu/component-carousel": "^1.2.1", "@asu/component-events": "^3.0.1", "@asu/components-core": "^3.1.0", + "@asu/unity-react-core": "^1.3.0", "@fortawesome/fontawesome-svg-core": "^6.3.0", "@fortawesome/free-regular-svg-icons": "^6.3.0", "@fortawesome/free-solid-svg-icons": "^6.3.0", @@ -239,9 +240,9 @@ } }, "node_modules/@asu/unity-react-core": { - "version": "1.2.0", - "resolved": "https://npm.pkg.github.com/download/@asu/unity-react-core/1.2.0/9addba731f2d1294f478ab71fdb0093d67a5e57c", - "integrity": "sha512-FNP000fSTmVZKicY15OWOHXHwmc3GXloA08qrn14LEc6R7TSkhi+TA2OTExN/U2vDWn98xKAOvaVknj2nshV1g==", + "version": "1.3.0", + "resolved": "https://npm.pkg.github.com/download/@asu/unity-react-core/1.3.0/46b1504f1295e485715df56829b90e3ee6508f0e", + "integrity": "sha512-3OXl+7e6vn7J695DDaG8QPYMlyH7evc1o7GTV5zSp353jayjTXr5xop7xvZt7MXSEpaPM5bM+OBYsoIm+fhoLQ==", "license": "MIT", "dependencies": { "@glidejs/glide": "~3.6.2", diff --git a/package.json b/package.json index 4fb73f54..e57c3177 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@asu/component-carousel": "^1.2.1", "@asu/component-events": "^3.0.1", "@asu/components-core": "^3.1.0", + "@asu/unity-react-core": "^1.3.0", "@fortawesome/fontawesome-svg-core": "^6.3.0", "@fortawesome/free-regular-svg-icons": "^6.3.0", "@fortawesome/free-solid-svg-icons": "^6.3.0", From 271fb37974be0addb2b9b2294e050d2bad6811b3 Mon Sep 17 00:00:00 2001 From: John Cox Date: Tue, 30 Dec 2025 16:34:42 -0700 Subject: [PATCH 09/18] init ranking card --- src/ranking-card/block.json | 24 +++++ src/ranking-card/edit.js | 87 ++++++++++++++++ src/ranking-card/frontend.js | 30 ++++++ src/ranking-card/index.js | 40 ++++++++ src/ranking-card/inspector.js | 186 ++++++++++++++++++++++++++++++++++ src/ranking-card/save.js | 66 ++++++++++++ 6 files changed, 433 insertions(+) create mode 100644 src/ranking-card/block.json create mode 100644 src/ranking-card/edit.js create mode 100644 src/ranking-card/frontend.js create mode 100644 src/ranking-card/index.js create mode 100644 src/ranking-card/inspector.js create mode 100644 src/ranking-card/save.js diff --git a/src/ranking-card/block.json b/src/ranking-card/block.json new file mode 100644 index 00000000..0bde636c --- /dev/null +++ b/src/ranking-card/block.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "unityblocks/ranking-card", + "version": "1.0.0", + "title": "Ranking Card", + "category": "unityblocks", + "keywords": ["unityblocks", "ranking", "card"], + "icon": "index-card", + "description": "A UDS default design - ranking card with an interactive show more.", + "supports": { + "html": false, + "align": ["wide", "full"] + }, + "textdomain": "unityblocks", + "viewScript": ["file:./frontend.js", "unityblocks-ranking-card-view-script"], + "editorScript": "file:./index.js", + "attributes": { + "maxItems": { + "type": "number", + "default": 6 + } + } +} diff --git a/src/ranking-card/edit.js b/src/ranking-card/edit.js new file mode 100644 index 00000000..8db4ce20 --- /dev/null +++ b/src/ranking-card/edit.js @@ -0,0 +1,87 @@ +/** + * 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"; + +/** + * External dependencies + */ +import { RankingCard } from "@asu/unity-react-core/dist/unityReactCore.es"; + +/** + * Internal dependencies + */ +import Inspector from "./inspector"; + +/** + * 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 {WPElement} Element to render. + */ +const Edit = (props) => { + const { + attributes: { + maxItems, + }, + } = props; + + const header = enableHeader + ? { + color: headerColor, + text: headerText, + } + : null; + + const ctaButton = enableHeader + ? { + color: ctaColor, + text: ctaText, + url: ctaUrl, + } + : null; + + const dataSource = { + url: dataSourceUrl, + filters: dataSourceFilters, + }; + + const args = { + header, + ctaButton, + dataSource, + noResultsText, + maxItems, + }; + + return ( + <> + +
+ +
+ + ); +}; + +export default Edit; diff --git a/src/ranking-card/frontend.js b/src/ranking-card/frontend.js new file mode 100644 index 00000000..9de046db --- /dev/null +++ b/src/ranking-card/frontend.js @@ -0,0 +1,30 @@ +const { render } = wp.element; + +import { RankingCard } from "@asu/unity-react-core/dist/unityReactCore.es"; + +// It is possible to load multiple events grids onto a page. +// Load each DOM element on page using the Gutenberg-generated class for the Events Grid block +const cards = document.querySelectorAll(".wp-block-unityblocks-ranking-card"); + +cards.forEach((rankingCard) => { + const enableHeader = rankingCard.dataset.enableheader === "true"; + const dataSource = JSON.parse(rankingCard.dataset.datasource); + const noResultsText = rankingCard.dataset.noresultstext; + const maxItems = rankingCard.dataset.maxitems; + + const props = enableHeader + ? { + header: JSON.parse(rankingCard.dataset.header), + ctaButton: JSON.parse(rankingCard.dataset.ctabutton), + dataSource, + noResultsText, + maxItems, + } + : { + dataSource, + noResultsText, + maxItems, + }; + + render(, rankingCard); +}); diff --git a/src/ranking-card/index.js b/src/ranking-card/index.js new file mode 100644 index 00000000..523e42d1 --- /dev/null +++ b/src/ranking-card/index.js @@ -0,0 +1,40 @@ +/** + * 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"; +import save from "./save"; + +/** + * 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, + + /** + * @see ./save.js + */ + save, + example: () => {}, +}); diff --git a/src/ranking-card/inspector.js b/src/ranking-card/inspector.js new file mode 100644 index 00000000..8e35dc0a --- /dev/null +++ b/src/ranking-card/inspector.js @@ -0,0 +1,186 @@ +/** + * WordPress dependencies + */ +import { __ } from "@wordpress/i18n"; +import { InspectorControls } from "@wordpress/block-editor"; +import { + PanelBody, + PanelRow, + RadioControl, + TextControl, + ToggleControl, +} from "@wordpress/components"; + +/** + * Inspector controls + * + * @param {Object} props + */ +const Inspector = (props) => { + const { + attributes: { + enableHeader, + headerText, + headerColor, + ctaColor, + ctaText, + ctaUrl, + dataSourceUrl, + dataSourceFilters, + noResultsText, + maxItems, + }, + setAttributes, + } = props; + + return ( + <> + + + + { + setAttributes({ enableHeader: value }); + }} + /> + + {enableHeader && ( + <> + + setAttributes({ headerText: value })} + /> + + + setAttributes({ headerColor: value })} + /> + + + )} + + + {enableHeader && ( + + + setAttributes({ ctaText: value })} + /> + + + setAttributes({ ctaUrl: value })} + /> + + + setAttributes({ ctaColor: value })} + /> + + + )} + + + + setAttributes({ noResultsText: value })} + /> + + + + setAttributes({ + maxItems: Number(value), // Force attribute to number because this input field returns value as string. + }) + } + /> + + + + + + + setAttributes({ + dataSourceUrl: value, + }) + } + /> + + + + setAttributes({ + dataSourceFilters: value, + }) + } + /> + + + + + ); +}; + +export default Inspector; diff --git a/src/ranking-card/save.js b/src/ranking-card/save.js new file mode 100644 index 00000000..547d0bde --- /dev/null +++ b/src/ranking-card/save.js @@ -0,0 +1,66 @@ +/** + * 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. + */ +const save = (props) => { + const { + attributes: { + enableHeader, + headerText, + headerColor, + ctaColor, + ctaText, + ctaUrl, + dataSourceUrl, + dataSourceFilters, + noResultsText, + maxItems, + }, + } = props; + + const header = enableHeader + ? JSON.stringify({ + color: headerColor, + text: headerText, + }) + : null; + + const ctaButton = enableHeader + ? JSON.stringify({ + color: ctaColor, + text: ctaText, + url: ctaUrl, + }) + : null; + + let dataSource = JSON.stringify({ + url: dataSourceUrl, + filters: dataSourceFilters, + }); + + const dataAttributes = { + "data-enableheader": enableHeader, + "data-header": header, + "data-ctabutton": ctaButton, + "data-datasource": dataSource, + "data-noresultstext": noResultsText, + "data-maxitems": maxItems, + }; + + return
; +}; + +export default save; From f0d84bc0f6244545dd6ca898edfdcae54c84f144 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:50:03 +0000 Subject: [PATCH 10/18] Initial plan From 9f4da9fcc2322c49175f2addafa8d1af17a2c107 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:59:01 +0000 Subject: [PATCH 11/18] Update ranking-card block with correct props from unity-react-core Co-authored-by: jkcox <8856538+jkcox@users.noreply.github.com> --- src/ranking-card/block.json | 31 ++++- src/ranking-card/edit.js | 40 +++---- src/ranking-card/frontend.js | 37 +++--- src/ranking-card/inspector.js | 211 +++++++++++++--------------------- src/ranking-card/save.js | 50 +++----- 5 files changed, 151 insertions(+), 218 deletions(-) diff --git a/src/ranking-card/block.json b/src/ranking-card/block.json index 0bde636c..f2caa46a 100644 --- a/src/ranking-card/block.json +++ b/src/ranking-card/block.json @@ -16,9 +16,34 @@ "viewScript": ["file:./frontend.js", "unityblocks-ranking-card-view-script"], "editorScript": "file:./index.js", "attributes": { - "maxItems": { - "type": "number", - "default": 6 + "imageSize": { + "type": "string", + "enum": ["small", "large"], + "default": "large" + }, + "image": { + "type": "string", + "default": "" + }, + "imageAlt": { + "type": "string", + "default": "" + }, + "heading": { + "type": "string", + "default": "" + }, + "body": { + "type": "string", + "default": "" + }, + "readMoreLink": { + "type": "string", + "default": "" + }, + "citation": { + "type": "string", + "default": "" } } } diff --git a/src/ranking-card/edit.js b/src/ranking-card/edit.js index 8db4ce20..96c6f7e8 100644 --- a/src/ranking-card/edit.js +++ b/src/ranking-card/edit.js @@ -42,36 +42,24 @@ import Inspector from "./inspector"; const Edit = (props) => { const { attributes: { - maxItems, + imageSize, + image, + imageAlt, + heading, + body, + readMoreLink, + citation, }, } = props; - const header = enableHeader - ? { - color: headerColor, - text: headerText, - } - : null; - - const ctaButton = enableHeader - ? { - color: ctaColor, - text: ctaText, - url: ctaUrl, - } - : null; - - const dataSource = { - url: dataSourceUrl, - filters: dataSourceFilters, - }; - const args = { - header, - ctaButton, - dataSource, - noResultsText, - maxItems, + imageSize, + image, + imageAlt, + heading, + body, + readMoreLink, + citation, }; return ( diff --git a/src/ranking-card/frontend.js b/src/ranking-card/frontend.js index 9de046db..1968a1ae 100644 --- a/src/ranking-card/frontend.js +++ b/src/ranking-card/frontend.js @@ -2,29 +2,28 @@ const { render } = wp.element; import { RankingCard } from "@asu/unity-react-core/dist/unityReactCore.es"; -// It is possible to load multiple events grids onto a page. -// Load each DOM element on page using the Gutenberg-generated class for the Events Grid block +// It is possible to load multiple ranking cards onto a page. +// Load each DOM element on page using the Gutenberg-generated class for the Ranking Card block const cards = document.querySelectorAll(".wp-block-unityblocks-ranking-card"); cards.forEach((rankingCard) => { - const enableHeader = rankingCard.dataset.enableheader === "true"; - const dataSource = JSON.parse(rankingCard.dataset.datasource); - const noResultsText = rankingCard.dataset.noresultstext; - const maxItems = rankingCard.dataset.maxitems; + const imageSize = rankingCard.dataset.imagesize; + const image = rankingCard.dataset.image; + const imageAlt = rankingCard.dataset.imagealt; + const heading = rankingCard.dataset.heading; + const body = rankingCard.dataset.body; + const readMoreLink = rankingCard.dataset.readmorelink; + const citation = rankingCard.dataset.citation; - const props = enableHeader - ? { - header: JSON.parse(rankingCard.dataset.header), - ctaButton: JSON.parse(rankingCard.dataset.ctabutton), - dataSource, - noResultsText, - maxItems, - } - : { - dataSource, - noResultsText, - maxItems, - }; + const props = { + imageSize, + image, + imageAlt, + heading, + body, + readMoreLink, + citation, + }; render(, rankingCard); }); diff --git a/src/ranking-card/inspector.js b/src/ranking-card/inspector.js index 8e35dc0a..8f9acbec 100644 --- a/src/ranking-card/inspector.js +++ b/src/ranking-card/inspector.js @@ -2,13 +2,14 @@ * WordPress dependencies */ import { __ } from "@wordpress/i18n"; -import { InspectorControls } from "@wordpress/block-editor"; +import { InspectorControls, MediaUpload, MediaUploadCheck } from "@wordpress/block-editor"; import { PanelBody, PanelRow, RadioControl, TextControl, - ToggleControl, + TextareaControl, + Button, } from "@wordpress/components"; /** @@ -19,16 +20,13 @@ import { const Inspector = (props) => { const { attributes: { - enableHeader, - headerText, - headerColor, - ctaColor, - ctaText, - ctaUrl, - dataSourceUrl, - dataSourceFilters, - noResultsText, - maxItems, + imageSize, + image, + imageAlt, + heading, + body, + readMoreLink, + citation, }, setAttributes, } = props; @@ -36,147 +34,92 @@ const Inspector = (props) => { return ( <> - + - { - setAttributes({ enableHeader: value }); - }} + setAttributes({ imageSize: value })} /> - {enableHeader && ( - <> - - setAttributes({ headerText: value })} - /> - - - setAttributes({ headerColor: value })} - /> - - - )} - - - {enableHeader && ( - - - setAttributes({ ctaText: value })} - /> - - - setAttributes({ ctaUrl: value })} - /> - - - setAttributes({ ctaColor: value })} - /> - - - )} - - - setAttributes({ noResultsText: value })} - /> + + { + setAttributes({ + image: media.url, + imageAlt: media.alt || "", + }); + }} + allowedTypes={["image"]} + value={image} + render={({ open }) => ( +
+ + {image && ( +
+ {imageAlt} +
+ )} +
+ )} + /> +
- setAttributes({ - maxItems: Number(value), // Force attribute to number because this input field returns value as string. - }) - } + label={__("Image Alt Text", "unityblocks")} + value={imageAlt} + onChange={(value) => setAttributes({ imageAlt: value })} />
- + - setAttributes({ - dataSourceUrl: value, - }) - } + label={__("Heading", "unityblocks")} + value={heading} + onChange={(value) => setAttributes({ heading: value })} + /> + + + setAttributes({ body: value })} + rows={6} /> - setAttributes({ - dataSourceFilters: value, - }) - } + label={__("Read More Link", "unityblocks")} + value={readMoreLink} + onChange={(value) => setAttributes({ readMoreLink: value })} /> + {imageSize === "small" && ( + + setAttributes({ citation: value })} + /> + + )}
diff --git a/src/ranking-card/save.js b/src/ranking-card/save.js index 547d0bde..6d4cf107 100644 --- a/src/ranking-card/save.js +++ b/src/ranking-card/save.js @@ -18,46 +18,24 @@ import { useBlockProps } from "@wordpress/block-editor"; const save = (props) => { const { attributes: { - enableHeader, - headerText, - headerColor, - ctaColor, - ctaText, - ctaUrl, - dataSourceUrl, - dataSourceFilters, - noResultsText, - maxItems, + imageSize, + image, + imageAlt, + heading, + body, + readMoreLink, + citation, }, } = props; - const header = enableHeader - ? JSON.stringify({ - color: headerColor, - text: headerText, - }) - : null; - - const ctaButton = enableHeader - ? JSON.stringify({ - color: ctaColor, - text: ctaText, - url: ctaUrl, - }) - : null; - - let dataSource = JSON.stringify({ - url: dataSourceUrl, - filters: dataSourceFilters, - }); - const dataAttributes = { - "data-enableheader": enableHeader, - "data-header": header, - "data-ctabutton": ctaButton, - "data-datasource": dataSource, - "data-noresultstext": noResultsText, - "data-maxitems": maxItems, + "data-imagesize": imageSize, + "data-image": image, + "data-imagealt": imageAlt, + "data-heading": heading, + "data-body": body, + "data-readmorelink": readMoreLink, + "data-citation": citation, }; return
; From 5d327dd22a08aacec665907154d8670e0972e8e9 Mon Sep 17 00:00:00 2001 From: John Cox Date: Tue, 6 Jan 2026 14:20:24 -0700 Subject: [PATCH 12/18] import update esm component --- src/ranking-card/edit.js | 2 +- src/ranking-card/frontend.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ranking-card/edit.js b/src/ranking-card/edit.js index 96c6f7e8..6e1f4c4d 100644 --- a/src/ranking-card/edit.js +++ b/src/ranking-card/edit.js @@ -16,7 +16,7 @@ import { useBlockProps } from "@wordpress/block-editor"; /** * External dependencies */ -import { RankingCard } from "@asu/unity-react-core/dist/unityReactCore.es"; +import { RankingCard } from "@asu/unity-react-core/dist/esm/components/RankingCard.es"; /** * Internal dependencies diff --git a/src/ranking-card/frontend.js b/src/ranking-card/frontend.js index 1968a1ae..0c88f59c 100644 --- a/src/ranking-card/frontend.js +++ b/src/ranking-card/frontend.js @@ -1,6 +1,6 @@ const { render } = wp.element; -import { RankingCard } from "@asu/unity-react-core/dist/unityReactCore.es"; +import { RankingCard } from "@asu/unity-react-core/dist/esm/components/RankingCard.es"; // It is possible to load multiple ranking cards onto a page. // Load each DOM element on page using the Gutenberg-generated class for the Ranking Card block From c3a28d3de1230eeaaceb10395698547c51f9c6e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 21:25:03 +0000 Subject: [PATCH 13/18] Fix DOM error when changing image size by removing conditional PanelRow Co-authored-by: jkcox <8856538+jkcox@users.noreply.github.com> --- src/ranking-card/inspector.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/ranking-card/inspector.js b/src/ranking-card/inspector.js index 8f9acbec..a1e9a402 100644 --- a/src/ranking-card/inspector.js +++ b/src/ranking-card/inspector.js @@ -110,16 +110,15 @@ const Inspector = (props) => { onChange={(value) => setAttributes({ readMoreLink: value })} /> - {imageSize === "small" && ( - - setAttributes({ citation: value })} - /> - - )} + + setAttributes({ citation: value })} + disabled={imageSize !== "small"} + /> + From 13d6b9e6cd6040469a51cc4f2555421bf9201446 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 21:36:28 +0000 Subject: [PATCH 14/18] Add key prop to RankingCard to force remount on imageSize change Co-authored-by: jkcox <8856538+jkcox@users.noreply.github.com> --- src/ranking-card/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ranking-card/edit.js b/src/ranking-card/edit.js index 6e1f4c4d..d360c6b4 100644 --- a/src/ranking-card/edit.js +++ b/src/ranking-card/edit.js @@ -66,7 +66,7 @@ const Edit = (props) => { <>
- +
); From 5b7cdaf767738312112764d8faf45c9c60aaf103 Mon Sep 17 00:00:00 2001 From: John Cox Date: Tue, 6 Jan 2026 16:44:48 -0700 Subject: [PATCH 15/18] build v1.0 ranking card --- build/ranking-card/block.json | 62 +++++++++++++++++++++++++++ build/ranking-card/frontend.asset.php | 1 + build/ranking-card/frontend.js | 1 + build/ranking-card/index.asset.php | 1 + build/ranking-card/index.js | 1 + 5 files changed, 66 insertions(+) create mode 100644 build/ranking-card/block.json create mode 100644 build/ranking-card/frontend.asset.php create mode 100644 build/ranking-card/frontend.js create mode 100644 build/ranking-card/index.asset.php create mode 100644 build/ranking-card/index.js diff --git a/build/ranking-card/block.json b/build/ranking-card/block.json new file mode 100644 index 00000000..ca6de992 --- /dev/null +++ b/build/ranking-card/block.json @@ -0,0 +1,62 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "unityblocks/ranking-card", + "version": "1.0.0", + "title": "Ranking Card", + "category": "unityblocks", + "keywords": [ + "unityblocks", + "ranking", + "card" + ], + "icon": "index-card", + "description": "A UDS default design - ranking card with an interactive show more.", + "supports": { + "html": false, + "align": [ + "wide", + "full" + ] + }, + "textdomain": "unityblocks", + "viewScript": [ + "file:./frontend.js", + "unityblocks-ranking-card-view-script" + ], + "editorScript": "file:./index.js", + "attributes": { + "imageSize": { + "type": "string", + "enum": [ + "small", + "large" + ], + "default": "large" + }, + "image": { + "type": "string", + "default": "" + }, + "imageAlt": { + "type": "string", + "default": "" + }, + "heading": { + "type": "string", + "default": "" + }, + "body": { + "type": "string", + "default": "" + }, + "readMoreLink": { + "type": "string", + "default": "" + }, + "citation": { + "type": "string", + "default": "" + } + } +} \ No newline at end of file diff --git a/build/ranking-card/frontend.asset.php b/build/ranking-card/frontend.asset.php new file mode 100644 index 00000000..a8c35f5a --- /dev/null +++ b/build/ranking-card/frontend.asset.php @@ -0,0 +1 @@ + array('react', 'wp-element'), 'version' => '2a9df455ff2087287c12'); diff --git a/build/ranking-card/frontend.js b/build/ranking-card/frontend.js new file mode 100644 index 00000000..acc51577 --- /dev/null +++ b/build/ranking-card/frontend.js @@ -0,0 +1 @@ +(()=>{"use strict";var e={n:t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.element,n=window.React;var r=e.n(n);function o(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var a,i={exports:{}},l={};i.exports=function(){if(a)return l;a=1;var e=r(),t=Symbol.for("react.element"),n=Symbol.for("react.fragment"),o=Object.prototype.hasOwnProperty,i=e.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,s={key:!0,ref:!0,__self:!0,__source:!0};function u(e,n,r){var a,l={},u=null,c=null;for(a in void 0!==r&&(u=""+r),void 0!==n.key&&(u=""+n.key),void 0!==n.ref&&(c=n.ref),n)o.call(n,a)&&!s.hasOwnProperty(a)&&(l[a]=n[a]);if(e&&e.defaultProps)for(a in n=e.defaultProps)void 0===l[a]&&(l[a]=n[a]);return{$$typeof:t,type:e,key:u,ref:c,props:l,_owner:i.current}}return l.Fragment=n,l.jsx=u,l.jsxs=u,l}();var s=i.exports;const u=e=>(e||[]).join(" "),{entries:c,setPrototypeOf:d,isFrozen:f,getPrototypeOf:p,getOwnPropertyDescriptor:h}=Object;let{freeze:m,seal:g,create:y}=Object,{apply:b,construct:v}=typeof Reflect<"u"&&Reflect;m||(m=function(e){return e}),g||(g=function(e){return e}),b||(b=function(e,t,n){return e.apply(t,n)}),v||(v=function(e,t){return new e(...t)});const S=P(Array.prototype.forEach),x=P(Array.prototype.lastIndexOf),k=P(Array.prototype.pop),w=P(Array.prototype.push),E=P(Array.prototype.splice),T=P(String.prototype.toLowerCase),C=P(String.prototype.toString),_=P(String.prototype.match),R=P(String.prototype.replace),N=P(String.prototype.indexOf),F=P(String.prototype.trim),A=P(Object.prototype.hasOwnProperty),I=P(RegExp.prototype.test),M=(O=TypeError,function(){for(var e=arguments.length,t=new Array(e),n=0;n1?n-1:0),o=1;o2&&void 0!==arguments[2]?arguments[2]:T;d&&d(e,null);let r=t.length;for(;r--;){let o=t[r];if("string"==typeof o){const e=n(o);e!==o&&(f(t)||(t[r]=e),o=e)}e[o]=!0}return e}function D(e){for(let t=0;t/gm),Q=g(/\$\{[\w\W]*/gm),ee=g(/^data-[\-\w.\u00B7-\uFFFF]+$/),te=g(/^aria-[\-\w]+$/),ne=g(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),re=g(/^(?:\w+script|data):/i),oe=g(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),ae=g(/^html$/i),ie=g(/^[a-z][.\w]*(-[.\w]+)+$/i);var le=Object.freeze({__proto__:null,ARIA_ATTR:te,ATTR_WHITESPACE:oe,CUSTOM_ELEMENT:ie,DATA_ATTR:ee,DOCTYPE_NAME:ae,ERB_EXPR:J,IS_ALLOWED_URI:ne,IS_SCRIPT_OR_DATA:re,MUSTACHE_EXPR:K,TMPLIT_EXPR:Q});var se=function e(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:typeof window>"u"?null:window;const n=t=>e(t);if(n.version="3.2.5",n.removed=[],!t||!t.document||9!==t.document.nodeType||!t.Element)return n.isSupported=!1,n;let{document:r}=t;const o=r,a=o.currentScript,{DocumentFragment:i,HTMLTemplateElement:l,Node:s,Element:u,NodeFilter:d,NamedNodeMap:f=t.NamedNodeMap||t.MozNamedAttrMap,HTMLFormElement:p,DOMParser:h,trustedTypes:g}=t,b=u.prototype,v=j(b,"cloneNode"),O=j(b,"remove"),P=j(b,"nextSibling"),D=j(b,"childNodes"),K=j(b,"parentNode");if("function"==typeof l){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let J,Q="";const{implementation:ee,createNodeIterator:te,createDocumentFragment:re,getElementsByTagName:oe}=r,{importNode:ie}=o;let se={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]};n.isSupported="function"==typeof c&&"function"==typeof K&&ee&&void 0!==ee.createHTMLDocument;const{MUSTACHE_EXPR:ue,ERB_EXPR:ce,TMPLIT_EXPR:de,DATA_ATTR:fe,ARIA_ATTR:pe,IS_SCRIPT_OR_DATA:he,ATTR_WHITESPACE:me,CUSTOM_ELEMENT:ge}=le;let{IS_ALLOWED_URI:ye}=le,be=null;const ve=L({},[...B,...U,...H,...V,...W]);let Se=null;const xe=L({},[...G,...Y,...X,...Z]);let ke=Object.seal(y(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),we=null,Ee=null,Te=!0,Ce=!0,_e=!1,Re=!0,Ne=!1,Fe=!0,Ae=!1,Ie=!1,Me=!1,Oe=!1,Pe=!1,Le=!1,De=!0,ze=!1,je=!0,Be=!1,Ue={},He=null;const $e=L({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let Ve=null;const qe=L({},["audio","video","img","source","image","track"]);let We=null;const Ge=L({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Ye="http://www.w3.org/1998/Math/MathML",Xe="http://www.w3.org/2000/svg",Ze="http://www.w3.org/1999/xhtml";let Ke=Ze,Je=!1,Qe=null;const et=L({},[Ye,Xe,Ze],C);let tt=L({},["mi","mo","mn","ms","mtext"]),nt=L({},["annotation-xml"]);const rt=L({},["title","style","font","a","script"]);let ot=null;const at=["application/xhtml+xml","text/html"];let it=null,lt=null;const st=r.createElement("form"),ut=function(e){return e instanceof RegExp||e instanceof Function},ct=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!lt||lt!==e){if((!e||"object"!=typeof e)&&(e={}),e=z(e),ot=-1===at.indexOf(e.PARSER_MEDIA_TYPE)?"text/html":e.PARSER_MEDIA_TYPE,it="application/xhtml+xml"===ot?C:T,be=A(e,"ALLOWED_TAGS")?L({},e.ALLOWED_TAGS,it):ve,Se=A(e,"ALLOWED_ATTR")?L({},e.ALLOWED_ATTR,it):xe,Qe=A(e,"ALLOWED_NAMESPACES")?L({},e.ALLOWED_NAMESPACES,C):et,We=A(e,"ADD_URI_SAFE_ATTR")?L(z(Ge),e.ADD_URI_SAFE_ATTR,it):Ge,Ve=A(e,"ADD_DATA_URI_TAGS")?L(z(qe),e.ADD_DATA_URI_TAGS,it):qe,He=A(e,"FORBID_CONTENTS")?L({},e.FORBID_CONTENTS,it):$e,we=A(e,"FORBID_TAGS")?L({},e.FORBID_TAGS,it):{},Ee=A(e,"FORBID_ATTR")?L({},e.FORBID_ATTR,it):{},Ue=!!A(e,"USE_PROFILES")&&e.USE_PROFILES,Te=!1!==e.ALLOW_ARIA_ATTR,Ce=!1!==e.ALLOW_DATA_ATTR,_e=e.ALLOW_UNKNOWN_PROTOCOLS||!1,Re=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,Ne=e.SAFE_FOR_TEMPLATES||!1,Fe=!1!==e.SAFE_FOR_XML,Ae=e.WHOLE_DOCUMENT||!1,Oe=e.RETURN_DOM||!1,Pe=e.RETURN_DOM_FRAGMENT||!1,Le=e.RETURN_TRUSTED_TYPE||!1,Me=e.FORCE_BODY||!1,De=!1!==e.SANITIZE_DOM,ze=e.SANITIZE_NAMED_PROPS||!1,je=!1!==e.KEEP_CONTENT,Be=e.IN_PLACE||!1,ye=e.ALLOWED_URI_REGEXP||ne,Ke=e.NAMESPACE||Ze,tt=e.MATHML_TEXT_INTEGRATION_POINTS||tt,nt=e.HTML_INTEGRATION_POINTS||nt,ke=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&&ut(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(ke.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&&ut(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(ke.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(ke.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Ne&&(Ce=!1),Pe&&(Oe=!0),Ue&&(be=L({},W),Se=[],!0===Ue.html&&(L(be,B),L(Se,G)),!0===Ue.svg&&(L(be,U),L(Se,Y),L(Se,Z)),!0===Ue.svgFilters&&(L(be,H),L(Se,Y),L(Se,Z)),!0===Ue.mathMl&&(L(be,V),L(Se,X),L(Se,Z))),e.ADD_TAGS&&(be===ve&&(be=z(be)),L(be,e.ADD_TAGS,it)),e.ADD_ATTR&&(Se===xe&&(Se=z(Se)),L(Se,e.ADD_ATTR,it)),e.ADD_URI_SAFE_ATTR&&L(We,e.ADD_URI_SAFE_ATTR,it),e.FORBID_CONTENTS&&(He===$e&&(He=z(He)),L(He,e.FORBID_CONTENTS,it)),je&&(be["#text"]=!0),Ae&&L(be,["html","head","body"]),be.table&&(L(be,["tbody"]),delete we.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw M('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw M('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');J=e.TRUSTED_TYPES_POLICY,Q=J.createHTML("")}else void 0===J&&(J=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const r="data-tt-policy-suffix";t&&t.hasAttribute(r)&&(n=t.getAttribute(r));const o="dompurify"+(n?"#"+n:"");try{return e.createPolicy(o,{createHTML:e=>e,createScriptURL:e=>e})}catch{return console.warn("TrustedTypes policy "+o+" could not be created."),null}}(g,a)),null!==J&&"string"==typeof Q&&(Q=J.createHTML(""));m&&m(e),lt=e}},dt=L({},[...U,...H,...$]),ft=L({},[...V,...q]),pt=function(e){w(n.removed,{element:e});try{K(e).removeChild(e)}catch{O(e)}},ht=function(e,t){try{w(n.removed,{attribute:t.getAttributeNode(e),from:t})}catch{w(n.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e)if(Oe||Pe)try{pt(t)}catch{}else try{t.setAttribute(e,"")}catch{}},mt=function(e){let t=null,n=null;if(Me)e=""+e;else{const t=_(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===ot&&Ke===Ze&&(e=''+e+"");const o=J?J.createHTML(e):e;if(Ke===Ze)try{t=(new h).parseFromString(o,ot)}catch{}if(!t||!t.documentElement){t=ee.createDocument(Ke,"template",null);try{t.documentElement.innerHTML=Je?Q:o}catch{}}const a=t.body||t.documentElement;return e&&n&&a.insertBefore(r.createTextNode(n),a.childNodes[0]||null),Ke===Ze?oe.call(t,Ae?"html":"body")[0]:Ae?t.documentElement:a},gt=function(e){return te.call(e.ownerDocument||e,e,d.SHOW_ELEMENT|d.SHOW_COMMENT|d.SHOW_TEXT|d.SHOW_PROCESSING_INSTRUCTION|d.SHOW_CDATA_SECTION,null)},yt=function(e){return e instanceof p&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof f)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},bt=function(e){return"function"==typeof s&&e instanceof s};function vt(e,t,r){S(e,(e=>{e.call(n,t,r,lt)}))}const St=function(e){let t=null;if(vt(se.beforeSanitizeElements,e,null),yt(e))return pt(e),!0;const r=it(e.nodeName);if(vt(se.uponSanitizeElement,e,{tagName:r,allowedTags:be}),e.hasChildNodes()&&!bt(e.firstElementChild)&&I(/<[/\w!]/g,e.innerHTML)&&I(/<[/\w!]/g,e.textContent)||7===e.nodeType||Fe&&8===e.nodeType&&I(/<[/\w]/g,e.data))return pt(e),!0;if(!be[r]||we[r]){if(!we[r]&&kt(r)&&(ke.tagNameCheck instanceof RegExp&&I(ke.tagNameCheck,r)||ke.tagNameCheck instanceof Function&&ke.tagNameCheck(r)))return!1;if(je&&!He[r]){const t=K(e)||e.parentNode,n=D(e)||e.childNodes;if(n&&t)for(let r=n.length-1;r>=0;--r){const o=v(n[r],!0);o.__removalCount=(e.__removalCount||0)+1,t.insertBefore(o,P(e))}}return pt(e),!0}return e instanceof u&&!function(e){let t=K(e);(!t||!t.tagName)&&(t={namespaceURI:Ke,tagName:"template"});const n=T(e.tagName),r=T(t.tagName);return!!Qe[e.namespaceURI]&&(e.namespaceURI===Xe?t.namespaceURI===Ze?"svg"===n:t.namespaceURI===Ye?"svg"===n&&("annotation-xml"===r||tt[r]):!!dt[n]:e.namespaceURI===Ye?t.namespaceURI===Ze?"math"===n:t.namespaceURI===Xe?"math"===n&&nt[r]:!!ft[n]:e.namespaceURI===Ze?!(t.namespaceURI===Xe&&!nt[r]||t.namespaceURI===Ye&&!tt[r])&&!ft[n]&&(rt[n]||!dt[n]):!("application/xhtml+xml"!==ot||!Qe[e.namespaceURI]))}(e)||("noscript"===r||"noembed"===r||"noframes"===r)&&I(/<\/no(script|embed|frames)/i,e.innerHTML)?(pt(e),!0):(Ne&&3===e.nodeType&&(t=e.textContent,S([ue,ce,de],(e=>{t=R(t,e," ")})),e.textContent!==t&&(w(n.removed,{element:e.cloneNode()}),e.textContent=t)),vt(se.afterSanitizeElements,e,null),!1)},xt=function(e,t,n){if(De&&("id"===t||"name"===t)&&(n in r||n in st))return!1;if((!Ce||Ee[t]||!I(fe,t))&&(!Te||!I(pe,t)))if(!Se[t]||Ee[t]){if(!(kt(e)&&(ke.tagNameCheck instanceof RegExp&&I(ke.tagNameCheck,e)||ke.tagNameCheck instanceof Function&&ke.tagNameCheck(e))&&(ke.attributeNameCheck instanceof RegExp&&I(ke.attributeNameCheck,t)||ke.attributeNameCheck instanceof Function&&ke.attributeNameCheck(t))||"is"===t&&ke.allowCustomizedBuiltInElements&&(ke.tagNameCheck instanceof RegExp&&I(ke.tagNameCheck,n)||ke.tagNameCheck instanceof Function&&ke.tagNameCheck(n))))return!1}else if(!We[t]&&!I(ye,R(n,me,""))&&("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==N(n,"data:")||!Ve[e])&&(!_e||I(he,R(n,me,"")))&&n)return!1;return!0},kt=function(e){return"annotation-xml"!==e&&_(e,ge)},wt=function(e){vt(se.beforeSanitizeAttributes,e,null);const{attributes:t}=e;if(!t||yt(e))return;const r={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Se,forceKeepAttr:void 0};let o=t.length;for(;o--;){const a=t[o],{name:i,namespaceURI:l,value:s}=a,u=it(i);let c="value"===i?s:F(s);if(r.attrName=u,r.attrValue=c,r.keepAttr=!0,r.forceKeepAttr=void 0,vt(se.uponSanitizeAttribute,e,r),c=r.attrValue,ze&&("id"===u||"name"===u)&&(ht(i,e),c="user-content-"+c),Fe&&I(/((--!?|])>)|<\/(style|title)/i,c)){ht(i,e);continue}if(r.forceKeepAttr||(ht(i,e),!r.keepAttr))continue;if(!Re&&I(/\/>/i,c)){ht(i,e);continue}Ne&&S([ue,ce,de],(e=>{c=R(c,e," ")}));const d=it(e.nodeName);if(xt(d,u,c)){if(J&&"object"==typeof g&&"function"==typeof g.getAttributeType&&!l)switch(g.getAttributeType(d,u)){case"TrustedHTML":c=J.createHTML(c);break;case"TrustedScriptURL":c=J.createScriptURL(c)}try{l?e.setAttributeNS(l,i,c):e.setAttribute(i,c),yt(e)?pt(e):k(n.removed)}catch{}}}vt(se.afterSanitizeAttributes,e,null)},Et=function e(t){let n=null;const r=gt(t);for(vt(se.beforeSanitizeShadowDOM,t,null);n=r.nextNode();)vt(se.uponSanitizeShadowNode,n,null),St(n),wt(n),n.content instanceof i&&e(n.content);vt(se.afterSanitizeShadowDOM,t,null)};return n.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=null,a=null,l=null,u=null;if(Je=!e,Je&&(e="\x3c!--\x3e"),"string"!=typeof e&&!bt(e)){if("function"!=typeof e.toString)throw M("toString is not a function");if("string"!=typeof(e=e.toString()))throw M("dirty is not a string, aborting")}if(!n.isSupported)return e;if(Ie||ct(t),n.removed=[],"string"==typeof e&&(Be=!1),Be){if(e.nodeName){const t=it(e.nodeName);if(!be[t]||we[t])throw M("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof s)r=mt("\x3c!----\x3e"),a=r.ownerDocument.importNode(e,!0),1===a.nodeType&&"BODY"===a.nodeName||"HTML"===a.nodeName?r=a:r.appendChild(a);else{if(!Oe&&!Ne&&!Ae&&-1===e.indexOf("<"))return J&&Le?J.createHTML(e):e;if(r=mt(e),!r)return Oe?null:Le?Q:""}r&&Me&&pt(r.firstChild);const c=gt(Be?e:r);for(;l=c.nextNode();)St(l),wt(l),l.content instanceof i&&Et(l.content);if(Be)return e;if(Oe){if(Pe)for(u=re.call(r.ownerDocument);r.firstChild;)u.appendChild(r.firstChild);else u=r;return(Se.shadowroot||Se.shadowrootmode)&&(u=ie.call(o,u,!0)),u}let d=Ae?r.outerHTML:r.innerHTML;return Ae&&be["!doctype"]&&r.ownerDocument&&r.ownerDocument.doctype&&r.ownerDocument.doctype.name&&I(ae,r.ownerDocument.doctype.name)&&(d="\n"+d),Ne&&S([ue,ce,de],(e=>{d=R(d,e," ")})),J&&Le?J.createHTML(d):d},n.setConfig=function(){ct(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),Ie=!0},n.clearConfig=function(){lt=null,Ie=!1},n.isValidAttribute=function(e,t,n){lt||ct({});const r=it(e),o=it(t);return xt(r,o,n)},n.addHook=function(e,t){"function"==typeof t&&w(se[e],t)},n.removeHook=function(e,t){if(void 0!==t){const n=x(se[e],t);return-1===n?void 0:E(se[e],n,1)[0]}return k(se[e])},n.removeHooks=function(e){se[e]=[]},n.removeAllHooks=function(){se={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},n}();let ue=null;const ce=e=>({__html:(ue||(ue=typeof window<"u"?se(window):se),ue).sanitize(e)});var de,fe,pe,he,me={exports:{}};me.exports=function(){if(he)return pe;he=1;var e=fe?de:(fe=1,de="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED");function t(){}function n(){}return n.resetWarningCache=t,pe=function(){function r(t,n,r,o,a,i){if(i!==e){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function o(){return r}r.isRequired=r;var a={array:r,bigint:r,bool:r,func:r,number:r,object:r,string:r,symbol:r,any:r,arrayOf:o,element:r,elementType:r,instanceOf:o,node:r,objectOf:o,oneOf:o,oneOfType:o,shape:o,exact:o,checkPropTypes:n,resetWarningCache:t};return a.PropTypes=a,a}}()();const ge=o(me.exports);ge.shape({event:ge.string,action:ge.string,name:ge.string,region:ge.string,section:ge.string,component:ge.string,type:ge.string,text:ge.string});const ye=({children:e})=>s.jsx(s.Fragment,{children:e});ye.propTypes={children:ge.oneOfType([ge.arrayOf(ge.node),ge.node,ge.string])};var be={exports:{}};!function(e){!function(){var t={}.hasOwnProperty;function n(){for(var e="",t=0;t-1||void 0;return{isBootstrap:e,isReact:!e||void 0}}ke=function(){if(Se)return Ee;Se=1;var e=r();function t(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n