Block Grid Editor (#12826)

* Refactor block list/grid property value editors for code reuse

* Prettify block grid editor data conveter a bit

* correct appearance and colors

* validation should only kick in if value is higher than 0 and not null

* Add copyright notice to code, a little formatting and file scoped namespaces where applicable

* custom views

* custom views for blocks

* Refactor block list min/max validation and reuse for block grid

* Prettify extraction of nested content-and-settings references for block grid

* Fix bad naming

* fix casing

* Refactor block list notification handler and reuse for block grid

* context sensitive ui for Blocks

* Refactor notification handlers for block list and block grid incl. unit tests

* Formatting and review

* Bump number of expected data editors in unit test

* initial progress on column options editor

* column span options UI

* column span UI adjustments

* context aware ui adjustments

* minor border improvement

* get and pick layout stylesheet

* remove random x

* make highlight border standout more

* dedicated context bar

* UI improvements

* remove annoying indication

* copy paste

* UI improvements

* remove div.umb-block-grid__block--view from partial

* show block ui, only hide when hovering area.

* area actions

* UI Adjustments

* Block grid editor localization (#12915)

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>

* fix case of empty value

* use right index

* place border on top of column indication

* heroblock

* userFlowWhenBlockWasCreated

* shorter messages

* overal improvements

* force left/right drag feature

* stylesheet picker localization

* localizations

* space

* unnesecary removal of space

* notes on masonry

* validation form parent skipping + better snapping when drag n drop

* remove icons

* make rows go as minimal as posible

* scale and drop indications

* use item width to determin forceLeft or forceRight

* clean up

* Make clipboard filtering work with block arrays

* readonly mode

* important note

* ask to revert failed paste

* dont allow for backdrop click

* changed wording

* more area actions outside grid container

* droppable indication

* implemented minimal responsive solution.

* add additional data fields about grid clumns

* improved fallback columns width + rows height

* make root element class `umb-block-grid`

* align layout with position relatives, to align visual experience.

* use clientX not screenX

* rename area classes so they dont include the word 'block'

* more renaming

* commit flexbox layout css to repo

* fixes

* fix typo

* only show a broader inline create button if the block is full width.

* simplify css

* remove headline block hover effect

* use some font, to make it look better.

* Drag and drop correctiions/limits

* clean up

* remove log

* make assumptions about proposed distance of target

* slightly white border on validation message for areas to make it standout

* drag n drop seems good at this stage

* force left and right buttons and indication

* correct if

* revert scale handler ui

* If columnSpans is empty we fall back to full width

* Access other PreValues from a PreValue Editor

* maybe temprorary turn of distance drag n' drop condition

* setDirty

* area alias was missing in razor and needed help text

* forceleft forceright translations

* forceLeft / forceRight razor

* correct translation +
remove data- on ng-click

* force placement when moving over edge

* remove unnesecary wrapper element

* show-validation

* red text, might be hard to see, lets see.

* for safety have the border-raduis correct

* better wordings

* Use mouse position to judge if the drop is good before or after.

* fit placeholder when item is begin dragged away

* initial step for finding nearest relation when drag-hovering nothing

* Much better drag n drop experience, must be tested further to prove its not breaking some cases.

* title for scale handler

* new approach + massive drag n drop clean up

* avoid complex CSS transfering of props, instead add the prop in HTML

* adjust placeAfter when in vertical direction mode

* do not scale bigger than the available space to the right

* clean css

* fix numbers

* Fix the case of flickering when hitting an empty area.

* correct placement of code

* package lock

* Adjustment for PreValue Area allowances

* comment on fit in line feature

* fixing scale and drag n drop potential issues

* clean up

* only disallow above max

* outcomment unused code

* clean up

* drag n drop above or below container

* fix for Firefox

* Do not edit block if there is no properties in content

* angularJS form for Entries, to correct validation

* parse layout columns, used to know if block is full width of the layout

* use inherited layout columns

* add this to the example html

* flexbox fix

* highlight if empty area

* comments for undefined column spans

* bring back approvedContainer lock period.

* fix

* Do not edit something without properties

* Remove Force, as thats confusing to read.

* correct localization key

* minor corrections

* Fit within context columns

* Conceptual, inline editor for Grid.

* fix casing

* consider related position in directional conditions

* set default max row span to 1

* update columnSpanOption check for sync

* move Editor to group of Rich Text

* more shift autoScroll

* assume layout columns comes as string

* fix variable name

* force left/right indication

* Inject Areas directly in slot, to enable custom view manipulations of such.

* fix sortableJS mis dropping items

* Overwrite create label

* ability to overwrite root layout create label

* Simplify PreValue editor by hidding options that is not in effect.

* Setup new areas as half width if possible

* Grouping blockTypes

* remove flexbox stylesheet

* Chose groups for area allowance

* ensure a good default width

* improve block active state

* Better contextual sizing

* Comment clean up

* Remove The StarterKit from branch

* Unique group identification for the property editor sortable

* only show avaiable block groups in picker.

* Indication of taget area

* Fallback to root grid columns

* use root layout grid columns, so dont update when layoutColumns change

* Ability to remove block group

* clean up references when removing Group or single Block

* fix drop in same group

* Block picker use contexual create label as headline

* adjust area highlight

* Prevalue editor

* structuralOptions

* Move pre values into tabs for better overview and scope

* Let area grid columns fallback to root grid columns, and let both default to "12" instead of "initial"

* remove input close tag

* Allow in areas

* Move build-in custom views

* only show property-into-button on hover when in group-panel__header

* some height for the show buttons

* filter available block type based on allowInAreas

* remove OnlySpecifiedAllowance

* unsupported block if trouble happend

* move allow at root to allowance group

* easily drop at top or below area if outside. If more than 100px outside then see if there is a parent layout to move into.

* block group validation

* shortcut for opening Areas if areas are defined

* scale label for Areas editor

* Added Legacy name on the old grid layout

* Remove files that come with the starter kit

* Sorting: takeover container detection

* Refactor models + remove unused properties

* Endpoint for creating block grid demo element types

* CTA for getting Block Grid demo blocks

* Refactor block grid sample element creation

* Fix Constants-PropertyEditors spacing

* Fixed unit tests

* Get sample configuration

* Labels for sample blocks

* Improve drag, to swap across unallowed area/root

* improved empty threshold

* hide the after inline-create-button if block is located at right side of area/layout

* clean up

* translations

* danish translations

* move outside edge for forceRight

* Update src/Umbraco.Web.Common/Extensions/BlockGridTemplateExtensions.cs

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

* Review comments

* Remove leftover TODO

* big cleanup

* Hide Sample CTA when installed

* clean up

* correct styling for wrapper + moving Block UI to z-index 3 for visiblity on top of other block

* slightly adjusting inline-create-buttons to avoid collisions

* correct localization for force left/right buttons

* gitignorer update

* Clean up nesting of classes in BlockGridConfiguration

* change gitignorer to include App_Plugin folder

* move default layout stylesheet

* remove specific App_plugin folder

* package-lock

* update sample custom views paths

* add custom views for sample

* Adding sample custom views

* Move sample partial Views and custom views

* Update views to not use ModelsBuilder

* Move sample custom views to wwwroot

* Updated Sample CTA text

* Use localize directory

* Ensure groupKey for items without such to work.

Co-authored-by: kjac <kja@umbraco.dk>
Co-authored-by: Warren Buckley <warren@umbraco.com>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
This commit is contained in:
Niels Lyngsø
2022-10-05 13:50:26 +02:00
committed by GitHub
parent efb994ecf7
commit 367a4b9727
137 changed files with 10847 additions and 1013 deletions

View File

@@ -0,0 +1,42 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Runtime.Serialization;
namespace Umbraco.Cms.Core.Models.Blocks;
[DataContract(Name = "area", Namespace = "")]
public class BlockGridArea : BlockModelCollection<BlockGridItem>
{
/// <summary>
/// Initializes a new instance of the <see cref="BlockGridArea" /> class.
/// </summary>
/// <param name="list">The list to wrap.</param>
/// <param name="alias">The area alias</param>
/// <param name="rowSpan">The number of rows this area should span</param>
/// <param name="columnSpan">The number of columns this area should span</param>
public BlockGridArea(IList<BlockGridItem> list, string alias, int rowSpan, int columnSpan) : base(list)
{
Alias = alias;
RowSpan = rowSpan;
ColumnSpan = columnSpan;
}
/// <summary>
/// The area alias
/// </summary>
[DataMember(Name = "alias")]
public string Alias { get; }
/// <summary>
/// The number of rows this area should span.
/// </summary>
[DataMember(Name = "rowSpan")]
public int RowSpan { get; }
/// <summary>
/// The number of columns this area should span.
/// </summary>
[DataMember(Name = "columnSpan")]
public int ColumnSpan { get; }
}

View File

@@ -0,0 +1,180 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Runtime.Serialization;
using Umbraco.Cms.Core.Models.PublishedContent;
namespace Umbraco.Cms.Core.Models.Blocks
{
/// <summary>
/// Represents a layout item for the Block Grid editor.
/// </summary>
/// <seealso cref="Umbraco.Core.Models.Blocks.IBlockReference{Umbraco.Core.Models.PublishedContent.IPublishedElement,Umbraco.Core.Models.PublishedContent.IPublishedElement}" />
[DataContract(Name = "block", Namespace = "")]
public class BlockGridItem : IBlockReference<IPublishedElement, IPublishedElement>
{
/// <summary>
/// Initializes a new instance of the <see cref="BlockGridItem" /> class.
/// </summary>
/// <param name="contentUdi">The content UDI.</param>
/// <param name="content">The content.</param>
/// <param name="settingsUdi">The settings UDI.</param>
/// <param name="settings">The settings.</param>
/// <param name="rowSpan">The number of rows to span</param>
/// <param name="columnSpan">The number of columns to span</param>
/// <exception cref="System.ArgumentNullException">contentUdi
/// or
/// content</exception>
public BlockGridItem(Udi contentUdi, IPublishedElement content, Udi settingsUdi, IPublishedElement settings)
{
ContentUdi = contentUdi ?? throw new ArgumentNullException(nameof(contentUdi));
Content = content ?? throw new ArgumentNullException(nameof(content));
SettingsUdi = settingsUdi;
Settings = settings;
}
/// <summary>
/// Gets the content UDI.
/// </summary>
/// <value>
/// The content UDI.
/// </value>
[DataMember(Name = "contentUdi")]
public Udi ContentUdi { get; }
/// <summary>
/// Gets the content.
/// </summary>
/// <value>
/// The content.
/// </value>
[DataMember(Name = "content")]
public IPublishedElement Content { get; }
/// <summary>
/// Gets the settings UDI.
/// </summary>
/// <value>
/// The settings UDI.
/// </value>
[DataMember(Name = "settingsUdi")]
public Udi SettingsUdi { get; }
/// <summary>
/// Gets the settings.
/// </summary>
/// <value>
/// The settings.
/// </value>
[DataMember(Name = "settings")]
public IPublishedElement Settings { get; }
/// <summary>
/// The number of rows this item should span
/// </summary>
[DataMember(Name = "rowSpan")]
public int RowSpan { get; set; }
/// <summary>
/// The number of columns this item should span
/// </summary>
[DataMember(Name = "columnSpan")]
public int ColumnSpan { get; set; }
/// <summary>
/// Forcing the item to be located in the left side.
/// </summary>
[DataMember(Name = "forceLeft")]
public bool ForceLeft { get; set; }
/// <summary>
/// Forcing the item to be located in the right side.
/// </summary>
[DataMember(Name = "forceRight")]
public bool ForceRight { get; set; }
/// <summary>
/// The grid areas within this item
/// </summary>
[DataMember(Name = "areas")]
public IEnumerable<BlockGridArea> Areas { get; set; } = Array.Empty<BlockGridArea>();
/// <summary>
/// The number of columns available for the areas to span
/// </summary>
[DataMember(Name = "areaGridColumns")]
public int? AreaGridColumns { get; set; }
/// <summary>
/// The number of columns in the root grid
/// </summary>
[DataMember(Name = "gridColumns")]
public int? GridColumns { get; set; }
}
/// <summary>
/// Represents a layout item with a generic content type for the Block List editor.
/// </summary>
/// <typeparam name="T">The type of the content.</typeparam>
/// <seealso cref="Umbraco.Core.Models.Blocks.IBlockReference{Umbraco.Core.Models.PublishedContent.IPublishedElement}" />
public class BlockGridItem<T> : BlockGridItem
where T : IPublishedElement
{
/// <summary>
/// Initializes a new instance of the <see cref="BlockGridItem{T}" /> class.
/// </summary>
/// <param name="contentUdi">The content UDI.</param>
/// <param name="content">The content.</param>
/// <param name="settingsUdi">The settings UDI.</param>
/// <param name="settings">The settings.</param>
/// <param name="rowSpan">The number of rows to span</param>
/// <param name="columnSpan">The number of columns to span</param>
public BlockGridItem(Udi contentUdi, T content, Udi settingsUdi, IPublishedElement settings)
: base(contentUdi, content, settingsUdi, settings)
{
Content = content;
}
/// <summary>
/// Gets the content.
/// </summary>
/// <value>
/// The content.
/// </value>
public new T Content { get; }
}
/// <summary>
/// Represents a layout item with generic content and settings types for the Block List editor.
/// </summary>
/// <typeparam name="TContent">The type of the content.</typeparam>
/// <typeparam name="TSettings">The type of the settings.</typeparam>
/// <seealso cref="Umbraco.Core.Models.Blocks.IBlockReference{Umbraco.Core.Models.PublishedContent.IPublishedElement}" />
public class BlockGridItem<TContent, TSettings> : BlockGridItem<TContent>
where TContent : IPublishedElement
where TSettings : IPublishedElement
{
/// <summary>
/// Initializes a new instance of the <see cref="BlockGridItem{TContent, TSettings}" /> class.
/// </summary>
/// <param name="contentUdi">The content udi.</param>
/// <param name="content">The content.</param>
/// <param name="settingsUdi">The settings udi.</param>
/// <param name="settings">The settings.</param>
/// <param name="rowSpan">The number of rows to span</param>
/// <param name="columnSpan">The number of columns to span</param>
public BlockGridItem(Udi contentUdi, TContent content, Udi settingsUdi, TSettings settings)
: base(contentUdi, content, settingsUdi, settings)
{
Settings = settings;
}
/// <summary>
/// Gets the settings.
/// </summary>
/// <value>
/// The settings.
/// </value>
public new TSettings Settings { get; }
}
}

View File

@@ -0,0 +1,43 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
namespace Umbraco.Cms.Core.Models.Blocks;
/// <summary>
/// The strongly typed model for the Block List editor.
/// </summary>
/// <seealso cref="ReadOnlyCollection{BlockGridItem}" />
[DataContract(Name = "blockgrid", Namespace = "")]
public class BlockGridModel : BlockModelCollection<BlockGridItem>
{
/// <summary>
/// Gets the empty <see cref="BlockGridModel" />.
/// </summary>
/// <value>
/// The empty <see cref="BlockGridModel" />.
/// </value>
public static BlockGridModel Empty { get; } = new BlockGridModel();
/// <summary>
/// Prevents a default instance of the <see cref="BlockGridModel" /> class from being created.
/// </summary>
private BlockGridModel()
: this(new List<BlockGridItem>(), null)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="BlockGridModel" /> class.
/// </summary>
/// <param name="list">The list to wrap.</param>
/// <param name="gridColumns">The number of columns in the grid</param>
public BlockGridModel(IList<BlockGridItem> list, int? gridColumns)
: base(list) => GridColumns = gridColumns;
/// <summary>
/// The number of columns in the grid
/// </summary>
public int? GridColumns { get; }
}

View File

@@ -1,3 +1,6 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Runtime.Serialization;
using Umbraco.Cms.Core.Models.PublishedContent;
@@ -8,7 +11,7 @@ namespace Umbraco.Cms.Core.Models.Blocks;
/// </summary>
/// <seealso cref="Umbraco.Core.Models.Blocks.IBlockReference{Umbraco.Core.Models.PublishedContent.IPublishedElement}" />
[DataContract(Name = "block", Namespace = "")]
public class BlockListItem : IBlockReference<IPublishedElement>
public class BlockListItem : IBlockReference<IPublishedElement, IPublishedElement>
{
/// <summary>
/// Initializes a new instance of the <see cref="BlockListItem" /> class.

View File

@@ -1,4 +1,6 @@
using System.Collections.ObjectModel;
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Runtime.Serialization;
namespace Umbraco.Cms.Core.Models.Blocks;
@@ -6,9 +8,8 @@ namespace Umbraco.Cms.Core.Models.Blocks;
/// <summary>
/// The strongly typed model for the Block List editor.
/// </summary>
/// <seealso cref="ReadOnlyCollection{BlockListItem}" />
[DataContract(Name = "blockList", Namespace = "")]
public class BlockListModel : ReadOnlyCollection<BlockListItem>
public class BlockListModel : BlockModelCollection<BlockListItem>
{
/// <summary>
/// Initializes a new instance of the <see cref="BlockListModel" /> class.
@@ -34,30 +35,4 @@ public class BlockListModel : ReadOnlyCollection<BlockListItem>
/// The empty <see cref="BlockListModel" />.
/// </value>
public static BlockListModel Empty { get; } = new();
/// <summary>
/// Gets the <see cref="BlockListItem" /> with the specified content key.
/// </summary>
/// <value>
/// The <see cref="BlockListItem" />.
/// </value>
/// <param name="contentKey">The content key.</param>
/// <returns>
/// The <see cref="BlockListItem" /> with the specified content key.
/// </returns>
public BlockListItem? this[Guid contentKey] => this.FirstOrDefault(x => x.Content.Key == contentKey);
/// <summary>
/// Gets the <see cref="BlockListItem" /> with the specified content UDI.
/// </summary>
/// <value>
/// The <see cref="BlockListItem" />.
/// </value>
/// <param name="contentUdi">The content UDI.</param>
/// <returns>
/// The <see cref="BlockListItem" /> with the specified content UDI.
/// </returns>
public BlockListItem? this[Udi contentUdi] => contentUdi is GuidUdi guidUdi
? this.FirstOrDefault(x => x.Content.Key == guidUdi.Guid)
: null;
}

View File

@@ -0,0 +1,42 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Collections.ObjectModel;
using Umbraco.Cms.Core.Models.PublishedContent;
namespace Umbraco.Cms.Core.Models.Blocks;
public abstract class BlockModelCollection<T> : ReadOnlyCollection<T>
where T : class, IBlockReference<IPublishedElement, IPublishedElement>
{
protected BlockModelCollection(IList<T> list) : base(list)
{
}
/// <summary>
/// Gets the <see cref="BlockListItem" /> with the specified content key.
/// </summary>
/// <value>
/// The <see cref="BlockListItem" />.
/// </value>
/// <param name="contentKey">The content key.</param>
/// <returns>
/// The <see cref="BlockListItem" /> with the specified content key.
/// </returns>
public T? this[Guid contentKey] => this.FirstOrDefault(x => x.Content.Key == contentKey);
/// <summary>
/// Gets the <see cref="BlockListItem" /> with the specified content UDI.
/// </summary>
/// <value>
/// The <see cref="BlockListItem" />.
/// </value>
/// <param name="contentUdi">The content UDI.</param>
/// <returns>
/// The <see cref="BlockListItem" /> with the specified content UDI.
/// </returns>
public T? this[Udi contentUdi] => contentUdi is GuidUdi guidUdi
? this.FirstOrDefault(x => x.Content.Key == guidUdi.Guid)
: null;
}

View File

@@ -1,3 +1,6 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
namespace Umbraco.Cms.Core.Models.Blocks;
public struct ContentAndSettingsReference : IEquatable<ContentAndSettingsReference>

View File

@@ -1,3 +1,6 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
namespace Umbraco.Cms.Core.Models.Blocks;
/// <summary>
@@ -36,3 +39,19 @@ public interface IBlockReference<TSettings> : IBlockReference
/// </value>
TSettings Settings { get; }
}
/// <summary>
/// Represents a data item reference with content and settings for a Block editor implementation.
/// </summary>
/// <typeparam name="TSettings">The type of the content.</typeparam>
public interface IBlockReference<TContent, TSettings> : IBlockReference<TSettings>
{
/// <summary>
/// Gets the content.
/// </summary>
/// <value>
/// The content.
/// </value>
TContent Content { get; }
}