latest changes

This commit is contained in:
2025-06-17 08:01:10 -07:00
parent edcad561a5
commit 4c48d7ccbd
50 changed files with 1735 additions and 195 deletions

View File

@@ -358,11 +358,11 @@
"sub_fields": [
{
"key": "field_6824e06c13d95",
"label": "Block",
"name": "block",
"label": "Blocks",
"name": "blocks",
"aria-label": "",
"type": "repeater",
"instructions": "",
"instructions": "Choose either a page\/product OR a product category. If you choose both, the page\/product on the left is the one that is chosen",
"required": 0,
"conditional_logic": 0,
"wrapper": {
@@ -371,16 +371,16 @@
"id": ""
},
"layout": "table",
"min": 0,
"max": 0,
"min": 1,
"max": 4,
"collapsed": "",
"button_label": "Add Row",
"button_label": "Add Block",
"rows_per_page": 20,
"sub_fields": [
{
"key": "field_6824e07913d96",
"label": "Target",
"name": "target",
"label": "Page\/Product",
"name": "page_product",
"aria-label": "",
"type": "post_object",
"instructions": "",
@@ -391,7 +391,10 @@
"class": "",
"id": ""
},
"post_type": "",
"post_type": [
"page",
"product"
],
"post_status": "",
"taxonomy": "",
"return_format": "object",
@@ -402,6 +405,143 @@
"ui": 1,
"bidirectional_target": [],
"parent_repeater": "field_6824e06c13d95"
},
{
"key": "field_682d15cc48cf3",
"label": "Product Category",
"name": "product_category",
"aria-label": "",
"type": "taxonomy",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"taxonomy": "product_cat",
"add_term": 0,
"save_terms": 0,
"load_terms": 0,
"return_format": "id",
"field_type": "select",
"allow_null": 1,
"allow_in_bindings": 0,
"bidirectional": 0,
"multiple": 0,
"bidirectional_target": [],
"parent_repeater": "field_6824e06c13d95"
}
]
}
],
"min": "",
"max": ""
},
"layout_682d09e6b11a2": {
"key": "layout_682d09e6b11a2",
"name": "vertical_callouts",
"label": "Vertical Callouts",
"display": "block",
"sub_fields": [
{
"key": "field_683777b154db8",
"label": "Headline",
"name": "headline",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"allow_in_bindings": 0,
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_682d09e6b11a7",
"label": "Blocks",
"name": "blocks",
"aria-label": "",
"type": "repeater",
"instructions": "Choose either a page\/product OR a product category. If you choose both, the page\/product on the left is the one that is chosen",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "table",
"min": 1,
"max": 4,
"collapsed": "",
"button_label": "Add Block",
"rows_per_page": 20,
"sub_fields": [
{
"key": "field_682d167848cf4",
"label": "Page\/Product",
"name": "page_product",
"aria-label": "",
"type": "post_object",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"post_type": [
"page",
"product"
],
"post_status": "",
"taxonomy": "",
"return_format": "object",
"multiple": 0,
"allow_null": 0,
"allow_in_bindings": 0,
"bidirectional": 0,
"ui": 1,
"bidirectional_target": [],
"parent_repeater": "field_682d09e6b11a7"
},
{
"key": "field_682d09e6b11a8",
"label": "Category",
"name": "category",
"aria-label": "",
"type": "taxonomy",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"taxonomy": "product_cat",
"add_term": 0,
"save_terms": 0,
"load_terms": 0,
"return_format": "object",
"field_type": "select",
"allow_null": 1,
"allow_in_bindings": 0,
"bidirectional": 0,
"multiple": 0,
"bidirectional_target": [],
"parent_repeater": "field_682d09e6b11a7"
}
]
}
@@ -550,6 +690,184 @@
],
"min": "",
"max": ""
},
"layout_6837a5ddc8328": {
"key": "layout_6837a5ddc8328",
"name": "callout_text",
"label": "Callout Text",
"display": "block",
"sub_fields": [
{
"key": "field_6837a5e3c832e",
"label": "Headline 1",
"name": "headline_1",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"allow_in_bindings": 0,
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_6837a5ebc832f",
"label": "Headline 2",
"name": "headline_2",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"allow_in_bindings": 0,
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_6837a5f2c8330",
"label": "Descriptive Text",
"name": "descriptive_text",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"allow_in_bindings": 0,
"placeholder": "",
"prepend": "",
"append": ""
}
],
"min": "",
"max": ""
},
"layout_683a43e4c858e": {
"key": "layout_683a43e4c858e",
"name": "accordian_text",
"label": "Accordian Text",
"display": "block",
"sub_fields": [
{
"key": "field_683a43f0c8590",
"label": "Text Blocks",
"name": "text_blocks",
"aria-label": "",
"type": "repeater",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "table",
"min": 0,
"max": 0,
"collapsed": "",
"button_label": "Add Row",
"rows_per_page": 20,
"sub_fields": [
{
"key": "field_683a4417c8591",
"label": "Headline",
"name": "headline",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"allow_in_bindings": 0,
"placeholder": "",
"prepend": "",
"append": "",
"parent_repeater": "field_683a43f0c8590"
},
{
"key": "field_683a4420c8592",
"label": "Content",
"name": "content",
"aria-label": "",
"type": "textarea",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"allow_in_bindings": 0,
"rows": "",
"placeholder": "",
"new_lines": "br",
"parent_repeater": "field_683a43f0c8590"
}
]
}
],
"min": "",
"max": ""
},
"layout_683cb88438a58": {
"key": "layout_683cb88438a58",
"name": "4_column_business_blocks",
"label": "4 Column Business Blocks",
"display": "block",
"sub_fields": [
{
"key": "field_683cb89138a5a",
"label": "You don't edit this here",
"name": "",
"aria-label": "",
"type": "message",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"message": "This could be shown on many pages, so click <a href=\"\/wp-admin\/admin.php?page=site_options\" target=\"_blank\">here<\/a> to manage this content.",
"new_lines": "wpautop",
"esc_html": 0
}
],
"min": "",
"max": ""
}
},
"min": "",
@@ -577,5 +895,5 @@
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1747418238
"modified": 1748810024
}

View File

@@ -0,0 +1,93 @@
{
"key": "group_682cf199a3483",
"title": "Shop Category Fields",
"fields": [
{
"key": "field_682cf19966afb",
"label": "Top Image",
"name": "top_image",
"aria-label": "",
"type": "image",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "url",
"library": "all",
"min_width": "",
"min_height": "",
"min_size": "",
"max_width": "",
"max_height": "",
"max_size": "",
"mime_types": "",
"allow_in_bindings": 0,
"preview_size": "medium"
},
{
"key": "field_682cf27e6b5c7",
"label": "Intro Headline",
"name": "intro_headline",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"allow_in_bindings": 0,
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_682cf2906b5c8",
"label": "Intro Content",
"name": "intro_content",
"aria-label": "",
"type": "wysiwyg",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 0,
"tabs": "all",
"toolbar": "full",
"media_upload": 1,
"delay": 0
}
],
"location": [
[
{
"param": "taxonomy",
"operator": "==",
"value": "product_cat"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1747776169
}

3
assets/arrow-right.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-right" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8"/>
</svg>

After

Width:  |  Height:  |  Size: 311 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-megaphone-fill" viewBox="0 0 16 16">
<path d="M13 2.5a1.5 1.5 0 0 1 3 0v11a1.5 1.5 0 0 1-3 0zm-1 .724c-2.067.95-4.539 1.481-7 1.656v6.237a25 25 0 0 1 1.088.085c2.053.204 4.038.668 5.912 1.56zm-8 7.841V4.934c-.68.027-1.399.043-2.008.053A2.02 2.02 0 0 0 0 7v2c0 1.106.896 1.996 1.994 2.009l.496.008a64 64 0 0 1 1.51.048m1.39 1.081q.428.032.85.078l.253 1.69a1 1 0 0 1-.983 1.187h-.548a1 1 0 0 1-.916-.599l-1.314-2.48a66 66 0 0 1 1.692.064q.491.026.966.06"/>
</svg>

After

Width:  |  Height:  |  Size: 559 B

16
composer.lock generated
View File

@@ -57,28 +57,28 @@
},
{
"name": "open-function-computers-llc/rad-theme-engine",
"version": "v1.0.34",
"version": "v1.0.38",
"source": {
"type": "git",
"url": "https://github.com/open-function-computers-llc/rad-theme-engine.git",
"reference": "c0b814bdd0ee1f093a051ed2e78efdf8c1f4f94e"
"reference": "90a351370bbd5a92a2c68c226a2c0cf3f97ce8f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/open-function-computers-llc/rad-theme-engine/zipball/c0b814bdd0ee1f093a051ed2e78efdf8c1f4f94e",
"reference": "c0b814bdd0ee1f093a051ed2e78efdf8c1f4f94e",
"url": "https://api.github.com/repos/open-function-computers-llc/rad-theme-engine/zipball/90a351370bbd5a92a2c68c226a2c0cf3f97ce8f4",
"reference": "90a351370bbd5a92a2c68c226a2c0cf3f97ce8f4",
"shasum": ""
},
"require": {
"jjgrainger/posttypes": "^2.1",
"php": ">=7.4",
"php": ">=8.1",
"salesforce/handlebars-php": "^2.3"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"bin": [
"bin/getIcon"
"bin/rad"
],
"type": "library",
"autoload": {
@@ -98,9 +98,9 @@
],
"support": {
"issues": "https://github.com/open-function-computers-llc/rad-theme-engine/issues",
"source": "https://github.com/open-function-computers-llc/rad-theme-engine/tree/v1.0.34"
"source": "https://github.com/open-function-computers-llc/rad-theme-engine/tree/v1.0.38"
},
"time": "2025-05-20T21:03:45+00:00"
"time": "2025-06-13T18:23:21+00:00"
},
{
"name": "salesforce/handlebars-php",

View File

@@ -1,5 +1,7 @@
<?php
use Helpers\McCansHelpers;
return [
"debug" => true,
"flex-file-prefix" => "flex",
@@ -73,6 +75,11 @@ return [
"testimonial" => \Helpers\McCansHelpers::testimonial(),
"locationTile" => \Helpers\McCansHelpers::locationTile(),
"productTile" => \Helpers\McCansHelpers::productTile(),
"renderBlock" => \Helpers\McCansHelpers::renderBlock(),
"renderCallout" => \Helpers\McCansHelpers::renderCallout(),
"productBox" => \Helpers\McCansHelpers::searchResultBox('product'),
"pageBox" => \Helpers\McCansHelpers::searchResultBox('page'),
"usd" => \Helpers\McCansHelpers::usd(),
],
],
@@ -86,6 +93,7 @@ return [
"post-thumbnails",
"menus",
"woocommerce",
"excerpts",
],
@@ -100,34 +108,107 @@ return [
"gutenberg",
"patterns",
"emojis",
"revisions",
"meta-generator",
"woocommerce.breadcrumb",
"woocommerce.sidebar",
"woocommerce.result_count",
"woocommerce.page_title",
"woocommerce.archive_description",
"woocommerce.reviews",
],
"hooks" => [
"woocommerce_before_main_content" => function () {
"actions" => [
"woocommerce_before_single_product_summary" => [function () {
echo site()->render('woocommerce-before-content', [
"additionalTopLevelClass" => "single-product-top",
]);
}, 0],
"woocommerce_single_product_summary" => function () {
echo site()->render('woocommerce-after-content');
},
[
"hook" => "woocommerce_after_single_product_summary",
"priority" => 0,
"callback" => function () {
echo site()->render('woocommerce-before-content');
},
],
"woocommerce_after_single_product" => function () {
echo site()->render('woocommerce-after-content');
},
[
"hook" => "woocommerce_after_single_product_summary",
"priority" => 18,
"callback" => function () {
echo site()->render('woocommerce-after-content');
}
],
[
"hook" => "woocommerce_after_single_product_summary",
"priority" => 19,
"callback" => function () {
echo site()->render('flex_callout_text', [
"headline_1" => "Shop",
"headline_2" => "Related Products",
]);
echo site()->render('woocommerce-before-content', [
"additionalTopLevelClass" => "related-wrapper"
]);
}
],
"woocommerce_before_main_content" => [function () {
// skip this for individual products
if (is_product()) {
return;
}
// shop page
if (is_shop()) {
echo site()->render("shop-top", site()->getPost(wc_get_page_id('shop'), ["headline", "intro_text", 'thumbnail']));
echo site()->render("shop-top", site()->getPost(wc_get_page_id('shop'), [
"headline",
"intro_text",
"thumbnail",
]));
echo site()->render('woocommerce-before-content');
return;
}
if (is_product_category()) {
echo site()->render("shop-category-slice", site()->getQueriedObject(null, [
"title",
"description",
"thumbnail",
"url",
"acf.intro_headline",
"acf.intro_content",
]));
echo site()->render('woocommerce-before-content');
return;
}
echo site()->render('woocommerce-before-content');
},
"woocommerce_after_main_content" => function () {
echo site()->render('woocommerce-after-content');
},
"init" => function() {
remove_all_actions( 'woocommerce_before_shop_loop_item' );
remove_all_actions( 'woocommerce_before_shop_loop_item_title' );
remove_all_actions( 'woocommerce_shop_loop_item_title' );
remove_all_actions( 'woocommerce_after_shop_loop_item_title' );
remove_all_actions( 'woocommerce_after_shop_loop_item' );
add_action( 'woocommerce_shop_loop_item_title', function() {
}, 5],
[
"hook" => "woocommerce_after_main_content",
"callback" => function () {
// skip this for individual products
if (is_product()) {
return;
}
echo site()->render('woocommerce-after-content');
},
"priority" => 50,
],
"init" => function () {
remove_all_actions('woocommerce_before_shop_loop_item');
remove_all_actions('woocommerce_before_shop_loop_item_title');
remove_all_actions('woocommerce_shop_loop_item_title');
remove_all_actions('woocommerce_after_shop_loop_item_title');
remove_all_actions('woocommerce_after_shop_loop_item');
add_action('woocommerce_shop_loop_item_title', function () {
global $product;
$data = site()->getPost($product->id, [
@@ -141,13 +222,7 @@ return [
"woocommerce.sku",
]);
if ($data["price"] && $data["msrp"]) {
$difference = $data["msrp"] - $data["price"];
if ($difference > 0) {
$data["savings"] = round(($difference / $data["msrp"]) * 100);
}
}
$data["savings"] = McCansHelpers::calcDifference($data["price"], $data["msrp"]);
echo site()->render("product-tile", $data);
});

260
dist/app.css vendored
View File

@@ -12214,22 +12214,48 @@ img {
max-width: 100%;
}
.montserrat, div.product-tile a.added_to_cart, footer.sub-footer, footer.site-footer .logo-col, footer.site-footer h2, footer.site-footer .h2, .cta-button a, .single-location .form-wrapper p:has(input[type=submit]) a, .cta-button input, .single-location .form-wrapper p:has(input[type=submit]) input, .headline-xl, section.intro-slide h1, section.intro-slide .h1, .background-image-cta h1, .background-image-cta .h1, .background-image-cta h2, .background-image-cta .h2, .background-image-cta h3, .background-image-cta .h3, .headline-lg, .single-location h1, .single-location .h1, .post-type-archive-location .intro h1, .post-type-archive-location .intro .h1, .testimonials-section h1, .testimonials-section .h1, .testimonials-section h2, .testimonials-section .h2, .testimonials-section h3, .testimonials-section .h3, .plain-text h1, .plain-text .h1, .plain-text h2, .plain-text .h2, .plain-text h3, .plain-text .h3 {
.montserrat, .single-product .wc-tabs li, .category-slice h2, .category-slice .h2, .page-tile h2, .page-tile .h2, div.product-tile a.learn-more, div.page-tile a.learn-more, div.product-tile a.added_to_cart, div.page-tile a.added_to_cart, section.intro-header h1, section.intro-header .h1, section.accordian-content a, footer.sub-footer, footer.site-footer .logo-col, footer.site-footer h2, footer.site-footer .h2, .cta-button a, .single-location .form-wrapper p:has(input[type=submit]) a, .cta-button input, .single-location .form-wrapper p:has(input[type=submit]) input, .headline-xl, section.intro-slide h1, section.intro-slide .h1, .background-image-cta h1, .background-image-cta .h1, .background-image-cta h2, .background-image-cta .h2, .background-image-cta h3, .background-image-cta .h3, .headline-lg, .single-location h1, .single-location .h1, .post-type-archive-location .intro h1, .post-type-archive-location .intro .h1, .category-slice-top h1, .category-slice-top .h1, .callout-text h2, .callout-text .h2, section.vertical-callouts h2, section.vertical-callouts .h2, .testimonials-section h1, .testimonials-section .h1, .testimonials-section h2, .testimonials-section .h2, .testimonials-section h3, .testimonials-section .h3, .plain-text h1, .plain-text .h1, .plain-text h2, .plain-text .h2, .plain-text h3, .plain-text .h3, .headline-md, .callout-page h3, .callout-page .h3, .callout-category h3, .callout-category .h3, section.intro-header h1, .headline-sm {
font-family: "Montserrat", sans-serif;
}
.change-black-to-red, div.product-tile a.added_to_cart::after, .testimonials-section .testimonials .slick-arrow img {
.change-black-to-red, div.product-tile a.added_to_cart::after, div.page-tile a.added_to_cart::after, .testimonials-section .testimonials .slick-arrow img {
filter: invert(10%) sepia(79%) saturate(7388%) hue-rotate(2deg) brightness(86%) contrast(111%);
}
.headline-lg, .single-location h1, .single-location .h1, .post-type-archive-location .intro h1, .post-type-archive-location .intro .h1, .testimonials-section h1, .testimonials-section .h1, .testimonials-section h2, .testimonials-section .h2, .testimonials-section h3, .testimonials-section .h3, .plain-text h1, .plain-text .h1, .plain-text h2, .plain-text .h2, .plain-text h3, .plain-text .h3 {
.headline-sm {
font-weight: 600;
font-size: 40px;
line-height: 40px;
letter-spacing: -1px;
}
@media (max-width: 767.98px) {
.headline-sm {
font-size: 30px;
line-height: 30px;
}
}
.headline-md, .callout-page h3, .callout-page .h3, .callout-category h3, .callout-category .h3, section.intro-header h1, section.intro-header .h1 {
font-weight: 600;
font-size: 54px;
line-height: 54px;
letter-spacing: -1px;
}
@media (max-width: 767.98px) {
.headline-md, .callout-page h3, .callout-page .h3, .callout-category h3, .callout-category .h3, section.intro-header h1, section.intro-header .h1 {
font-size: 40px;
line-height: 40px;
}
}
.headline-lg, .single-location h1, .single-location .h1, .post-type-archive-location .intro h1, .post-type-archive-location .intro .h1, .category-slice-top h1, .category-slice-top .h1, .callout-text h2, .callout-text .h2, section.vertical-callouts h2, section.vertical-callouts .h2, .testimonials-section h1, .testimonials-section .h1, .testimonials-section h2, .testimonials-section .h2, .testimonials-section h3, .testimonials-section .h3, .plain-text h1, .plain-text .h1, .plain-text h2, .plain-text .h2, .plain-text h3, .plain-text .h3 {
font-weight: 600;
font-size: 60px;
line-height: 60px;
letter-spacing: -3%;
}
@media (max-width: 767.98px) {
.headline-lg, .single-location h1, .single-location .h1, .post-type-archive-location .intro h1, .post-type-archive-location .intro .h1, .testimonials-section h1, .testimonials-section .h1, .testimonials-section h2, .testimonials-section .h2, .testimonials-section h3, .testimonials-section .h3, .plain-text h1, .plain-text .h1, .plain-text h2, .plain-text .h2, .plain-text h3, .plain-text .h3 {
.headline-lg, .single-location h1, .single-location .h1, .post-type-archive-location .intro h1, .post-type-archive-location .intro .h1, .category-slice-top h1, .category-slice-top .h1, .callout-text h2, .callout-text .h2, section.vertical-callouts h2, section.vertical-callouts .h2, .testimonials-section h1, .testimonials-section .h1, .testimonials-section h2, .testimonials-section .h2, .testimonials-section h3, .testimonials-section .h3, .plain-text h1, .plain-text .h1, .plain-text h2, .plain-text .h2, .plain-text h3, .plain-text .h3 {
font-size: 45px;
line-height: 45px;
}
@@ -12253,21 +12279,21 @@ img {
}
.cta-button a, .single-location .form-wrapper p:has(input[type=submit]) a, .cta-button input, .single-location .form-wrapper p:has(input[type=submit]) input {
text-transform: uppercase;
background-color: #ae1716;
background-color: #c80000;
color: #fff !important;
text-decoration: none;
display: inline-block;
min-height: 47px;
line-height: 47px;
border: 1px solid #ae1716;
border: 1px solid #c80000;
border-radius: 25px;
padding: 0 60px;
transition: all 0.3s ease;
font-size: 20px;
}
.cta-button a:hover, .single-location .form-wrapper p:has(input[type=submit]) a:hover, .cta-button input:hover, .single-location .form-wrapper p:has(input[type=submit]) input:hover {
background-color: #f5f5f5;
color: #ae1716 !important;
background-color: #f2f2f2;
color: #c80000 !important;
}
.cta-button.small a, .single-location .form-wrapper p.small:has(input[type=submit]) a {
padding: 0 25px;
@@ -12286,7 +12312,7 @@ input, label, textarea {
}
.top-bar {
background-color: #f5f5f5;
background-color: #f2f2f2;
padding: 25px 0 25px;
}
.top-bar input[type=text] {
@@ -12458,7 +12484,7 @@ input, label, textarea {
}
footer.site-footer {
background-color: #f5f5f5;
background-color: #f2f2f2;
padding: 2rem 0;
}
footer.site-footer h2, footer.site-footer .h2 {
@@ -12521,7 +12547,7 @@ footer.site-footer .logo-col img {
}
footer.sub-footer {
background-color: #f5f5f5;
background-color: #f2f2f2;
border-top: 1px solid #cbcbcb;
padding: 1.5rem 0;
}
@@ -12679,6 +12705,74 @@ footer.sub-footer {
font-weight: bold;
}
section.block-links {
padding: 4rem 0;
}
section.vertical-callouts {
background-color: #f2f2f2;
margin-bottom: 4rem;
padding: 4rem 0;
}
section.vertical-callouts h2, section.vertical-callouts .h2 {
text-align: center;
text-transform: uppercase;
margin-bottom: 4rem;
}
.callout-text {
background-color: #f2f2f2;
text-align: center;
padding: 3rem 0;
}
.callout-text h2, .callout-text .h2 {
text-transform: uppercase;
}
.callout-text h2 span, .callout-text .h2 span {
color: #c80000;
}
.callout-text h2 span.line-2, .callout-text .h2 span.line-2 {
display: block;
}
.callout-text p {
font-size: 29px;
margin-top: 1rem;
margin-bottom: 0;
}
section.accordian-content {
padding: 4rem 0;
}
section.accordian-content .accordian-item {
margin: 3rem 0;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
padding: 1rem;
}
section.accordian-content .accordian-item svg {
transition: transform 0.3s ease;
}
section.accordian-content .accordian-item.expanded svg {
transform: rotate(180deg);
}
section.accordian-content .accordian-item .content {
display: none;
}
section.accordian-content a {
color: #c80000;
text-decoration: none;
font-weight: bold;
text-transform: uppercase;
}
section.intro-header {
background-color: #f2f2f2;
padding: 4rem 0;
}
section.intro-header h1, section.intro-header .h1 {
text-transform: uppercase;
margin: 0;
}
.top-image {
position: relative;
background-size: cover;
@@ -12726,6 +12820,41 @@ section.intro-slide p {
font-size: 34px;
}
.callout-page, .callout-category {
background-color: #fff;
border-radius: 15px;
padding: 1rem;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
}
.callout-page .image-wrapper .image-side, .callout-category .image-wrapper .image-side {
aspect-ratio: 1;
display: block;
width: 500px;
background-position: center;
background-size: cover;
}
.callout-page .meta-top, .callout-category .meta-top {
text-transform: uppercase;
color: #c80000;
font-size: 16px;
letter-spacing: 1px;
margin: 0;
}
.callout-page h3, .callout-page .h3, .callout-category h3, .callout-category .h3 {
text-transform: uppercase;
margin-bottom: 1.5rem;
}
.callout-page .excerpt-wrapper, .callout-category .excerpt-wrapper {
max-width: 600px;
font-size: 1.25rem;
line-height: 2rem;
margin-bottom: 2rem;
}
.callout-page .excerpt-wrapper p, .callout-category .excerpt-wrapper p {
margin: 0;
}
section.woocommerce-wrapper {
margin-top: 4rem;
}
@@ -12734,14 +12863,14 @@ section.woocommerce-wrapper ul.products {
flex-wrap: wrap;
}
div.product-tile {
div.product-tile, div.page-tile {
border-radius: 15px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
padding: 1rem;
position: relative;
height: 100%;
}
div.product-tile a.add_to_cart_button {
div.product-tile a.add_to_cart_button, div.page-tile a.add_to_cart_button {
position: absolute;
top: 1rem;
right: 1rem;
@@ -12752,20 +12881,21 @@ div.product-tile a.add_to_cart_button {
justify-content: center;
align-items: center;
margin: 0 !important;
opacity: 0;
transition: all 0.3s ease;
border-radius: 50%;
}
div.product-tile a.add_to_cart_button img {
div.product-tile a.add_to_cart_button img, div.page-tile a.add_to_cart_button img {
margin: 0 !important;
width: 20px !important;
}
div.product-tile a.add_to_cart_button::after {
div.product-tile a.add_to_cart_button::after, div.page-tile a.add_to_cart_button::after {
position: absolute;
left: 80%;
margin: 0 !important;
top: -20%;
}
div.product-tile a.added_to_cart {
div.product-tile a.added_to_cart, div.page-tile a.added_to_cart {
padding-top: 0;
position: absolute;
display: block;
@@ -12774,7 +12904,7 @@ div.product-tile a.added_to_cart {
text-transform: uppercase;
font-weight: bold;
}
div.product-tile a.added_to_cart::after {
div.product-tile a.added_to_cart::after, div.page-tile a.added_to_cart::after {
content: "";
width: 13px;
height: 14px;
@@ -12785,26 +12915,26 @@ div.product-tile a.added_to_cart::after {
top: 3px;
background-size: contain;
}
div.product-tile a.added_to_cart:hover::after {
div.product-tile a.added_to_cart:hover::after, div.page-tile a.added_to_cart:hover::after {
left: 105%;
}
div.product-tile:hover .add_to_cart_button {
div.product-tile:hover .add_to_cart_button, div.page-tile:hover .add_to_cart_button {
opacity: 1;
}
div.product-tile h2, div.product-tile .h2 {
div.product-tile h2, div.product-tile .h2, div.page-tile h2, div.page-tile .h2 {
font-weight: bold;
font-size: 20px;
line-height: 26px;
text-align: center;
}
div.product-tile h2 a, div.product-tile .h2 a {
div.product-tile h2 a, div.product-tile .h2 a, div.page-tile h2 a, div.page-tile .h2 a {
color: #000;
text-decoration: none;
}
div.product-tile h2 a:hover, div.product-tile .h2 a:hover {
div.product-tile h2 a:hover, div.product-tile .h2 a:hover, div.page-tile h2 a:hover, div.page-tile .h2 a:hover {
color: #c80000;
}
div.product-tile p.price {
div.product-tile p.price, div.page-tile p.price {
color: #c80000 !important;
font-size: 25px !important;
line-height: 26px;
@@ -12812,20 +12942,96 @@ div.product-tile p.price {
margin: 2rem 0 0 !important;
font-weight: bold !important;
}
div.product-tile p.msrp {
div.product-tile p.msrp, div.page-tile p.msrp {
color: #acacac !important;
font-size: 15px !important;
line-height: 21px;
text-align: center;
margin: 0;
}
div.product-tile p.savings {
div.product-tile p.savings, div.page-tile p.savings {
color: #c80000 !important;
font-size: 16px !important;
line-height: 16px;
text-align: center;
margin: 0;
}
div.product-tile a.learn-more, div.page-tile a.learn-more {
text-transform: uppercase;
color: #c80000;
font-weight: bold;
text-decoration: none;
padding-bottom: 1rem;
}
.page-tile h2, .page-tile .h2 {
text-align: left !important;
font-size: 32px !important;
line-height: 37px !important;
text-transform: uppercase;
margin: 1rem 0;
}
.category-slice-top {
background-color: #f2f2f2;
padding: 4rem 0;
text-align: center;
}
.category-slice-top h1, .category-slice-top .h1 {
text-transform: uppercase;
}
.category-slice-top p {
font-size: 29px;
line-height: 30px;
}
.category-slice {
padding: 4rem 0 0;
}
.category-slice img {
max-height: 300px;
width: auto;
}
.category-slice h2, .category-slice .h2 {
font-weight: bold;
margin-bottom: 2rem;
}
.single-product .single-product-top {
background-color: #f2f2f2;
padding: 4rem 0 !important;
margin-top: 0 !important;
}
.single-product .wc-tabs {
list-style-type: none;
display: flex;
border-bottom: 1px solid #000;
padding: 0 0 1rem;
margin: 0 0 2rem;
gap: 2rem;
}
.single-product .wc-tabs li {
font-weight: 600;
text-transform: uppercase;
font-size: 1.5rem;
}
.single-product .wc-tabs li a {
color: #505050;
text-decoration: none;
}
.single-product .wc-tabs li.active {
position: relative;
}
.single-product .wc-tabs li.active:after {
content: "";
position: absolute;
top: calc(100% + 1rem);
transform: translateY(-1px);
display: block;
width: 100%;
height: 3px;
background-color: #c80000;
}
.post-type-archive-location .intro {
text-align: center;
@@ -12892,3 +13098,7 @@ div.product-tile p.savings {
color: #505050;
text-decoration: none;
}
.search-results .tile-wrapper {
margin-bottom: 1.5rem;
}

16
dist/app.js vendored
View File

@@ -13837,11 +13837,25 @@ jquery__WEBPACK_IMPORTED_MODULE_0___default()("body").on("click", ".toggle-sub-n
jquery__WEBPACK_IMPORTED_MODULE_0___default()(e.target).parent("li").toggleClass("children-showing");
});
// mobile nav toggle
document.getElementById("mobile-nav-toggle").addEventListener("click", function (e) {
jquery__WEBPACK_IMPORTED_MODULE_0___default()("#mobile-nav-toggle").on("click", function (e) {
e.preventDefault();
document.getElementsByTagName("body")[0].classList.toggle("mobile-nav-open");
});
// accordian expander
// $(".accordian-item .content").fadeOut(0);
jquery__WEBPACK_IMPORTED_MODULE_0___default()(".accordian-item a").on("click", function (e) {
e.preventDefault();
var wrapper = jquery__WEBPACK_IMPORTED_MODULE_0___default()(e.target).closest(".accordian-item");
window.activeThingy = wrapper;
if (wrapper.hasClass("expanded")) {
wrapper.find(".content").fadeOut();
} else {
wrapper.find(".content").fadeIn();
}
wrapper.toggleClass("expanded");
});
/***/ }),
/***/ "./src/scss/app.scss":

View File

@@ -1,4 +1,4 @@
{
"/app.js": "/app.js?id=dba67887fc4ea12d32fe1c2757aaa5c1",
"/app.css": "/app.css?id=4a8b4a07b6234e03881bacb9bf94454f"
"/app.js": "/app.js?id=8af0c9c0d989b79afe124f8138dc1a63",
"/app.css": "/app.css?id=d043bd4af528893790cc648d33110d1f"
}

View File

@@ -6,9 +6,7 @@
// https://rad-theme-engine.ofco.cloud/docs/guides/helpers/
echo site()->render("header", [
"logo" => site()->getAssetURL("main-logo.jpg"),
"accountHref" => get_permalink(73),
"person-icon" => site()->getAssetContents("person-circle.svg"),
"phone-icon" => site()->getAssetContents("telephone.svg"),
"menu" => site()->renderMenu("main-nav"),
"searchTerm" => $_GET["s"] ?? "",
]);

View File

@@ -18,6 +18,13 @@ class McCansHelpers
};
}
public static function usd()
{
return function ($template, $context, $args, $source) {
return "$" . number_format((float) $context->get($args), 2);
};
}
public static function locationTile()
{
return function ($template, $context, $args, $source) {
@@ -36,17 +43,156 @@ class McCansHelpers
"woocommerce.cartUrl",
"woocommerce.attribute.msrp",
]);
if ((float) $data["msrp"] && (float) $data["price"]) {
if ($data["price"] != 0) {
$difference = $data["msrp"] - $data["price"];
if ($difference > 0) {
$savingsPercent = round(($difference / $data["msrp"])*100);
}
$data["savings"] = self::calcDifference($data["price"], $data["msrp"]);
$data["savings"] = $savingsPercent;
}
}
return site()->render("product-tile", $data);
};
}
public static function renderBlock()
{
return function ($template, $context, $args, $source) {
// if a page or product is chosen...
if ($context->get($args)["page_product"]) {
$post = $context->get($args)["page_product"];
if ($post->post_type === "page") {
return site()->render("page-tile", site()->getPost($post, [
"title",
"url",
"thumbnail",
]));
}
$data = site()->getPost($post, [
"url",
"id",
"title",
"thumbnail",
"woocommerce.price",
"woocommerce.attribute.msrp",
"woocommerce.cartUrl",
"woocommerce.sku",
]);
$data["savings"] = self::calcDifference($data["price"], $data["msrp"]);
return site()->render("product-tile", $data);
}
// if we got here, then hopefully the product_category is set
if (!$context->get($args)["product_category"]) {
return "No valid option chosen...";
}
$termID = $context->get($args)["product_category"];
$term = get_term($termID);
return site()->render("page-tile", site()->getQueriedObject($term, ['title', 'thumbnail', 'url']));
};
}
public static function renderCallout()
{
return function ($template, $context, $args, $source) {
// if a page or product is chosen...
if ($context->get($args)["page_product"]) {
$post = $context->get($args)["page_product"];
if ($post->post_type === "page") {
return site()->render("page-callout", site()->getPost($post, [
"title",
"url",
"thumbnail",
"excerpt",
]));
}
$data = site()->getPost($post, [
"url",
"id",
"title",
"thumbnail",
"woocommerce.price",
"woocommerce.attribute.msrp",
"woocommerce.cartUrl",
"woocommerce.sku",
]);
$data["savings"] = self::calcDifference($data["price"], $data["msrp"]);
return site()->render("product-tile", $data);
}
// if we got here, then hopefully the product_category is set
if (!$context->get($args)["category"]) {
return "No valid option chosen...";
}
$termID = $context->get($args)["category"];
$term = get_term($termID);
return site()->render("category-callout", site()->getQueriedObject($term, [
'title',
'thumbnail',
'url',
'description',
]));
};
}
public static function calcDifference($price, $msrp): string
{
if (!$price) {
return "";
}
if (!$msrp) {
return "";
}
$difference = $msrp - $price;
if ($difference === 0) {
return "";
}
return round(($difference / $msrp) * 100);
}
public static function searchResultBox(string $type)
{
return function ($template, $context, $args, $source) use ($type) {
$post = $context->get($args);
if ($type === "product") {
if ($post["post_type"] !== "product") {
return "";
}
$data = site()->getPost($post["id"], [
"url",
"id",
"title",
"thumbnail",
"woocommerce.price",
"woocommerce.attribute.msrp",
"woocommerce.cartUrl",
"woocommerce.sku",
]);
$data["savings"] = self::calcDifference($data["price"], $data["msrp"]);
return site()->render("product-tile", $data);
}
if ($type === "page") {
// var_dump($post);
if ($post["post_type"] !== "page") {
return "";
}
return site()->render("page-tile", site()->getPost($post["id"], [
"url",
"id",
"title",
"thumbnail",
]));
}
return site()->render("search-box", $context->get($args));
};
}
}

15
search.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
get_header();
$results = site()->getDefaultPosts(["id", "post_type", "title"]);
$hasResults = count($results) > 0;
echo site()->render("search-results", [
"searchTerm" => $_GET["s"] ?? "",
"hasResults" => $hasResults,
"pages" => array_filter($results, fn ($r) => $r["post_type"] === "page"),
"products" => array_filter($results, fn ($r) => $r["post_type"] === "product"),
]);
get_footer();

View File

@@ -1,13 +0,0 @@
<?php
get_header('shop');
do_action('woocommerce_before_main_content');
while (have_posts()):
the_post();
wc_get_template_part('content', 'single-product');
endwhile;
do_action('woocommerce_after_main_content');
do_action('woocommerce_sidebar');
get_footer('shop');

View File

@@ -87,7 +87,24 @@ $("body").on("click", ".toggle-sub-nav", (e) => {
$(e.target).parent("li").toggleClass("children-showing");
});
// mobile nav toggle
document.getElementById("mobile-nav-toggle").addEventListener("click", (e) => {
$("#mobile-nav-toggle").on("click", (e) => {
e.preventDefault();
document.getElementsByTagName("body")[0].classList.toggle("mobile-nav-open");
});
// accordian expander
// $(".accordian-item .content").fadeOut(0);
$(".accordian-item a").on("click", (e) => {
e.preventDefault();
const wrapper = $(e.target).closest(".accordian-item");
window.activeThingy = wrapper;
if (wrapper.hasClass("expanded")) {
wrapper.find(".content").fadeOut();
} else {
wrapper.find(".content").fadeIn();
}
wrapper.toggleClass("expanded");
});

View File

@@ -13,10 +13,20 @@
@import "flex-plain-text";
@import "flex-logo-slider";
@import "flex-testimonials";
@import "flex-block-links";
@import "flex-veritcal-callouts";
@import "flex-callout-text";
@import "flex-accordian-content";
@import "flex-intro-header";
@import "top-image";
@import "intro-slide";
@import "callouts";
@import "shop";
@import "shop-catgeory";
@import "product";
@import "locations";
@import "location";
@import "search";

42
src/scss/callouts.scss Normal file
View File

@@ -0,0 +1,42 @@
.callout-page, .callout-category {
background-color: $c-white;
border-radius: 15px;
padding: 1rem;
box-shadow: 0 0 30px rgba($c-black, 0.1);
margin-bottom: 2rem;
.image-wrapper {
.image-side {
aspect-ratio: 1;
display: block;
width: 500px;
background-position: center;
background-size: cover;
}
}
.meta-top {
text-transform: uppercase;
color: $c-brightRed;
font-size: 16px;
letter-spacing: 1px;
margin: 0;
}
h3 {
@extend .headline-md;
text-transform: uppercase;
margin-bottom: 1.5rem;
}
.excerpt-wrapper {
max-width: 600px;
font-size: 1.25rem;
line-height: 2rem;
margin-bottom: 2rem;
p {
margin: 0;
}
}
}

View File

@@ -0,0 +1,31 @@
section.accordian-content {
padding: 4rem 0;
.accordian-item {
margin: 3rem 0;
box-shadow: 0 0 20px rgba($c-black, 0.1);
padding: 1rem;
svg {
transition: transform $transition-time ease;
}
&.expanded {
svg {
transform: rotate(180deg);
}
}
.content {
display: none;
}
}
a {
@extend .montserrat;
color: $c-brightRed;
text-decoration: none;
font-weight: bold;
text-transform: uppercase;
}
}

View File

@@ -0,0 +1,3 @@
section.block-links {
padding: 4rem 0;
}

View File

@@ -0,0 +1,24 @@
.callout-text {
background-color: $c-offWhite;
text-align: center;
padding: 3rem 0;
h2 {
@extend .headline-lg;
text-transform: uppercase;
span {
color: $c-brightRed;
&.line-2 {
display: block;
}
}
}
p {
font-size: 29px;
margin-top: 1rem;
margin-bottom: 0;
}
}

View File

@@ -0,0 +1,11 @@
section.intro-header {
background-color: $c-offWhite;
padding: 4rem 0;
h1 {
@extend .montserrat;
@extend .headline-md;
text-transform: uppercase;
margin: 0;
}
}

View File

@@ -0,0 +1,12 @@
section.vertical-callouts {
background-color: $c-offWhite;
margin-bottom: 4rem;
padding: 4rem 0;
h2 {
@extend .headline-lg;
text-align: center;
text-transform: uppercase;
margin-bottom: 4rem;
}
}

View File

@@ -10,6 +10,32 @@ img {
filter: invert(10%) sepia(79%) saturate(7388%) hue-rotate(2deg) brightness(86%) contrast(111%);
}
.headline-sm {
@extend .montserrat;
font-weight: 600;
font-size: 40px;
line-height: 40px;
letter-spacing: -1px;
@include media-breakpoint-down(md) {
font-size: 30px;
line-height: 30px;
}
}
.headline-md {
@extend .montserrat;
font-weight: 600;
font-size: 54px;
line-height: 54px;
letter-spacing: -1px;
@include media-breakpoint-down(md) {
font-size: 40px;
line-height: 40px;
}
}
.headline-lg {
@extend .montserrat;
font-weight: 600;
@@ -42,13 +68,13 @@ img {
a, input {
@extend .montserrat;
text-transform: uppercase;
background-color: $c-red;
background-color: $c-brightRed;
color: $c-white !important;
text-decoration: none;
display: inline-block;
min-height: 47px;
line-height: 47px;
border: 1px solid $c-red;
border: 1px solid $c-brightRed;
border-radius: 25px;
padding: 0 60px;
transition: all $transition-time ease;
@@ -56,7 +82,7 @@ img {
&:hover {
background-color: $c-offWhite;
color: $c-red !important;
color: $c-brightRed !important;
}
}

43
src/scss/product.scss Normal file
View File

@@ -0,0 +1,43 @@
.single-product {
.single-product-top {
background-color: $c-offWhite;
padding: 4rem 0 !important;
margin-top: 0 !important;
}
.wc-tabs {
list-style-type: none;
display: flex;
border-bottom: 1px solid $c-black;
padding: 0 0 1rem;
margin: 0 0 2rem;
gap: 2rem;
li {
@extend .montserrat;
font-weight: 600;
text-transform: uppercase;
font-size: 1.5rem;
a {
color: $c-textAlt;
text-decoration: none;
}
&.active {
position: relative;
&:after {
content: "";
position: absolute;
top: calc(100% + 1rem);
transform: translateY(-1px);
display: block;
width: 100%;
height: 3px;
background-color: $c-brightRed;
}
}
}
}
}

5
src/scss/search.scss Normal file
View File

@@ -0,0 +1,5 @@
.search-results {
.tile-wrapper {
margin-bottom: 1.5rem;
}
}

View File

@@ -0,0 +1,30 @@
.category-slice-top {
background-color: $c-offWhite;
padding: 4rem 0;
text-align: center;
h1 {
@extend .headline-lg;
text-transform: uppercase;
}
p {
font-size: 29px;
line-height: 30px;
}
}
.category-slice {
padding: 4rem 0 0;
img {
max-height: 300px;
width: auto;
}
h2 {
@extend .montserrat;
font-weight: bold;
margin-bottom: 2rem;
}
}

View File

@@ -8,7 +8,7 @@ section.woocommerce-wrapper {
}
div.product-tile {
div.product-tile, div.page-tile {
border-radius: 15px;
box-shadow: 0 0 20px rgba($c-black, 0.1);
padding: 1rem;
@@ -26,7 +26,7 @@ div.product-tile {
justify-content: center;
align-items: center;
margin: 0 !important;
// opacity: 0;
opacity: 0;
transition: all $transition-time ease;
border-radius: 50%;
@@ -119,4 +119,24 @@ div.product-tile {
text-align: center;
margin: 0;
}
a.learn-more {
@extend .montserrat;
text-transform: uppercase;
color: $c-brightRed;
font-weight: bold;
text-decoration: none;
padding-bottom: 1rem;
}
}
.page-tile {
h2 {
@extend .montserrat;
text-align: left !important;
font-size: 32px !important;
line-height: 37px !important;
text-transform: uppercase;
margin: 1rem 0;
}
}

View File

@@ -1,6 +1,6 @@
$c-black: #000;
$c-white: #fff;
$c-offWhite: #f5f5f5;
$c-offWhite: #f2f2f2;
$c-lightGrey: #d9d9d9;
$c-grey: #cbcbcb;
$c-midGrey: #acacac;

20
tpl/category-callout.tpl Normal file
View File

@@ -0,0 +1,20 @@
<div class="callout-category">
<div class="d-flex gap-5 align-items-center">
<div class="image-wrapper">
<div class="image-side" style="background-image: url({{ thumbnail }})">&nbsp;</div>
</div>
<div class="content-wrapper">
<p class="meta-top">Available Now</p>
<h3>{{{ title }}}</h3>
<div class="excerpt-wrapper">
{{{ description }}}
</div>
<p class="cta-button small">
<a href="{{ url }}">Shop {{{ title }}}</a>
</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,24 @@
<section class="accordian-content">
<div class="container">
<div class="row">
<div class="col">
{{#each text_blocks}}
<div class="accordian-item {{#if @first}}expanded{{/if}}">
<div class="d-flex gap-3 align-items-center">
<div class="content-wrapper w-100">
<a href="#">{{ headline }}</a>
<div class="content" {{#if @first}}style="display: block"{{/if}}>
{{{ content }}}
</div>
</div>
<div class="arrow-wrapper">
<a href="#">{{#assetContents chevron-down.svg}}</a>
</div>
</div>
</div>
{{/each}}
</div>
</div>
</div>
</section>

11
tpl/flex_block_links.tpl Normal file
View File

@@ -0,0 +1,11 @@
<section class="block-links">
<div class="container">
<div class="row">
{{#each blocks}}
<div class="col">
{{#renderBlock this}}
</div>
{{/each}}
</div>
</div>
</section>

16
tpl/flex_callout_text.tpl Normal file
View File

@@ -0,0 +1,16 @@
<section class="callout-text">
<div class="container">
<div class="row">
<div class="col">
<h2>{{{ headline_1 }}}
{{#if headline_2}}
<span class="line-2">{{ headline_2 }}</span>
{{/if}}
</h2>
{{#if descriptive_text}}
<p>{{descriptive_text}}</p>
{{/if}}
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,12 @@
<section class="vertical-callouts">
<div class="container">
<div class="row">
<div class="col">
<h2>{{ headline }}</h2>
{{#each blocks}}
{{#renderCallout this}}
{{/each}}
</div>
</div>
</div>
</section>

View File

@@ -20,10 +20,12 @@
<div class="row">
<div class="col">
<div class="d-flex gap-5 justify-content-end">
<input type="text" placeholder="What are you searching for?" />
<form action="/">
<input type="text" name="s" placeholder="What are you searching for?" value="{{ searchTerm }}" />
</form>
<div>
<a href="{{ accountHref }}" class="account-button">
{{{ person-icon }}}
{{#assetContents person-circle.svg}}
My Account
</a>
</div>
@@ -51,7 +53,7 @@
<nav class="align-items-center gap-3 main-nav">
{{{ menu }}}
<p class="cta-button small">
<a href="tel:855-622-2661">{{{ phone-icon }}} 855-622-2661</a>
<a href="tel:855-622-2661">{{#assetContents telephone.svg}} 855-622-2661</a>
</p>
</nav>
</div>

20
tpl/page-callout.tpl Normal file
View File

@@ -0,0 +1,20 @@
<div class="callout-page">
<div class="d-flex gap-5 align-items-center">
<div class="image-wrapper">
<div class="image-side" style="background-image: url({{ thumbnail }})">&nbsp;</div>
</div>
<div class="content-wrapper">
<p class="meta-top">Available Now</p>
<h3>{{{ title }}}</h3>
<div class="excerpt-wrapper">
{{{ excerpt }}}
</div>
<p class="cta-button small">
<a href="{{ url }}">Shop {{{ title }}}</a>
</p>
</div>
</div>
</div>

9
tpl/page-tile.tpl Normal file
View File

@@ -0,0 +1,9 @@
<div class="page-tile">
<a href="{{ url }}">
<img src="{{ thumbnail }}" alt="{{ title }}" />
</a>
<h2>
<a href="{{ url }}">{{{ title }}}</a>
</h2>
<a href="{{ url }}" class="learn-more">Learn More {{#assetContents arrow-right.svg}}</a>
</div>

View File

@@ -14,9 +14,9 @@
<img src="{{ thumbnail }}" alt="{{ title }}" />
</a>
<h2>
<a href="{{ url }}">{{ title }}</a>
<a href="{{ url }}">{{{ title }}}</a>
</h2>
<p class="price">${{ price }}</p>
<p class="price">{{#usd price }}</p>
{{#if msrp}}
<p class="msrp">MSRP: ${{ msrp }}</p>
{{/if}}

56
tpl/search-results.tpl Normal file
View File

@@ -0,0 +1,56 @@
<section class="intro-header">
<div class="container">
<div class="row">
<div class="col">
<div class="d-flex gap-3 justify-content-between">
<h1>Search results for {{ searchTerm }} ({{#queryCount}})</h1>
{{#paginationLinks}}
</div>
</div>
</div>
</div>
</section>
<section class="results-container">
<div class="container">
<div class="row">
<div class="col">
{{#if hasResults}}
<h2 class="mt-5 headline-sm">Products:</h2>
<div class="row">
{{#each products}}
<div class="col-6 col-md-4 col-lg-3 tile-wrapper">
{{#productBox this}}
</div>
{{/each}}
</div>
<h2 class="mt-5 headline-md">Pages</h2>
<div class="row">
{{#each pages}}
<div class="col-6 col-md-4 col-lg-3 tile-wrapper">
{{#pageBox this}}
</div>
{{else}}
<div class="col-6 col-md-4 col-lg-3 tile-wrapper">
<p>No pages</p>
</div>
{{/each}}
</div>
<div class="row">
<div class="col">
</div>
</div>
{{else}}
<p>&nbsp;</p>
<p class="mt-3 mb-5 text-center">Sorry, there were no results found for "{{ searchTerm }}". Please try a different search.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
{{/if}}
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,30 @@
<section class="category-slice-top">
<div class="container">
<div class="row">
<div class="col">
<h1>{{{ title }}}</h1>
<p>{{ description }}</p>
</div>
</div>
</div>
</section>
<section class="category-slice">
<div class="container">
<div class="row">
<div class="col-12 col-sm-6 col-md-4 text-center">
<img src="{{ thumbnail }}" alt="{{ title }}" />
</div>
<div class="col-12 col-sm-6 col-md-8">
<h2>{{ intro_headline }}</h2>
{{{ intro_content }}}
<p class="cta-button small">
<a href="#products">Shop Now</a>
</p>
</div>
</div>
</div>
</section>
<a name="products"></a>

10
tpl/shop-category-top.tpl Normal file
View File

@@ -0,0 +1,10 @@
<section class="intro-slide" style="background-image: url({{ thumbnail }})">
<div class="container">
<div class="row">
<div class="col">
<h1>{{{ title }}}</h1>
<p>{{ description }}</p>
</div>
</div>
</div>
</section>

View File

@@ -1,4 +1,4 @@
<section class="woocommerce-wrapper">
<section class="woocommerce-wrapper {{additionalTopLevelClass}}">
<div class="container">
<div class="row">
<div class="col">

View File

@@ -4,7 +4,7 @@
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../open-function-computers-llc/rad-theme-engine/bin/getIcon)
* This file includes the referenced bin path (../open-function-computers-llc/rad-theme-engine/bin/rad)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
@@ -112,8 +112,8 @@ if (PHP_VERSION_ID < 80000) {
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
return include("phpvfscomposer://" . __DIR__ . '/..'.'/open-function-computers-llc/rad-theme-engine/bin/getIcon');
return include("phpvfscomposer://" . __DIR__ . '/..'.'/open-function-computers-llc/rad-theme-engine/bin/rad');
}
}
return include __DIR__ . '/..'.'/open-function-computers-llc/rad-theme-engine/bin/getIcon';
return include __DIR__ . '/..'.'/open-function-computers-llc/rad-theme-engine/bin/rad';

View File

@@ -54,30 +54,30 @@
},
{
"name": "open-function-computers-llc/rad-theme-engine",
"version": "v1.0.34",
"version_normalized": "1.0.34.0",
"version": "v1.0.38",
"version_normalized": "1.0.38.0",
"source": {
"type": "git",
"url": "https://github.com/open-function-computers-llc/rad-theme-engine.git",
"reference": "c0b814bdd0ee1f093a051ed2e78efdf8c1f4f94e"
"reference": "90a351370bbd5a92a2c68c226a2c0cf3f97ce8f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/open-function-computers-llc/rad-theme-engine/zipball/c0b814bdd0ee1f093a051ed2e78efdf8c1f4f94e",
"reference": "c0b814bdd0ee1f093a051ed2e78efdf8c1f4f94e",
"url": "https://api.github.com/repos/open-function-computers-llc/rad-theme-engine/zipball/90a351370bbd5a92a2c68c226a2c0cf3f97ce8f4",
"reference": "90a351370bbd5a92a2c68c226a2c0cf3f97ce8f4",
"shasum": ""
},
"require": {
"jjgrainger/posttypes": "^2.1",
"php": ">=7.4",
"php": ">=8.1",
"salesforce/handlebars-php": "^2.3"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"time": "2025-05-20T21:03:45+00:00",
"time": "2025-06-13T18:23:21+00:00",
"bin": [
"bin/getIcon"
"bin/rad"
],
"type": "library",
"installation-source": "dist",
@@ -98,7 +98,7 @@
],
"support": {
"issues": "https://github.com/open-function-computers-llc/rad-theme-engine/issues",
"source": "https://github.com/open-function-computers-llc/rad-theme-engine/tree/v1.0.34"
"source": "https://github.com/open-function-computers-llc/rad-theme-engine/tree/v1.0.38"
},
"install-path": "../open-function-computers-llc/rad-theme-engine"
},

View File

@@ -3,7 +3,7 @@
'name' => 'open-function-computers-llc/wp-theme',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => 'c074a5ef18d6702ae4abe09fa1289fc93726117d',
'reference' => 'edcad561a551ed7689bc2ae7fb39e1cde81aba2e',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -20,9 +20,9 @@
'dev_requirement' => false,
),
'open-function-computers-llc/rad-theme-engine' => array(
'pretty_version' => 'v1.0.34',
'version' => '1.0.34.0',
'reference' => 'c0b814bdd0ee1f093a051ed2e78efdf8c1f4f94e',
'pretty_version' => 'v1.0.38',
'version' => '1.0.38.0',
'reference' => '90a351370bbd5a92a2c68c226a2c0cf3f97ce8f4',
'type' => 'library',
'install_path' => __DIR__ . '/../open-function-computers-llc/rad-theme-engine',
'aliases' => array(),
@@ -31,7 +31,7 @@
'open-function-computers-llc/wp-theme' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => 'c074a5ef18d6702ae4abe09fa1289fc93726117d',
'reference' => 'edcad561a551ed7689bc2ae7fb39e1cde81aba2e',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),

View File

@@ -4,8 +4,8 @@
$issues = array();
if (!(PHP_VERSION_ID >= 70400)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
if (!(PHP_VERSION_ID >= 80100)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {

View File

@@ -1,37 +0,0 @@
#!/usr/bin/env php
<?php
$autoloadPaths = [
__DIR__ . '/../../../autoload.php', // typical path when symlinked in vendor/bin
__DIR__ . '/../vendor/autoload.php', // fallback if run directly from source
];
$found = false;
foreach ($autoloadPaths as $path) {
if (file_exists($path)) {
require $path;
$found = true;
break;
}
}
if (!$found) {
fwrite(STDERR, "Could not locate Composer autoloader.\n");
exit(1);
}
use ofc\IconGetter;
if ($argc !== 2) {
fwrite(STDERR, "Usage: getIcon <icon-name>\n");
exit(1);
}
$icon = trim(strtolower($argv[1]));
try {
IconGetter::get($icon);
} catch (Exception $e) {
fwrite(STDERR, "Error: ".$e->getMessage().PHP_EOL);
exit(1);
}

View File

@@ -0,0 +1,49 @@
#!/usr/bin/env php
<?php
$autoloadPaths = [
__DIR__ . '/../../../autoload.php', // typical path when symlinked in vendor/bin
__DIR__ . '/../vendor/autoload.php', // fallback if run directly from source
];
$found = false;
foreach ($autoloadPaths as $path) {
if (file_exists($path)) {
require $path;
$found = true;
break;
}
}
if (!$found) {
fwrite(STDERR, "Could not locate Composer autoloader.\n");
exit(1);
}
if ($argc < 2) {
fwrite(STDERR, "Usage: rad <command> [options]\n");
exit(1);
}
$command = strtolower(trim($argv[1]));
$arguments = array_slice($argv, 2);
$availableCommands = [
"get-icon" => \ofc\Commands\GetIconCommand::class,
// TODO: additional commands go below
// "swap-library" => \ofc\Commands\LibrarySwap::class,
];
if (!isset($availableCommands[$command])) {
fwrite(STDERR, "Error: Unknown command '$command'\n");
exit(1);
}
// If the user requested help
if (in_array('--help', $arguments)) {
echo $availableCommands[$command]::getHelp().PHP_EOL.PHP_EOL;
exit(0);
}
$availableCommands[$command]::run($arguments);

View File

@@ -2,7 +2,7 @@
"name": "open-function-computers-llc/rad-theme-engine",
"description": "A suite of classes to make WordPress theme development cleaner",
"require": {
"php": ">=7.4",
"php": ">=8.1",
"jjgrainger/posttypes": "^2.1",
"salesforce/handlebars-php": "^2.3"
},
@@ -25,6 +25,6 @@
"post-create-project-cmd": "ofc\\RadThemeEngine::setup"
},
"bin": [
"bin/getIcon"
"bin/rad"
]
}

View File

@@ -0,0 +1,35 @@
<?php
namespace ofc\Commands;
use ofc\IconGetter;
class GetIconCommand
{
public static function run(array $args): void
{
if (count($args) !== 1) {
fwrite(STDERR, "Usage: rad get-icon <icon-name>\n");
var_dump($args);
exit(1);
}
$icon = trim(strtolower($args[0]));
IconGetter::get($icon);
}
public static function getHelp(): string
{
return <<<HELP
Usage: rad get-icon <icon-name>
Description:
Download the SVG icon from https://icons.getbootstrap.com and place it in your theme assets folder.
TODO: link to the docs page.
Example:
rad get-icon phone
HELP;
}
}

View File

@@ -85,6 +85,39 @@ class RadThemeEngine
};
}
public static function pagination()
{
return function ($template, $context, $args, $source) {
return site()->renderTemplate(<<<HTML
<nav class="pagination-links">
<ul>
{{#if older }}
<li><a href="{{ older }}">Next</a></li>
{{/if}}
{{#if newer }}
<li><a href="{{ newer }}">Previous</a></li>
{{/if}}
</ul>
</nav>
HTML, site()->getPaginationLinks());
};
}
public static function count()
{
return function ($template, $context, $args, $source) {
return count($context->get($args));
};
}
public static function queryCount()
{
global $wp_query;
return function ($template, $context, $args, $source) use ($wp_query) {
return $wp_query->found_posts;
};
}
public static function assetURL()
{
return function ($template, $context, $args, $source) {

View File

@@ -2,6 +2,7 @@
namespace ofc;
use Exception;
use PostTypes\PostType;
use Handlebars\Handlebars;
use Handlebars\Loader\FilesystemLoader;
@@ -77,7 +78,8 @@ class Site
$this->registerCPTs();
// register custom hook callbacks
$this->processHooks();
$this->processActions();
$this->processFilters(); // TODO
// add custom shortcodes
$this->registerShortcodes();
@@ -432,6 +434,10 @@ class Site
define('DISALLOW_FILE_EDIT', true);
continue;
}
if ($key === "revisions") {
add_filter('wp_revisions_to_keep', fn ($num, $post) => 0, 10, 2);
continue;
}
if ($key === "gutenberg") {
add_filter('use_block_editor_for_post', '__return_false', 10);
add_action('wp_enqueue_scripts', function () {
@@ -481,6 +487,25 @@ class Site
add_action('init', fn () => add_filter('woocommerce_show_page_title', '__return_false'));
continue;
}
if ($key === "archive_description") {
add_action('init', fn () => remove_action('woocommerce_archive_description', 'woocommerce_taxonomy_archive_description', 10));
continue;
}
if ($key === "reviews") {
add_action('init', function () {
remove_post_type_support('product', 'comments');
});
add_filter('woocommerce_product_reviews_enabled', '__return_false');
add_filter('comments_array', function ($comments, $post_id) {
if (get_post_type($post_id) === 'product') {
return [];
}
return $comments;
}, 10, 2);
continue;
}
continue;
}
@@ -541,6 +566,11 @@ class Site
add_theme_support('menus');
continue;
}
if ($key === "excerpts" || $key === "excerpt") {
add_action('init', fn () => add_post_type_support('page', ['excerpt']));
add_action('init', fn () => add_post_type_support('post', ['excerpt']));
continue;
}
if ($key === "styleselect") {
add_filter('mce_buttons_2', function ($buttons) {
return array_merge(['styleselect'], $buttons);
@@ -556,6 +586,9 @@ class Site
}
if ($key === "woocommerce") {
add_theme_support('woocommerce');
add_theme_support('wc-product-gallery-zoom');
add_theme_support('wc-product-gallery-lightbox');
add_theme_support('wc-product-gallery-slider');
continue;
}
@@ -655,6 +688,10 @@ class Site
"assetURL" => \ofc\RadThemeEngine::assetURL(),
"assetUrl" => \ofc\RadThemeEngine::assetURL(),
"assetContents" => \ofc\RadThemeEngine::assetContents(),
"count" => \ofc\RadThemeEngine::count(),
"length" => \ofc\RadThemeEngine::count(),
"paginationLinks" => \ofc\RadThemeEngine::pagination(),
"queryCount" => \ofc\RadThemeEngine::queryCount(),
];
foreach ($helpers as $name => $callback) {
$this->hb->addHelper($name, $callback);
@@ -700,7 +737,20 @@ class Site
}
return "";
}
return $this->hb->render($fileName, $data);
try {
return $this->hb->render($fileName, $data);
} catch (Exception $e) {
$template = <<<HTML
<div style="padding: 1rem; text-align: center; border: 1px solid #da3636; border-radius: 5px; margin: 1rem;">{{{ message }}}</div>
HTML;
$message = isset($this->config["debug"]) && $this->config["debug"] === true ?
"There was an error. Please see the message below:<br /><strong>" . $e->getMessage() ."</strong>":
"There was an error rendering this page. Please reach out to the site owner or try again later.";
return $this->renderTemplate($template, ["message" => $message]);
}
}
/**
@@ -1012,6 +1062,19 @@ class Site
return $output;
}
public function getQueriedObject($term = null, $fields = [])
{
if (is_null($term)) {
$term = get_queried_object();
}
if ($fields === []) {
return $term;
}
return $this->getFieldsForTerm($fields, $term);
}
public function getTerm($slug, $fields = [])
{
$args = [
@@ -1027,32 +1090,51 @@ class Site
$output = [];
foreach ($results as $term) {
$append = [];
foreach ($fields as $key) {
$oldKey = $key;
if ($key == "id") {
$key = "term_id";
}
if ($key == "title") {
$key = "name";
}
if ($oldKey != $key) {
$append[$oldKey] = $term->$key;
continue;
}
if ($key === "url" || $key === "permalink") {
$append[$key] = get_term_link($term);
continue;
}
$append[$key] = $term->$key;
}
$append = $this->getFieldsForTerm($fields, $term);
$output[] = $append;
}
return $output;
}
private function getFieldsForTerm(array $fields, $term)
{
$output = [];
foreach ($fields as $key) {
if ($key === "id" || $key === "ID" || $key === "term_id") {
$output[$key] = $term->term_id;
continue;
}
if ($key === "title" || $key === "name") {
$output[$key] = $term->name;
continue;
}
if ($key === "url" || $key === "permalink") {
$output[$key] = get_term_link($term);
continue;
}
if ($key === "thumbnail") {
$thumbnailID = get_term_meta($term->term_id, 'thumbnail_id', true);
if (!$thumbnailID) {
$output[$key] = "";
continue;
}
$output[$key] = wp_get_attachment_url($thumbnailID);
continue;
}
if (substr($key, 0, 4) === "acf.") {
$key = str_replace("acf.", "", $key);
$output[$key] = get_field($key, $term->taxonomy . "_" . $term->term_id);
continue;
}
$output[$key] = $term->$key;
}
return $output;
}
private function processArgs(array $args) : array
{
// wordpress default
@@ -1413,19 +1495,51 @@ class Site
}
}
private function processHooks()
private function processActions()
{
if (!isset($this->config["hooks"]) || !is_array($this->config["hooks"])) {
if (!isset($this->config["actions"]) || !is_array($this->config["actions"])) {
return;
}
$defaultPriorities = [
'woocommerce_before_main_content' => 5,
'woocommerce_after_main_content' => 50,
];
foreach ($this->config["hooks"] as $hookName => $callback) {
$priority = $defaultPriorities[$hookName] ?? 99;
add_action($hookName, $callback, $priority);
foreach ($this->config["actions"] as $hookName => $callback) {
if (is_array($callback) && array_is_list($callback)) {
$priority = $callback[1];
$callback = $callback[0];
add_action($hookName, $callback, $priority);
continue;
}
if (is_array($callback) && isset($callback["hook"]) && isset($callback["callback"])) {
add_action($callback["hook"], $callback["callback"], $callback["priority"] ?? 99);
continue;
}
add_action($hookName, $callback, 99);
}
}
/**
* parseArgs
* Used in conjuction with the handlebars helpers to grab all the different args
*
* @param string $args
* @return array
*/
public function parseArgs(string $args): array
{
$parsed = [];
// Match key="value", key='value', or key=value
preg_match_all('/(\w+)=(".*?"|\'.*?\'|\S+)/', $args, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$key = $match[1];
$value = $match[2];
// Strip quotes if present
$value = trim($value, "\"'");
$parsed[$key] = $value;
}
return $parsed;
}
}