Files
cannaiq/wordpress-plugin/widgets/product-image-overlay.php
Kelly c33ed1cae9
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
feat: CannaiQ Menus WordPress Plugin v2.0.0 - Modular Component Library
New modular component widgets:
- Discount Ribbon (ribbon/pill/text styles)
- Strain Badge (Sativa/Indica/Hybrid colored pills)
- THC/CBD Meter (progress bars or badges)
- Effects Display (styled chips with icons)
- Price Block (original + sale price)
- Cart Button (styled CTA linking to menu)
- Stock Indicator (in/out of stock badges)
- Product Image + Badges (image with overlays)

New card template:
- Premium Product Card (ready-to-use template)

Extended dynamic tags (30+ total):
- Discount %, Strain Badge, THC/CBD Badge
- Effects Chips, Terpenes, Price Display
- Menu URL, Stock Status, and more

New files:
- assets/css/components.css
- includes/effects-icons.php (SVG icons)
- 10 new widget files
- dynamic-tags-extended.php

Branding updated to "CannaiQ" throughout.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 00:21:40 -07:00

391 lines
13 KiB
PHP

<?php
/**
* CannaIQ Product Image Overlay Widget
*
* Displays product image with positioned badge overlays.
*
* @package CannaIQ_Menus
* @since 2.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
class CannaIQ_Product_Image_Overlay_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'cannaiq_product_image_overlay';
}
public function get_title() {
return __('Product Image + Badges', 'cannaiq-menus');
}
public function get_icon() {
return 'eicon-image';
}
public function get_categories() {
return ['cannaiq'];
}
public function get_keywords() {
return ['image', 'product', 'photo', 'overlay', 'badges', 'cannaiq'];
}
protected function register_controls() {
$this->start_controls_section(
'content_section',
[
'label' => __('Image', 'cannaiq-menus'),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
$this->add_control(
'image_source',
[
'label' => __('Image Source', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'auto',
'options' => [
'auto' => __('Auto (from product)', 'cannaiq-menus'),
'custom' => __('Custom image', 'cannaiq-menus'),
],
]
);
$this->add_control(
'custom_image',
[
'label' => __('Choose Image', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::MEDIA,
'default' => [
'url' => \Elementor\Utils::get_placeholder_image_src(),
],
'condition' => [
'image_source' => 'custom',
],
]
);
$this->add_control(
'fallback_image',
[
'label' => __('Fallback Image', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::MEDIA,
'description' => __('Shown if product has no image', 'cannaiq-menus'),
]
);
$this->add_control(
'aspect_ratio',
[
'label' => __('Aspect Ratio', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => '1/1',
'options' => [
'1/1' => __('Square (1:1)', 'cannaiq-menus'),
'4/3' => __('4:3', 'cannaiq-menus'),
'3/4' => __('3:4', 'cannaiq-menus'),
'16/9' => __('16:9', 'cannaiq-menus'),
'auto' => __('Auto', 'cannaiq-menus'),
],
]
);
$this->add_control(
'hover_effect',
[
'label' => __('Hover Effect', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'zoom',
'options' => [
'none' => __('None', 'cannaiq-menus'),
'zoom' => __('Zoom', 'cannaiq-menus'),
],
]
);
$this->end_controls_section();
// Overlay Badges Section
$this->start_controls_section(
'overlays_section',
[
'label' => __('Badge Overlays', 'cannaiq-menus'),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
$this->add_control(
'show_discount_badge',
[
'label' => __('Show Discount Badge', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => __('Yes', 'cannaiq-menus'),
'label_off' => __('No', 'cannaiq-menus'),
'return_value' => 'yes',
'default' => 'yes',
]
);
$this->add_control(
'discount_position',
[
'label' => __('Discount Position', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'top-left',
'options' => [
'top-left' => __('Top Left', 'cannaiq-menus'),
'top-right' => __('Top Right', 'cannaiq-menus'),
'bottom-left' => __('Bottom Left', 'cannaiq-menus'),
'bottom-right' => __('Bottom Right', 'cannaiq-menus'),
],
'condition' => [
'show_discount_badge' => 'yes',
],
]
);
$this->add_control(
'show_strain_badge',
[
'label' => __('Show Strain Badge', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => __('Yes', 'cannaiq-menus'),
'label_off' => __('No', 'cannaiq-menus'),
'return_value' => 'yes',
'default' => 'yes',
]
);
$this->add_control(
'strain_position',
[
'label' => __('Strain Position', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'bottom-left',
'options' => [
'top-left' => __('Top Left', 'cannaiq-menus'),
'top-right' => __('Top Right', 'cannaiq-menus'),
'bottom-left' => __('Bottom Left', 'cannaiq-menus'),
'bottom-right' => __('Bottom Right', 'cannaiq-menus'),
],
'condition' => [
'show_strain_badge' => 'yes',
],
]
);
$this->add_control(
'show_thc_badge',
[
'label' => __('Show THC Badge', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => __('Yes', 'cannaiq-menus'),
'label_off' => __('No', 'cannaiq-menus'),
'return_value' => 'yes',
'default' => '',
]
);
$this->add_control(
'thc_position',
[
'label' => __('THC Position', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'bottom-right',
'options' => [
'top-left' => __('Top Left', 'cannaiq-menus'),
'top-right' => __('Top Right', 'cannaiq-menus'),
'bottom-left' => __('Bottom Left', 'cannaiq-menus'),
'bottom-right' => __('Bottom Right', 'cannaiq-menus'),
],
'condition' => [
'show_thc_badge' => 'yes',
],
]
);
$this->end_controls_section();
// Style Section
$this->start_controls_section(
'style_section',
[
'label' => __('Style', 'cannaiq-menus'),
'tab' => \Elementor\Controls_Manager::TAB_STYLE,
]
);
$this->add_control(
'border_radius',
[
'label' => __('Border Radius', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SLIDER,
'size_units' => ['px'],
'range' => [
'px' => [
'min' => 0,
'max' => 50,
],
],
'default' => [
'size' => 8,
],
'selectors' => [
'{{WRAPPER}} .cannaiq-product-image' => 'border-radius: {{SIZE}}{{UNIT}};',
],
]
);
$this->add_control(
'background_color',
[
'label' => __('Background Color', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#f9fafb',
'selectors' => [
'{{WRAPPER}} .cannaiq-product-image' => 'background-color: {{VALUE}};',
],
]
);
$this->add_group_control(
\Elementor\Group_Control_Box_Shadow::get_type(),
[
'name' => 'box_shadow',
'selector' => '{{WRAPPER}} .cannaiq-product-image',
]
);
$this->end_controls_section();
}
protected function render() {
$settings = $this->get_settings_for_display();
// Get image URL
$image_url = '';
if ($settings['image_source'] === 'custom') {
$image_url = $settings['custom_image']['url'] ?? '';
} else {
global $cannaiq_current_product;
if (isset($cannaiq_current_product)) {
$image_url = $cannaiq_current_product['Image']
?? $cannaiq_current_product['images'][0]['url']
?? $cannaiq_current_product['image_url']
?? '';
}
}
// Use fallback if no image
if (empty($image_url) && !empty($settings['fallback_image']['url'])) {
$image_url = $settings['fallback_image']['url'];
}
// Get product name for alt text
$alt_text = '';
global $cannaiq_current_product;
if (isset($cannaiq_current_product)) {
$alt_text = $cannaiq_current_product['Name'] ?? $cannaiq_current_product['name'] ?? '';
}
// Aspect ratio style
$aspect_style = '';
if ($settings['aspect_ratio'] !== 'auto') {
$aspect_style = 'aspect-ratio: ' . $settings['aspect_ratio'] . ';';
}
// Hover class
$hover_class = $settings['hover_effect'] === 'zoom' ? 'cannaiq-product-image--hover-zoom' : '';
// Group badges by position
$badges_by_position = [];
if ($settings['show_discount_badge'] === 'yes') {
$badges_by_position[$settings['discount_position']][] = 'discount';
}
if ($settings['show_strain_badge'] === 'yes') {
$badges_by_position[$settings['strain_position']][] = 'strain';
}
if ($settings['show_thc_badge'] === 'yes') {
$badges_by_position[$settings['thc_position']][] = 'thc';
}
?>
<div class="cannaiq-product-image <?php echo esc_attr($hover_class); ?>" style="<?php echo esc_attr($aspect_style); ?>">
<?php if (!empty($image_url)): ?>
<img src="<?php echo esc_url($image_url); ?>" alt="<?php echo esc_attr($alt_text); ?>" />
<?php endif; ?>
<?php foreach ($badges_by_position as $position => $badges): ?>
<div class="cannaiq-product-image__overlay cannaiq-product-image__overlay--<?php echo esc_attr($position); ?>">
<div class="cannaiq-product-image__badges">
<?php foreach ($badges as $badge_type): ?>
<?php $this->render_badge($badge_type); ?>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php
}
private function render_badge($type) {
global $cannaiq_current_product;
switch ($type) {
case 'discount':
if (!isset($cannaiq_current_product)) return;
$original = $cannaiq_current_product['Prices'][0]
?? $cannaiq_current_product['regular_price']
?? null;
$sale = $cannaiq_current_product['specialPrice']
?? $cannaiq_current_product['sale_price']
?? null;
if ($original && $sale && $original > $sale) {
$percent = round((($original - $sale) / $original) * 100);
echo '<span class="cannaiq-discount-ribbon cannaiq-discount-ribbon--pill">' . esc_html($percent) . '% OFF</span>';
}
break;
case 'strain':
if (!isset($cannaiq_current_product)) return;
$strain = strtolower($cannaiq_current_product['strainType']
?? $cannaiq_current_product['strain_type']
?? '');
if (!empty($strain) && in_array($strain, ['sativa', 'indica', 'hybrid'])) {
$colors = [
'sativa' => '#22c55e',
'indica' => '#8b5cf6',
'hybrid' => '#f97316',
];
$color = $colors[$strain];
echo '<span class="cannaiq-strain-badge cannaiq-strain-badge--pill" style="background-color: ' . esc_attr($color) . '; color: white;">' . esc_html(strtoupper($strain)) . '</span>';
}
break;
case 'thc':
if (!isset($cannaiq_current_product)) return;
$thc = $cannaiq_current_product['THCContent']['range'][0]
?? $cannaiq_current_product['THC']
?? $cannaiq_current_product['thc_percentage']
?? null;
if ($thc !== null && $thc > 0) {
echo '<span class="cannaiq-potency-badge cannaiq-potency-badge--pill" style="background-color: #1f2937; color: white;">' . esc_html(number_format((float)$thc, 1)) . '% THC</span>';
}
break;
}
}
}