Skip to content

Commit

Permalink
Move Layout Builder to Core (#2223)
Browse files Browse the repository at this point in the history
This PR addresses #1921 

It adds a new Layout, called Layout Builder, that has dynamic rows for
the fields.

💾 [Build
file](https://www.dropbox.com/scl/fi/zh4uqp6yidb1zkqqs98bh/gravityview-2.32-33bacbeef.zip?rlkey=w47ub1vhuj0pwetbjwmpb99w3&dl=1)
(33bacbe).
mrcasual authored Dec 12, 2024
2 parents 26370fd + a42ad3b commit 3f0f199
Showing 23 changed files with 611 additions and 6 deletions.
2 changes: 1 addition & 1 deletion assets/css/admin-entries-list.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion assets/css/admin-global.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/css/admin-views.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions assets/js/admin-views.js
Original file line number Diff line number Diff line change
@@ -2558,10 +2558,10 @@
revert: 75,
connectWith: ".active-drop-field",
start: function( event, ui ) {
$( panel ).find( ".active-drop-container-field" ).addClass('is-receivable');
$( document.body ).find( ".active-drop-container-field" ).addClass('is-receivable');
},
stop: function( event, ui ) {
$( panel ).find( ".active-drop-container-field" ).removeClass('is-receivable');
$( document.body ).find( ".active-drop-container-field" ).removeClass('is-receivable');
},
change: function( event, ui ) {
vcfg.setUnsavedChanges( true );
2 changes: 1 addition & 1 deletion assets/js/admin-views.min.js

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions future/includes/class-gv-template-entry-layout-builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
namespace GV;

/** If this file is called directly, abort. */
if ( ! defined( 'GRAVITYVIEW_DIR' ) ) {
die();
}

require_once 'trait-gv-field-renderer.php';

/**
* The single Entry template.
*
* @since $ver$
*/
final class Entry_Layout_Builder_Template extends Entry_Template {
use Field_Renderer_Trait;
/**
* {@inheritDoc}
*
* @since $ver$
*
* @var string
*/
public static $slug = \GravityView_Layout_Builder::ID;
}
1 change: 1 addition & 0 deletions future/includes/class-gv-template-entry.php
Original file line number Diff line number Diff line change
@@ -184,3 +184,4 @@ public function get_back_label( $do_replace = true ) {
require gravityview()->plugin->dir( 'future/includes/class-gv-template-entry-table.php' );
require gravityview()->plugin->dir( 'future/includes/class-gv-template-entry-list.php' );
require gravityview()->plugin->dir( 'future/includes/class-gv-template-entry-legacy.php' );
require gravityview()->plugin->dir( 'future/includes/class-gv-template-entry-layout-builder.php' );
27 changes: 27 additions & 0 deletions future/includes/class-gv-template-view-layout-builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
namespace GV;

/** If this file is called directly, abort. */
if ( ! defined( 'GRAVITYVIEW_DIR' ) ) {
die();
}

require_once 'trait-gv-field-renderer.php';

/**
* The View template.
*
* @since $ver$
*/
final class View_Layout_Builder_Template extends View_Template {
use Field_Renderer_Trait;

/**
* {@inheritDoc}
*
* @since $ver$
*
* @var string
*/
public static $slug = \GravityView_Layout_Builder::ID;
}
1 change: 1 addition & 0 deletions future/includes/class-gv-template-view.php
Original file line number Diff line number Diff line change
@@ -158,3 +158,4 @@ public function render() {
require gravityview()->plugin->dir( 'future/includes/class-gv-template-view-table.php' );
require gravityview()->plugin->dir( 'future/includes/class-gv-template-view-list.php' );
require gravityview()->plugin->dir( 'future/includes/class-gv-template-view-legacy.php' );
require gravityview()->plugin->dir( 'future/includes/class-gv-template-view-layout-builder.php' );
66 changes: 66 additions & 0 deletions future/includes/trait-gv-field-renderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
namespace GV;

/**
* Trait responsible for rendering a single field for an entry.
*
* @since $ver$
*/
trait Field_Renderer_Trait {
/**
* Output the field in the diy view.
*
* @param Field $field The field to output.
* @param Entry $entry The entry.
* @param array $extras Extra stuff, like wpautop, etc.
*
* @return string The field HTML.
*/
public function the_field( Field $field, Entry $entry, array $extras = [] ): string {
$form = $this->view->form;

$context = Template_Context::from_template( $this, compact( 'field', 'entry' ) );

$renderer = new Field_Renderer();
$source = is_numeric( $field->ID ) ? $this->view->form : new Internal_Source();

$value = $renderer->render( $field, $this->view, $source, $entry, $this->request );
$label = apply_filters(
'gravityview/template/field_label',
$field->get_label( $this->view, $form ),
$field->as_configuration(),
$form->form ?: null,
null,
);

/**
* @filter `gravityview/template/field/label` Override the field label.
* @since 2.0
*
* @param [in,out] string $label The label to override.
* @param \GV\Template_Context $context The context.
*/
$label = apply_filters( 'gravityview/template/field/label', $label, $context );

/**
* @filter `gravityview/template/table/entry/hide_empty`
*
* @param boolean $hide_empty Should the row be hidden if the value is empty? Default: don't hide.
* @param \GV\Template_Context $context The context ;) Love it, cherish it. And don't you dare modify it!
*/
$hide_empty = apply_filters(
'gravityview/render/hide-empty-zone',
Utils::get( $extras, 'hide_empty', $this->view->settings->get( 'hide_empty', false ) ),
$context,
);

$markup = '<div id="{{ field_id }}" class="{{ class }}">
{{ label }}
<div class="gv-grid-value">{{ value }}</div>
</div>';

$extras = array_merge( $extras, compact( 'hide_empty', 'value', 'label', 'markup' ) );

return \gravityview_field_output( $extras, $context );
}
}
Empty file.
161 changes: 161 additions & 0 deletions includes/presets/layout-builder/class-gravityview-layout-builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

use GV\Entry_Layout_Builder_Template;
use GV\Field_Collection;
use GV\Grid;
use GV\View_Layout_Builder_Template;

/**
* Registers the Layout Builder Layout.
*
* @since $ver$
*/
final class GravityView_Layout_Builder extends GravityView_Template {
/**
* The Layout ID.
*
* @since $ver$
*
* @var string
*/
public const ID = 'gravityview-layout-builder';

/**
* Creates the layout.
*
* @since $ver$
*/
public function __construct() {
$areas = Grid::prefixed(
self::ID,
static fn() => [ Grid::get_row_by_type( '100' ) ],
);

parent::__construct(
self::ID,
[
'slug' => self::ID,
'type' => 'custom',
'label' => __( 'Layout Builder', 'gk-gravityview' ),
'description' => __(
'Display items in customizable rows and columns.',
'gk-gravityview',
),
'css_source' => null,
'logo' => plugins_url( 'includes/presets/layout-builder/logo-layout-builder.svg', GRAVITYVIEW_FILE ),
],
[
'show_as_link' => [
'type' => 'checkbox',
'label' => esc_html__( 'Link to single entry', 'gk-gravityview' ),
'value' => false,
'context' => 'directory',
'priority' => 1190,
'group' => 'display',
],
],
$areas,
);

add_filter( 'gravityview/template/view/class', [ __CLASS__, 'get_view_class' ] );
add_filter( 'gravityview/template/entry/class', [ __CLASS__, 'get_view_class' ] );

add_filter( 'gk/gravityview/admin-views/view/is-dynamic', [ __CLASS__, 'make_dynamic' ], 5, 3 );
add_filter( 'gk/gravityview/admin-views/view/template/active-areas', [ __CLASS__, 'replace_active_areas' ], 5, 4 );

add_action( 'wp_enqueue_scripts', [ __CLASS__, 'register_style_assets' ] );
}

/**
* Returns {@see ViewTemplate} class name to be used as the View template class.
*
* @used-by `gravityview/template/view/class` filter.
*
* @since TBD
*
* @param string $template_class View template class.
*
* @return string The template class to use.
*/
public static function get_view_class( string $template_class ): string {
// GravityView expects the class to be in the "GV\View_<name>_Template" format.
$is_layout_builder_template = false !== stripos( $template_class, '_' . self::ID . '_' );
if ( ! $is_layout_builder_template ) {
return $template_class;
}

return stripos( $template_class, 'view_' ) !== false
? View_Layout_Builder_Template::class
: Entry_Layout_Builder_Template::class;
}

/**
* Returns the dynamic areas, stored in the fields array.
*
* @since $ver$
*
* @param array $areas The current areas.
* @param string $template_id The template ID.
* @param string $context The context / zone.
* @param array $fields The fields to render.
*
* @return array The rows with the active dynamic areas.
*/
public static function replace_active_areas(
array $areas,
string $template_id,
string $context,
array $fields
): array {
if ( self::ID !== $template_id ) {
return $areas;
}

$collection = Field_Collection::from_configuration( $fields );
$rows = Grid::prefixed(
self::ID,
static fn() => Grid::get_rows_from_collection( $collection, $context ),
);

return $rows ?: $areas;
}

/**
* Makes the field sections for the Layout Builder template sortable.
*
* @since $ver$
*
* @param bool $is_dynamic Whether it is dynamic.
* @param string $template_id The template ID.
* @param string $type The object type.
*
* @return bool Whether it is sortable
*/
public static function make_dynamic( bool $is_dynamic, string $template_id, string $type ): bool {
if (
self::ID !== $template_id
|| 'field' !== $type
) {
return $is_dynamic;
}

return true;
}

/**
* Registers the style assets for the View layout.
*
* @since $ver$
*/
public static function register_style_assets(): void {
$style = 'gravityview_style_' . self::ID;
wp_register_style(
$style,
plugin_dir_url( GRAVITYVIEW_FILE ) . 'templates/css/layout-builder.css',
[],
GV_PLUGIN_VERSION
);
}
}

new GravityView_Layout_Builder();
1 change: 1 addition & 0 deletions includes/presets/layout-builder/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?php // Silence is golden
1 change: 1 addition & 0 deletions includes/presets/layout-builder/logo-layout-builder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions includes/presets/register-default-templates.php
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ function gravityview_register_default_templates() {
include_once $path . 'resume-board/class-gravityview-preset-resume-board.php';
include_once $path . 'job-board/class-gravityview-preset-job-board.php';
include_once $path . 'event-listings/class-gravityview-preset-event-listings.php';
include_once $path . 'layout-builder/class-gravityview-layout-builder.php';
}


1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ Beautifully display your Gravity Forms entries. Learn more on [gravitykit.com](h
= develop =

#### 🚀 Added
* New Layout Builder View type for creating custom layouts with single or multi-column configurations and adjustable widths.
* `:initials` merge tag modifier for Name fields to display initials.

#### 🐛 Fixed
1 change: 1 addition & 0 deletions templates/css/layout-builder.css
8 changes: 8 additions & 0 deletions templates/css/source/layout-builder.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
$color-border: #E3E6EF;
$color-label: #565D67;
$color-primary: #0073AA;
$color-danger: #AA0000;
$padding: 16px;

@import 'layout-builder/edit_entry';
@import 'layout-builder/view_entry';
93 changes: 93 additions & 0 deletions templates/css/source/layout-builder/_edit_entry.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
.gv-edit-entry-wrapper .gform_wrapper {
border: 1px solid $color-border;
background: #FFF;
padding: $padding 0;
}

.gv-edit-entry-wrapper .gform_wrapper {
padding: $padding;

.gform-footer {
margin-top: $padding;
background-color: #F7FAFC;
padding: $padding $padding*2;
border-radius: 4px;

#publishing-action {
flex: 1;
}

input.button,
a.btn {
font-size: 1em;
line-height: 1.3em;
text-decoration: none;
display: inline-block;
border-radius: 4px;
padding: 14px 20px;
margin: 0;
border: 1px solid $color-border;
color: $color-primary;
cursor: pointer;

&:hover {
color: #005680;
border-color: #005680;;
}

&.button-primary {
background-color: $color-primary;
border-color: $color-primary;
color: #FFF;

&:hover {
background-color: #005680;
border-color: #005680;
}
}

&.btn-danger {
color: $color-danger;

&:hover {
background-color: $color-danger;
border-color: $color-danger;
color: #FFF;
}
}

&.gv-button-delete {
float: right;
}

&.gv-button-cancel {
margin-left: 5px;
border-color: transparent;
}
}
}

.gfield_label,
.gform-field-label--type-sub {
color: $color-label;
font-weight: normal;
}

.gfield_label {
margin-bottom: 3px;
}

label.gform-field-label--type-sub {
text-transform: uppercase;
font-size: .7em;
padding-left: 14px;
}

.ginput_container {
input {
border: 1px solid $color-border;
border-radius: 4px;
padding: 13px;
}
}
}
54 changes: 54 additions & 0 deletions templates/css/source/layout-builder/_view_entry.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.gv-layout-builder-view--entry {
border: 1px solid $color-border;
background: #FFF;
padding: $padding 0;

+ .gv-layout-builder-view--entry {
margin-top: -1px;
}

.gv-grid-row {
display: flex;

& + .gv-grid-row {
div[class^=gv-field-] {
margin-top: $padding;
border-top: 1px solid $color-border;
padding-top: $padding;
}
}
}

div[class^=gv-field-] {
.gv-field-label {
color: $color-label;
font-size: .8em;

+ * {
margin: 0;
}
}

p, ul, ol {
margin: 0;

& + p, ul, ol {
margin-top: 1em;
}
}

& + div[class^=gv-field-] {
margin-top: $padding;
border-top: 1px solid $color-border;
padding-top: $padding;
}
}

div[class^=gv-grid-col-] {
padding: 0 $padding;

& + div[class^=gv-grid-col-] {
border-left: 1px solid $color-border;
}
}
}
1 change: 1 addition & 0 deletions templates/css/source/layout-builder/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?php // Silence is golden
76 changes: 76 additions & 0 deletions templates/entries/gravityview-layout-builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php
/**
* @global Template_Context $gravityview
*/

use GV\Grid;
use GV\Template_Context;

if ( ! isset( $gravityview ) || empty( $gravityview->template ) ) {
gravityview()->log->error( '{file} template was loaded without context', [ 'file' => __FILE__ ] );

return;
}

gravityview_before( $gravityview );

ob_start();
gravityview_header( $gravityview );

$zone = 'single';
$rows = Grid::prefixed(
GravityView_Layout_Builder::ID,
static fn() => Grid::get_rows_from_collection( $gravityview->fields, $zone )
);

// There are entries. Loop through them.
$entry = $gravityview->entry;
$back_link = gravityview_back_link( $gravityview );
if ( $back_link ) {
printf( '<p class="gv-back-link">%s</p>', $back_link );
}
?>
<div class="gv-layout-builder-view gv-layout-builder-view--entry gv-grid">
<?php foreach ( $rows as $row ) { ?>
<div class="gv-grid-row">
<?php
foreach ( $row as $col => $areas ) {
$column = $col;
?>
<div class="gv-grid-col-<?php echo esc_attr( $column ); ?>">
<?php
if ( ! empty( $areas ) ) {
foreach ( $areas as $area ) {
foreach ( $gravityview->fields->by_position( $zone . '_' . $area['areaid'] )->all() as $field ) {
echo $gravityview->template->the_field( $field, $entry );
}
}
}
?>
</div>
<?php } // $row ?>
</div>
<?php } // $rows ?>
</div>
<?php

gravityview_footer( $gravityview );
$content = ob_get_clean();
/**
* Modify the wrapper container.
*
* @since 2.15
*
* @param string $wrapper_container Wrapper container HTML markup
* @param string $anchor_id (optional) Unique anchor ID to identify the view.
* @param \GV\View $view The View.
*/
$class = gv_container_class( 'gv-layout-builder-container', false, $gravityview );
$wrapper_container = apply_filters(
'gravityview/view/wrapper_container',
'<div id="' . esc_attr( $gravityview->view->get_anchor_id() ) . '" class="' . esc_attr( $class ) . '">{content}</div>',
$gravityview->view->get_anchor_id(),
$gravityview->view
);

echo $wrapper_container ? str_replace( '{content}', $content, $wrapper_container ) : $content;
86 changes: 86 additions & 0 deletions templates/views/gravityview-layout-builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php
/**
* @global Template_Context $gravityview
*/

use GV\Grid;
use GV\Template_Context;

if ( ! isset( $gravityview ) || empty( $gravityview->template ) ) {
gravityview()->log->error( '{file} template was loaded without context', [ 'file' => __FILE__ ] );

return;
}

gravityview_before( $gravityview );

ob_start();
gravityview_header( $gravityview );

// There are no entries.
if ( ! $gravityview->entries->count() ) {
?>
<div class="gv-layout-builder-view gv-no-results">
<div class="gv-layout-builder-view-title">
<h3><?php echo gv_no_results( true, $gravityview ); ?></h3>
</div>
</div>
<?php
} else {
$zone = 'directory';
$rows = Grid::prefixed(
GravityView_Layout_Builder::ID,
static fn() => Grid::get_rows_from_collection( $gravityview->fields, $zone )
);
// There are entries. Loop through them.
foreach ( $gravityview->entries->all() as $entry ) {
?>
<div class="gv-layout-builder-view gv-layout-builder-view--entry gv-grid">
<?php foreach ( $rows as $row ) { ?>
<div class="gv-grid-row">
<?php
foreach ( $row as $col => $areas ) {
$column = $col;
?>
<div class="gv-grid-col-<?php echo esc_attr( $column ); ?>">
<?php
if ( ! empty( $areas ) ) {
foreach ( $areas as $area ) {
foreach ( $gravityview->fields->by_position( $zone . '_' . $area['areaid'] )->all() as $field ) {
echo $gravityview->template->the_field( $field, $entry );
}
}
}
?>
</div>
<?php } // $row ?>
</div>
<?php } // $rows ?>
</div>
<?php
}
}

gravityview_footer( $gravityview );

$content = ob_get_clean();
/**
* Modify the wrapper container.
*
* @since 2.15
*
* @param string $wrapper_container Wrapper container HTML markup
* @param string $anchor_id (optional) Unique anchor ID to identify the view.
* @param \GV\View $view The View.
*/
$class = gv_container_class( 'gv-layout-builder-container', false, $gravityview );
$wrapper_container = apply_filters(
'gravityview/view/wrapper_container',
'<div id="' . esc_attr( $gravityview->view->get_anchor_id() ) . '" class="' . esc_attr( $class ) . '">{content}</div>',
$gravityview->view->get_anchor_id(),
$gravityview->view
);

echo $wrapper_container ? str_replace( '{content}', $content, $wrapper_container ) : $content;

gravityview_after( $gravityview );

0 comments on commit 3f0f199

Please sign in to comment.