From: Stephen Cameron Date: Thu, 14 Jan 2021 21:36:16 +0000 (+0100) Subject: WIP #4064 @12 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=c7af3be2bc36be4e67f871fdfe9ccdc507f7fa9f;p=usines-reunies.git WIP #4064 @12 --- diff --git a/web/app/mu-plugins/cube/src/CPT/Realisation.php b/web/app/mu-plugins/cube/src/CPT/Realisation.php index 1015860..5a6541d 100644 --- a/web/app/mu-plugins/cube/src/CPT/Realisation.php +++ b/web/app/mu-plugins/cube/src/CPT/Realisation.php @@ -27,6 +27,10 @@ class Realisation { ->set_width(50) ->set_required(), + Field::make('text', 'thumbnail_caption', __('Description courte')) + ->set_help_text("Par exemple : « Hôtel, Lyon ». Affiché sous la vignette sur la page d'index") + ->set_required(), + Field::make('rich_text', 'description', __('Description', 'usines')) ->set_required(), @@ -48,6 +52,19 @@ class Realisation { Field::make('text', 'testimonial_author', __('Auteur du témoignage', 'usines')), ]); + + // Add special options page for extra content on Réalisations index page + Container::make('theme_options', __("Réalisations : options de la page d'index")) + ->set_page_menu_title("Page d'index") + ->set_page_parent('edit.php?post_type=realisation') // Add to the "Réalisations" CPT menu + ->add_fields([ + + Field::make('text', 'realisations_title', __("Titre de la page d'index des réalisations")) + ->set_default_value('Réalisations'), + + Field::make('textarea', 'realisations_intro_text', __("Texte d'introduction")) + ->set_help_text('Contenu affiché au début de la page Réalisations.') + ]); }); } diff --git a/web/app/mu-plugins/cube/src/Common/Setup.php b/web/app/mu-plugins/cube/src/Common/Setup.php index 7553d31..95a4237 100644 --- a/web/app/mu-plugins/cube/src/Common/Setup.php +++ b/web/app/mu-plugins/cube/src/Common/Setup.php @@ -13,7 +13,7 @@ class Setup { // Register Carbon Fields add_action('after_setup_theme', function () { - if (isset($_GET['action']) && $_GET['action'] !== 'elementor') { + if (!(is_admin() && isset($_GET['action']) && $_GET['action'] === 'elementor')) { Carbon_Fields::boot(); } }); diff --git a/web/app/mu-plugins/cube/src/Elementor/Setup.php b/web/app/mu-plugins/cube/src/Elementor/Setup.php index 5a1dbc7..0402f97 100644 --- a/web/app/mu-plugins/cube/src/Elementor/Setup.php +++ b/web/app/mu-plugins/cube/src/Elementor/Setup.php @@ -30,6 +30,7 @@ class Setup { $elementor->widgets_manager->register_widget_type( new Widgets\Circle() ); $elementor->widgets_manager->register_widget_type( new Widgets\ImageMap() ); $elementor->widgets_manager->register_widget_type( new Widgets\TeamGrid() ); + $elementor->widgets_manager->register_widget_type( new Widgets\Realisations() ); } diff --git a/web/app/mu-plugins/cube/src/Elementor/Widgets/Realisations.php b/web/app/mu-plugins/cube/src/Elementor/Widgets/Realisations.php new file mode 100644 index 0000000..93bced7 --- /dev/null +++ b/web/app/mu-plugins/cube/src/Elementor/Widgets/Realisations.php @@ -0,0 +1,105 @@ +start_controls_section( + 'section_content', + [ + 'label' => __( 'Réalisations', 'cube' ), + ] + ); + + $this->add_control( + 'widget_description', + [ + 'raw' => __( 'This widget will display the most recent réalisation posts.', 'cube' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + ] + ); + + $this->add_control( + 'posts_limit', + [ + 'label' => __( 'Number of posts to display', 'cube' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 10, + ] + ); + + $this->end_controls_section(); + + $this->common_controls(); + } + /** + * 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() { + + $posts_limit = $this->get_settings('posts_limit'); + + $realisation_posts = wp_get_recent_posts([ + 'numberposts' => $posts_limit, + 'orderby' => 'post_date', + 'order' => 'DESC', + 'post_type' => 'realisation', + 'post_status' => 'publish', + 'suppress_filters' => true + ]); + + if ($realisation_posts) { + foreach ($realisation_posts as $realisation_post) { + echo view('partials.content-realisation', compact('realisation_post')); + } + } + + } +} diff --git a/web/app/themes/Usines/app/View/Composers/Realisation.php b/web/app/themes/Usines/app/View/Composers/Realisation.php new file mode 100644 index 0000000..90b6568 --- /dev/null +++ b/web/app/themes/Usines/app/View/Composers/Realisation.php @@ -0,0 +1,48 @@ + $this->get_category(), + 'hero_image' => carbon_get_post_meta($postID, 'hero_image'), + 'description' => carbon_get_post_meta($postID, 'description'), + 'gallery' => carbon_get_post_meta($postID, 'gallery'), + 'testimonial' => carbon_get_post_meta($postID, 'testimonial'), + 'testimonial_author' => carbon_get_post_meta($postID, 'testimonial_author'), + ]; + } + + /** + * Returns the category name (there can only be one) + * + * @return string + */ + public function get_category() + { + // Since there can only be one category assigned, we can flatten the array + // out and return the category if it exists (ref: https://stackoverflow.com/a/8131148) + return reset(get_the_terms(get_the_ID(), 'realisation_category')); + } +} diff --git a/web/app/themes/Usines/app/View/Composers/Realisations.php b/web/app/themes/Usines/app/View/Composers/Realisations.php new file mode 100644 index 0000000..d4d3ea6 --- /dev/null +++ b/web/app/themes/Usines/app/View/Composers/Realisations.php @@ -0,0 +1,54 @@ + carbon_get_theme_option('realisations_title'), + 'intro' => carbon_get_theme_option('realisations_intro_text'), + 'footer' => $this->get_footer(), + ]; + } + + /** + * Returns the footer section from the Elementor template library. + * + * @return string + */ + public function get_footer() + { + // Fetch content from Elementor template named 'realisations-footer' + $page = get_page_by_path('realisations-footer', OBJECT, 'elementor_library'); + $content = ''; + + if ($page->ID) { + $content = ElementorPlugin::instance()->frontend->get_builder_content_for_display($page->ID); + } + + return $content; + } +} diff --git a/web/app/themes/Usines/app/filters.php b/web/app/themes/Usines/app/filters.php index e72f782..16a0679 100644 --- a/web/app/themes/Usines/app/filters.php +++ b/web/app/themes/Usines/app/filters.php @@ -14,3 +14,31 @@ namespace App; add_filter('excerpt_more', function () { return ' … ' . __('Continued', 'sage') . ''; }); + +/** + * When calling wp_list_categories(), WordPress doesn't apply the .current-cat class to the "Show All" categories link. + * It is also missing the .cat-item class, which makes styling more awkward, so both these need to be added... + * Ref: https://wordpress.stackexchange.com/a/331859 + */ +add_filter('wp_list_categories', function ( $output, $args ) { + if ( array_key_exists( 'show_option_all', $args ) && $args['show_option_all'] ) { + if ( ! array_key_exists( 'current_category', $args ) || $args['current_category'] ) { + if ( is_category() || is_tax() || is_tag() ) { + if ( ! array_key_exists( 'taxonomy', $args ) ) { + $args['taxonomy'] = 'category'; + } + $current_term_object = get_queried_object(); + if ( $args['taxonomy'] !== $current_term_object->taxonomy ) { + $output = str_replace( "class='cat-item-all'", "class='cat-item-all current-cat'", $output ); + } + } else { + $output = str_replace( "class='cat-item-all'", "class='cat-item-all current-cat'", $output ); + } + } + } + + // Add missing 'cat-item' class before cat-item-all + $output = str_replace( "class='cat-item-all", "class='cat-item cat-item-all", $output ); + + return $output; +}, 10, 2 ); diff --git a/web/app/themes/Usines/app/setup.php b/web/app/themes/Usines/app/setup.php index cbaca46..2063b6d 100755 --- a/web/app/themes/Usines/app/setup.php +++ b/web/app/themes/Usines/app/setup.php @@ -8,6 +8,54 @@ namespace App; use function Roots\asset; +/** + * Override Elementor CSS order + * Normally, the Sage CSS is the last to load but we need Elementor's post specific CSS + * to load after the theme so it can override things like the padding. Elementor's default + * frontend CSS contains 10px padding on all populated elements, which messes with our + * designs so we override this in our theme. However, to override it, we are forced to use a + * CSS selector with high specificity in our theme's CSS: + * .elementor-column-gap-default > .elementor-row > .elementor-column > .elementor-element-populated + * When Elementor generates its post-xx.css files for custom styling set on a page, the specificity + * of this CSS rule is the same as ours so it can never override ours unless it is the last stylesheet + * to load. The desired loading order is: + * 1) Elementor Global / Frontend CSS + * 2) Sage Main CSS (Theme CSS) + * 3) Elementor post specific CSS (post-xx.css) + * + * Elementor has a hook when registering CSS files - https://code.elementor.com/hooks/elementor-css-file-name-enqueue/ + * However, I couldn't get this to work properly with add_action and we need to use the post ID (get_the_ID()), which + * doesn't always seem to be available. I need to figure out where this hook should be added so that it is ready... + * + * For now, the solution is quite low-level and we inject a dependency of our theme's 'sage/app' CSS file into + * Elementor's post specific CSS file just before the stylesheets are printed. By having our theme as a dependency, + * Elementor's post-xx.css file is forced to load after the theme's CSS... + * + * TODO: revisit this and see if there's a better solution. See issue here: https://github.com/elementor/elementor/issues/7658 + */ +add_action('wp_print_styles', function() { + global $wp_styles; + $sage_css_handle = 'sage/app'; + $elementor_css_handle = 'elementor-post-'. get_the_ID(); + + // First, check if Elementor styles are loaded yet and if not, load them because we need to be able + // to make them a dependency of the main theme styles in order to have the correct CSS order... + // If we don't do this, on some pages that load Elementor late (eg. index.blade.php), the Elementor CSS + // will be loaded when fetching the builder content but it will be in the footer due to the late call. + // Although not perfect, it's easiest to always load the Elementor frontend CSS even if it's not strictly needed. + if (!isset($wp_styles->registered['elementor-frontend'])) { + \Elementor\Plugin::instance()->frontend->enqueue_styles(); + } + + // Add the theme's main CSS as a dependency for the post-specific CSS in order to get the + // theme CSS to output first, thereby allowing it to be overridden by the post specific CSS. + if (isset($wp_styles->registered[$elementor_css_handle])) { + $wp_styles->registered[$elementor_css_handle]->deps[] = $sage_css_handle; + } + +}); + + /** * Register the theme assets. * @@ -33,7 +81,15 @@ add_action('wp_enqueue_scripts', function () { wp_enqueue_script('comment-reply'); } - wp_enqueue_style('sage/app.css', asset('styles/app.css')->uri(), false, null); + // Ensure Elementor CSS is loaded before theme CSS (see above) + $styles = ['styles/app.css']; + + foreach ($styles as $stylesheet) { + if (asset($stylesheet)->exists()) { + wp_enqueue_style('sage/' . basename($stylesheet, '.css'), asset($stylesheet)->uri(), ['elementor-frontend'], null); + } + } + }, 100); /** diff --git a/web/app/themes/Usines/config/poet.php b/web/app/themes/Usines/config/poet.php index ef9ac22..a6fd6ef 100644 --- a/web/app/themes/Usines/config/poet.php +++ b/web/app/themes/Usines/config/poet.php @@ -44,6 +44,7 @@ return [ 'labels' => [ 'singular' => 'Catégorie', 'plural' => 'Catégories', + 'slug' => 'categories-realisation', ], ], ], diff --git a/web/app/themes/Usines/package.json b/web/app/themes/Usines/package.json index 89a6c2e..e2b0810 100644 --- a/web/app/themes/Usines/package.json +++ b/web/app/themes/Usines/package.json @@ -34,6 +34,7 @@ "babel-loader": "^8.2.1", "browser-sync": "^2.26.12", "browser-sync-webpack-plugin": "^2.0.1", + "columns.js": "^1.1.2", "cross-env": "^7.0.2", "eslint": "^7.7.0", "eslint-plugin-import": "^2.22.0", diff --git a/web/app/themes/Usines/resources/assets/scripts/masonry-columns.js b/web/app/themes/Usines/resources/assets/scripts/masonry-columns.js new file mode 100644 index 0000000..66a0b44 --- /dev/null +++ b/web/app/themes/Usines/resources/assets/scripts/masonry-columns.js @@ -0,0 +1,28 @@ +//-- Lightweight Masonry Library Setup +import Masonry from 'columns.js'; // https://github.com/mladenilic/columns.js + +const debounce = (callback, wait) => { + let timeout; + + return () => { + const context = this, args = arguments; + clearTimeout(timeout); + timeout = setTimeout(() => { + timeout = null; + callback.apply(context, args); + }, wait); + }; +}; + +// Auto-trigger for any elements carrying the data-masonry-columns attribute +// Settings come from JSON in the data attribute of the element to make this more flexible +document.querySelectorAll('[data-masonry-columns]') + .forEach(function(grid) { + let masonry = new Masonry(grid, JSON.parse(grid.dataset.masonryColumns)); + + // Update masonry columns when resizing + window.addEventListener('resize', debounce(() => { + masonry.render(); + }, 50)); + }); + diff --git a/web/app/themes/Usines/resources/assets/styles/app.styl b/web/app/themes/Usines/resources/assets/styles/app.styl index cfd6695..4b56453 100644 --- a/web/app/themes/Usines/resources/assets/styles/app.styl +++ b/web/app/themes/Usines/resources/assets/styles/app.styl @@ -12,7 +12,7 @@ @import 'common/layout' @import 'components/*' @import 'widgets/*' -//@import 'pages/*' +@import 'pages/*' // Allow spacing classes to override others defined here @import 'common/spacing' diff --git a/web/app/themes/Usines/resources/assets/styles/common/global.styl b/web/app/themes/Usines/resources/assets/styles/common/global.styl index 865e544..48c1acc 100644 --- a/web/app/themes/Usines/resources/assets/styles/common/global.styl +++ b/web/app/themes/Usines/resources/assets/styles/common/global.styl @@ -12,6 +12,9 @@ body a @apply transition-colors unquote('hover:text-red') +b, strong + font-weight: bold + img .rounded-full & // So this can be applied to parent element and also to override Elementor default CSS border-radius: 999px diff --git a/web/app/themes/Usines/resources/assets/styles/components/masonry-columns.styl b/web/app/themes/Usines/resources/assets/styles/components/masonry-columns.styl new file mode 100644 index 0000000..aa4484b --- /dev/null +++ b/web/app/themes/Usines/resources/assets/styles/components/masonry-columns.styl @@ -0,0 +1,17 @@ +// Columns.js Masonry +$masonry-max-columns = 3 // Max number of columns to support +$masonry-column-gap = 5.88% // Desired gap between columns (based on container width) + +[data-columns] + display: flex + justify-content: space-between + +for columns in range(1, $masonry-max-columns) + [data-columns=\"{columns}\"] > * + // Calculate how wide the columns should be based on number of columns + // and also subtract enough to leave the correct column gaps. + // There will be (columns - 1) gaps so the amount to reduce each column by is gaps / columns * gap-size + // Flexbox's justify-content: space-between will make the gaps even with from leftover space + //flex-basis: (100% / columns - ($masonry-column-gap * (columns - 1) / columns)) + flex-basis: s('calc(100% / %d - (%s * %d / %d))', columns, $masonry-column-gap, columns - 1, columns) + //flex-basis: s('calc(100% / %d)', columns) diff --git a/web/app/themes/Usines/resources/assets/styles/pages/realisations.styl b/web/app/themes/Usines/resources/assets/styles/pages/realisations.styl new file mode 100644 index 0000000..1f00002 --- /dev/null +++ b/web/app/themes/Usines/resources/assets/styles/pages/realisations.styl @@ -0,0 +1,41 @@ +.realisations + &-categories + margin-top: -1rem // Offset margin for internal row spacing when list wraps + + li + display: inline-block + margin-top: 1rem + + &:not(:last-child) // Spacing between items + margin-right: 2.5rem + + &.current-cat a:after // Rotate arrow down when current category + transform: rotate(45deg) + transform-origin: 75% 75% + + a + position: relative + + // Circle arrow icon (>) + &:before // Circle ( ) + content: '' + display: inline-block + width: 2em + height: @width + vertical-align: middle + margin-right: 0.5em + border-radius: 50% + border: 2px solid + + &:after // Arrow > + content: '' + width: 0.5625em + height: @width + border-style: solid + border-width: 0 2px 2px 0 + transform: rotate(-45deg) + position: absolute + left: 0.6em + top: 0.4em + + diff --git a/web/app/themes/Usines/resources/views/archive-realisation.blade.php b/web/app/themes/Usines/resources/views/archive-realisation.blade.php index a2cb98b..8a5d641 100644 --- a/web/app/themes/Usines/resources/views/archive-realisation.blade.php +++ b/web/app/themes/Usines/resources/views/archive-realisation.blade.php @@ -1,29 +1,59 @@ +{{-- Réalisations Index Page --}} +{{-- See app/View/Composers/Realisations.php for data sources --}} @extends('layouts.app') @section('content') - -
- - {{-- TODO ! - - @if (! have_posts()) - @alert(['type' => 'warning']) - {{ __('Sorry, no results were found.', 'sage') }} - @endalert - - {!! get_search_form(false) !!} - @endif - - @while (have_posts()) @php(the_post()) - @includeFirst(['partials.content-'.get_post_type(), 'partials.content']) - @endwhile + @php + $settings = [ + 'columns' => 3, + 'algorithm' => 'chronological', + 'breakpoints' => [ // min-width => number of columns + 1100 => 3, + 516 => 2, + 0 => 1, + ], + ]; + + $masonry = json_encode($settings); + @endphp + + +
+

{{ $title }}

+ +
+ {{-- Inset background layer --}} +
+ + {{-- Intro text that is stored in theme's custom options (see Réalisations menu in dashboard) --}} +
{{ $intro }}
+ + {{-- Réalisation Categories --}} +
    + {!! + wp_list_categories([ + 'echo' => 0, + 'show_option_all' => __('Tout voir'), // See app/filters.php for extra treatment to add current class + 'style' => 'list', + 'taxonomy' => 'realisation_category', + 'title_li' => '', + ]) !!} +
+ +
+ @while (have_posts()) @php(the_post()) + @includeFirst(['partials.content-'.get_post_type(), 'partials.content']) + @endwhile +
{!! get_the_posts_navigation() !!} - --}} - +
+ + {!! $footer !!} + @endsection @section('sidebar') diff --git a/web/app/themes/Usines/resources/views/partials/content-realisation.blade.php b/web/app/themes/Usines/resources/views/partials/content-realisation.blade.php new file mode 100644 index 0000000..5b5a81e --- /dev/null +++ b/web/app/themes/Usines/resources/views/partials/content-realisation.blade.php @@ -0,0 +1,14 @@ +@php + // Note: since this template is used by the standard WordPress loop AND by the Elementor widget, which + // exists outside the loop, we are sometimes getting data that is passed in and other times relying on + // the global loop context. As a result, the code had to be modified a bit to work in both situations. + $postID = isset($realisation_post) ? $realisation_post['ID'] : $post->ID; + $thumbnail = carbon_get_post_meta($postID, 'thumbnail'); + $thumbnail_image = $thumbnail ? wp_get_attachment_image($thumbnail, 'large') : ''; + $thumbnail_caption = carbon_get_post_meta($postID, 'thumbnail_caption'); +@endphp + + diff --git a/web/app/themes/Usines/resources/views/partials/content-single-elementor_library.blade.php b/web/app/themes/Usines/resources/views/partials/content-single-elementor_library.blade.php new file mode 100644 index 0000000..365b8e1 --- /dev/null +++ b/web/app/themes/Usines/resources/views/partials/content-single-elementor_library.blade.php @@ -0,0 +1,22 @@ +
+
+

+ {!! $title !!} +
+ ID: {{ get_post_field('post_name') }} +

+ {{-- @include('partials/entry-meta')--}} +
+ +
+ @php(the_content()) +
+ + {{-- +
+ {!! wp_link_pages(['echo' => 0, 'before' => '']) !!} +
+ + @php(comments_template()) + --}} +
diff --git a/web/app/themes/Usines/resources/views/partials/content-single-realisation.blade.php b/web/app/themes/Usines/resources/views/partials/content-single-realisation.blade.php new file mode 100644 index 0000000..dd5f0bd --- /dev/null +++ b/web/app/themes/Usines/resources/views/partials/content-single-realisation.blade.php @@ -0,0 +1,28 @@ +{{-- Réalisation Detail Page --}} +{{-- See app/View/Composers/Realisation.php for data sources --}} +
+
+

{!! $title !!}

+ {{-- @include('partials/entry-meta')--}} +
{!! $category->name !!}
+
+ +
+
{{-- Inset light background --}}
+ + {{-- Just the URL: @image($hero_image, 'raw') --}} + @image($hero_image, 'full', ['class' => 'block mx-auto mb-1v']) + +
{!! nl2br($description) !!}
+ + @dump($gallery) + +
+ + {{-- +
+ {!! wp_link_pages(['echo' => 0, 'before' => '']) !!} +
+ --}} + +
diff --git a/web/app/themes/Usines/resources/views/single.blade.php b/web/app/themes/Usines/resources/views/single.blade.php index 849a82f..dc77f82 100644 --- a/web/app/themes/Usines/resources/views/single.blade.php +++ b/web/app/themes/Usines/resources/views/single.blade.php @@ -2,7 +2,7 @@ @section('content') -
+
@while(have_posts()) @php(the_post()) @includeFirst(['partials.content-single-' . get_post_type(), 'partials.content-single']) diff --git a/web/app/themes/Usines/resources/views/taxonomy-realisation_category.blade.php b/web/app/themes/Usines/resources/views/taxonomy-realisation_category.blade.php new file mode 100644 index 0000000..289d37b --- /dev/null +++ b/web/app/themes/Usines/resources/views/taxonomy-realisation_category.blade.php @@ -0,0 +1,2 @@ +{{-- Re-use the Réalistions archive page for the filtered categories --}} +@include('archive-realisation') diff --git a/web/app/themes/Usines/webpack.mix.js b/web/app/themes/Usines/webpack.mix.js index 736b33d..c720b5c 100644 --- a/web/app/themes/Usines/webpack.mix.js +++ b/web/app/themes/Usines/webpack.mix.js @@ -41,6 +41,7 @@ mix.stylus('resources/assets/styles/admin.styl', 'styles'); mix .js('resources/assets/scripts/app.js', 'scripts') .js('resources/assets/scripts/intro-carousel.js', 'scripts') + .js('resources/assets/scripts/masonry-columns.js', 'scripts') .js('resources/assets/scripts/image-map.js', 'scripts') .js('resources/assets/scripts/customizer.js', 'scripts') .blocks('resources/assets/scripts/editor.js', 'scripts') diff --git a/web/app/themes/Usines/yarn.lock b/web/app/themes/Usines/yarn.lock index a44c4c1..e84b3b0 100644 --- a/web/app/themes/Usines/yarn.lock +++ b/web/app/themes/Usines/yarn.lock @@ -3199,6 +3199,11 @@ colorette@^1.2.1: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== +columns.js@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/columns.js/-/columns.js-1.1.2.tgz#079c18d173fee71c47bc6904414f243a18f31c0c" + integrity sha512-ipdHiOn+ZcHH0WeRFywQIskoFzZd4CuUYRkcqX3Q3ry/nQpQeRToZ7bYyhAEXE+nU16umLvG7zjGjh/dQlfUKw== + commander@2.17.x: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"