public function register_customisations() {
- // ToDo: customise heading widget to add prefix_class for the alignment in order to control position of underline when heading is centred vs left aligned
+ // Extend Elementor's heading widget to add a CSS class based on the alignment setting so we can also control the position of the underline
+ add_action( 'elementor/element/heading/section_title/before_section_end', function( $element, $args ) {
+ $elementor = Plugin::instance();
+
+ // Get the align control for updating
+ $control_data = $elementor->controls_manager->get_control_from_stack( $element->get_name(), 'align' );
+
+ if (is_wp_error($control_data)) {
+ return;
+ }
+
+ // Add a prefix class so heading will have correct class applied when alignment is changed
+ $control_data['prefix_class'] = 'heading-align-';
+ $element->update_control( 'align', $control_data );
+ }, 10, 2);
+
+ // Todo: consider add a control to the existing heading widget that allows the underline to be disabled (via prefix_class?)
}
$elementor->widgets_manager->register_widget_type( new Widgets\TextBlock() );
$elementor->widgets_manager->register_widget_type( new Widgets\HeroBlock() );
+ $elementor->widgets_manager->register_widget_type( new Widgets\ProfileGrid() );
$elementor->widgets_manager->register_widget_type( new Widgets\BackgroundImage() );
}
--- /dev/null
+<?php
+
+namespace PhysioAssist\Elementor\Widgets;
+
+use Elementor\Widget_Base;
+use Elementor\Controls_Manager;
+use Elementor\Utils;
+
+
+class ProfileGrid extends Widget_Base {
+
+ protected $_has_template_content = false; // Tell Elementor that content is all rendered dynamically
+
+ // Widget name / ID
+ public function get_name() {
+ return 'cube-profile-grid';
+ }
+
+ // Elementor widget title
+ public function get_title() {
+ return __( 'Profile Grid', 'cube' );
+ }
+
+ // Elementor interface icon
+ public function get_icon() {
+ return 'eicon-archive-posts';
+ }
+
+ // Where to display the widget in the Elementor interface
+ public function get_categories() {
+ return [ 'theme-elements' ];
+ }
+
+ /**
+ * List of scripts the widget depends on.
+ * Used to set scripts dependencies required to run the widget.
+ *
+ * @since 1.0.0
+ * @access public
+ * @return array Widget scripts dependencies.
+ */
+ public function get_script_depends() {
+ return [];
+ }
+ /**
+ * Register the widget controls.
+ * Adds different input fields to allow the user to change and customize the widget settings.
+ *
+ * @since 1.0.0
+ * @access protected
+ */
+ protected function _register_controls() {
+
+ $this->start_controls_section(
+ 'section_content',
+ [
+ 'label' => __( 'Profile Grid', 'cube' ),
+ ]
+ );
+
+ $this->add_control(
+ 'items',
+ [
+ 'label' => __( 'Items', 'cube' ),
+ 'type' => Controls_Manager::REPEATER,
+ 'fields' => [
+ [
+ 'name' => 'image',
+ 'label' => __('Image', 'cube'),
+ 'label_block' => true,
+ 'type' => Controls_Manager::MEDIA,
+ 'default' => [
+ 'url' => Utils::get_placeholder_image_src(),
+ ],
+ ],
+ [
+ 'name' => 'title',
+ 'label' => __( 'Title', 'cube' ),
+ 'type' => Controls_Manager::TEXT,
+ 'label_block' => true,
+ ],
+ [
+ 'name' => 'subtitle',
+ 'label' => __( 'Subtitle', 'cube' ),
+ 'type' => Controls_Manager::TEXT,
+ 'label_block' => true,
+ ],
+ [
+ 'name' => 'body',
+ 'label' => __('Body', 'cube'),
+ 'type' => Controls_Manager::WYSIWYG,
+ 'default' => '',
+ ],
+ [
+ 'name' => 'cta_text',
+ 'label' => __('Call to Action text', 'cube'),
+ 'type' => Controls_Manager::TEXT,
+ 'default' => ''
+ ],
+ [
+ 'name' => 'cta_link',
+ 'label' => __('Call to Action link', 'cube'),
+ 'type' => Controls_Manager::URL,
+ 'default' => [
+ 'url' => '',
+ 'is_external' => false,
+ ],
+ 'show_external' => true
+ ],
+ ],
+ 'title_field' => '{{{ title }}}',
+ ]
+ );
+ $this->end_controls_section();
+ }
+ /**
+ * Render the widget output on the frontend.
+ * Written in PHP and used to generate the final HTML.
+ *
+ * @since 1.0.0
+ * @access protected
+ */
+ protected function render() {
+
+ $items = $this->get_settings('items');
+
+ $html = \App\template('widgets/profile-grid', compact('items'));
+
+ echo '<div class="profile-grid">'. $html .'</div>';
+ }
+
+}
return "<?= " . __NAMESPACE__ . "\\asset_path({$asset}); ?>";
});
+ /**
+ * Create @image() Blade directive
+ */
+ sage('blade')->compiler()->directive('image', function ($expression) {
+
+ if (empty($expression)) return '';
+
+ // Set default extra parameters (size, attributes)
+ // Not all items will always be passed...
+ // Ref: http://php.net/manual/en/function.list.php#113189
+ // Note: This is extra weird because we're generating PHP code
+ // strings that will be executed later so we can't use variables normally...
+ $defaults = [
+ // 0 index is not included because it will always be set at this point
+ 1 => "'full'", // size
+ 2 => "[]", // attributes
+ ];
+
+ list($id, $size, $attributes) = array_replace($defaults, explode(', ', $expression));
+
+ return "<?php
+ if (isset($id) && !empty($id)) {
+ echo wp_get_attachment_image($id, $size, false, $attributes);
+ }
+ ?>";
+ });
+
/**
* Setup Blade SVG library
font-weight: 700
-.elementor-widget-heading
- .elementor-heading-title
- color: $colors.headings
- font-family: $font
- font-size: $font-size-large
- white-space: pre-wrap
- +below('large')
- font-size: $font-size-medium
+.elementor-heading-title
+ color: $colors.headings
+ font-family: $font
+ font-size: $font-size-large
+ white-space: pre-wrap
+
+ +below('large')
+ font-size: $font-size-medium
+
+ &:after
+ content: ''
+ display: block
+ margin-top: 0.9em
+ width: 1.88em
+ height: 3px
+ background-color: currentColor
+
+ // Center underline
+ .heading-align-center &
+ margin-left: auto
+ margin-right: auto
- &:after
- content: ''
- display: block
- margin: 0.9em auto 0
- width: 1.88em
- height: 3px
- background-color: currentColor
+ // Right-align underline
+ .heading-align-right &
+ margin-left: auto
--- /dev/null
+.profile-grid
+ display: flex
+ flex-wrap: wrap
+ justify-content: space-between
+ constrain(margin-bottom, -5vw) // Counteract space between rows for the last row
+
+ &-item
+ display: flex
+ flex: 0 1 45%
+ constrain(margin-bottom, 5vw)
+
+ &-image
+ align-self: flex-start
+ width: 35%
+
+ &-text
+ constrain(padding-left, 3.5vw) // Gutter between image and text
+
+ &-title
+ color: $colors.headings
+ font-size: 20px
+ font-weight: 600
+ text-transform: uppercase
+ margin-bottom: 0.8em
+ line-height: 1.2
+
+ &-subtitle
+ color: $colors.sub-headings
+ font-size: 14px
+ font-weight: 600
+ text-transform: uppercase
+ margin-bottom: 1em
+
+ &-body
+ color: $colors.text
+ font-size: 16px
+
+ &-cta
+ display: block
+ color: $colors.headings
+ font-size: 14px
+ font-weight: 600
+ text-transform: uppercase
+ margin-top: 1.4em
+
+ &:hover
+ color: $colors.light-blue
+
+ svg
+ width: 6px
+ height: 10px
+ display: inline-block
+ margin-right: 4px
+
+ path
+ fill: currentColor
--- /dev/null
+@foreach ($items as $item)
+
+ <div class="profile-grid-item">
+ @image($item['image']['id'], 'full', ['class' => 'profile-grid-image'])
+
+ <div class="profile-grid-text">
+
+ <h3 class="profile-grid-title">{{ $item['title'] }}</h3>
+
+ @if ($item['subtitle'])
+ <h4 class="profile-grid-subtitle">{{ $item['subtitle'] }}</h4>
+ @endif
+
+ <div class="profile-grid-body">
+ {!! $item['body'] !!}
+ </div>
+
+ @if (!empty($item['cta_text']) && !empty($item['cta_link']['url']))
+ <a href="{{ $item['cta_link']['url'] }}"
+ class="profile-grid-cta"
+ @if ($item['cta_link']['is_external'])
+ target="_blank" rel="noopener"
+ @endif
+ >
+ @svg('arrow')
+ {{ $item['cta_text'] }}
+ </a>
+ @endif
+
+ </div>
+ </div>
+
+@endforeach
+
+{{--<pre>--}}
+ {{--@php(print_r($items))--}}
+{{--</pre>--}}