Skip to content

Commit

Permalink
Always lazy load new blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
ttempleton committed Mar 10, 2024
1 parent d15e0b6 commit 6ca02ec
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 496 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## Removed
- Removed the `neoblocks_owners` table; the Craft 5 `elements_owners` table is used instead
- Removed `benf\neo\models\Settings::$enableLazyLoadingNewBlocks`; new blocks are now always lazy loaded
- Removed `benf\neo\services\Blocks::renderTabs()`

## 4.0.7 - 2024-03-09

Expand Down
2 changes: 0 additions & 2 deletions src/assets/InputAsset.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ private static function _getBlockTypesJsSettings(Field $field, array $blockTypes
{
$user = Craft::$app->getUser()->getIdentity();
$pluginSettings = Neo::$plugin->getSettings();
$loadTabs = !$pluginSettings->enableLazyLoadingNewBlocks;
$disablePermissions = !$pluginSettings->enableBlockTypeUserPermissions;
$jsBlockTypes = [];

Expand Down Expand Up @@ -214,7 +213,6 @@ private static function _getBlockTypesJsSettings(Field $field, array $blockTypes
fn($tab) => Craft::t('site', $tab->name),
$blockType->getFieldLayout()->getTabs()
),
'tabs' => $loadTabs ? Neo::$plugin->blocks->renderTabs($block) : null,
'fieldLayoutId' => $blockType->fieldLayoutId,
'groupId' => $blockType->groupId,
'hasChildBlocksUiElement' => $blockType->hasChildBlocksUiElement(),
Expand Down
2 changes: 1 addition & 1 deletion src/assets/dist/neo-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/assets/dist/neo-main.js.map

Large diffs are not rendered by default.

255 changes: 21 additions & 234 deletions src/assets/src/input/Block.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import Garnish from 'garnish'
import Craft from 'craft'

import NS from '../namespace'
import Tab from './BlockTypeTab'

import { addFieldLinks } from '../plugins/cpfieldinspect/main'

Expand All @@ -23,8 +22,6 @@ const _defaults = {
showBlockTypeHandle: false
}

const _resources = {}

const _escapeMap = {
'&': '&',
'<': '&lt;',
Expand All @@ -34,23 +31,6 @@ const _escapeMap = {
'/': '&#x2F;'
}

function _resourceFilter () {
let url = this.href || this.src

if (url) {
const paramIndex = url.indexOf('?')

url = (paramIndex < 0 ? url : url.substr(0, paramIndex))

const isNew = !Object.prototype.hasOwnProperty.call(_resources, url)
_resources[url] = 1

return isNew
}

return true
}

function _escapeHTML (str) {
return str ? str.replace(/[&<>"'/]/g, s => _escapeMap[s]) : ''
}
Expand All @@ -71,39 +51,28 @@ export default Garnish.Base.extend({
_modified: true,
_initialState: null,
_forceModified: false,
_tabs: null,
_html: null,
_js: null,

init (settings = {}, generateElement = false) {
init (settings = {}) {
settings = Object.assign({}, _defaults, settings)

this._templateNs = NS.parse(settings.namespace)
this._field = settings.field
this._blockType = settings.blockType
if (settings.tabs !== null) {
this._tabs = settings.tabs.tabNames?.map(
tab => tab instanceof Tab
? tab
: new Tab({
name: tab,
uid: settings.tabs.tabUids[tab]
})
) ?? []
} else {
this._tabs = null
}
this._html = settings.tabs?.html ?? null
this._js = settings.tabs?.js ?? null
this._blockHtml = settings.blockHtml
this._bodyHtml = settings.bodyHtml
this._headHtml = settings.headHtml
this._id = settings.id
this._enabled = settings.enabled && this._blockType.getEnabled()
this._initialEnabled = settings.enabled
this._modified = settings.modified
this._showButtons = settings.showButtons
this._renderOldChildBlocksContainer = !settings.blockType.hasChildBlocksUiElement()
this.$container = generateElement
? this._generateElement(settings.showBlockTypeHandle)
this.$container = this._blockHtml
? $(this._blockHtml)
: this._field.$container.find(`[data-neo-b-id=${this._id}]`)
this._uuid = settings.uuid ?? this.$container.data('neo-b-uuid')

const $neo = this.$container.find('[data-neo-b]')
this.$bodyContainer = $neo.filter(`[data-neo-b="${this._id}.container.body"]`)
Expand Down Expand Up @@ -145,202 +114,15 @@ export default Garnish.Base.extend({
this.$container.data('block', this)
},

_generateElement (showHandle = false) {
NS.enter(this._templateNs)
const baseInputName = NS.toFieldName()
const baseInputId = NS.toString('-')
NS.leave()

const type = this._blockType
const tabs = this._tabs ?? type.getTabs()
const hasTabs = tabs.length > 0
const isParent = type.isParent()
const actionBtnLabel = `${type.getName()} ${Craft.t('neo', 'Actions')}`
const actionMenuId = `neoblock-action-menu-${this._id}`
const tabsBtnLabel = `${type.getName()} ${Craft.t('neo', 'Tabs')}`
const tabsMenuId = `neoblock-tabs-menu-${this._id}`
const sortOrderName = `${this._templateNs[0]}[${this._templateNs.slice(1, this._templateNs.length - 2).join('][')}][sortOrder]`
const elementHtml = []
elementHtml.push(`
<div class="ni_block ni_block--${type.getHandle()} is-${this._collapsed ? 'collapsed' : 'expanded'} ${!hasTabs && !isParent ? 'is-empty' : ''} ${isParent ? 'is-parent' : ''}" data-neo-b-id="${this._id}" data-neo-b-name="${type.getName()}">
<input type="hidden" name="${baseInputName}[type]" value="${type.getHandle()}">
<input type="hidden" name="${baseInputName}[enabled]" value="${this._enabled ? '1' : ''}" data-neo-b="${this._id}.input.enabled">
<input type="hidden" name="${baseInputName}[level]" value="${this._level}" data-neo-b="${this._id}.input.level">
<input type="hidden" name="${sortOrderName}[]" value="${this._id}" data-neo-b="${this._id}.input.sortOrder">`)

if (isNaN(parseInt(this._id))) {
elementHtml.push(`
<input type="hidden" name="${baseInputName}[collapsed]" value="${!this._expanded ? '1' : ''}" data-neo-b="${this._id}.input.collapsed">`)
}

elementHtml.push(`
<div class="ni_block_topbar" data-neo-b="${this._id}.container.topbar">
<div class="ni_block_topbar_left" data-neo-b="${this._id}.container.topbarLeft">
<div class="ni_block_topbar_item title">
<span class="blocktype" data-neo-b="${this._id}.select">${type.getName()}</span>
</div>
<div class="ni_block_topbar_item preview-container clip-text">
<span class="preview" data-neo-b="${this._id}.container.preview">&nbsp;</span>
</div>
</div>
<div class="ni_block_topbar_right" data-neo-b="${this._id}.container.topbarRight">
<div class="ni_block_topbar_item size-full tabs">`)

if (hasTabs || isParent) {
elementHtml.push(`
<div class="tabs_trigger" data-neo-b="${this._id}.button.toggler"></div>`)
}

if (tabs.length > 1) {
elementHtml.push(`
<div class="tabs_inner" data-neo-b="${this._id}.container.tabs">`)

for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i]
const tabName = tab.getName()
const tabUid = tab.getUid()
elementHtml.push(`
<a class="tab ${!i ? 'is-selected' : ''}" data-neo-b="${this._id}.button.tab" data-neo-b-info="${tabName}" data-neo-b-tabuid="${tabUid}">${tabName}</a>`)
}

elementHtml.push(`
</div>
<div>
<button type="button" role="button" title=${Craft.t('neo', 'Tabs')} aria-controls="${tabsMenuId}" aria-label="${tabsBtnLabel}" data-disclosure-trigger data-neo-b="${this._id}.button.tabs" class="tabs_btn menubtn">
${tabs[0].getName()}
</button>
<div id="${tabsMenuId}" class="neo_block_tabs-menu menu menu--disclosure">
<ul>`)

for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i]
const tabName = tab.getName()
const tabUid = tab.getUid()
elementHtml.push(`
<li>
<a${!i ? ' class="is-selected"' : ''} href="#" type="button" role="button" aria-label="${tabName}" data-neo-b="${this._id}.button.tab" data-neo-b-info="${tabName}" data-neo-b-tabuid="${tabUid}">${tabName}</a>
</li>`)
}

elementHtml.push(`
</ul>
</div>
</div>`)
}

elementHtml.push(`
</div>
<div class="ni_block_topbar_item hidden" data-neo-b="${this._id}.status">
<div class="status off" title="${Craft.t('neo', 'Disabled')}"></div>
</div>
<div class="ni_block_topbar_item actions">
<div data-neo-b="${this._id}.select">
<div class="checkbox block-checkbox" title="${Craft.t('neo', 'Select')}" aria-label="${Craft.t('neo', 'Select')}"></div>
</div>
<div class="block-settings">
<div>
<button class="btn action-btn menubtn" type="button" role="button" title="${Craft.t('neo', 'Actions')}" aria-controls="${actionMenuId}" aria-label="${actionBtnLabel}" data-disclosure-trigger data-neo-b="${this._id}.button.actions"></button>
<div id="${actionMenuId}" class="menu menu--disclosure" data-neo-b="${this._id}.container.menu">
<ul>`)

if (hasTabs || isParent) {
elementHtml.push(`
<li><a data-icon="collapse" data-action="collapse" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Collapse')}">${Craft.t('neo', 'Collapse')}</a></li>
<li class="hidden"><a data-icon="expand" data-action="expand" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Expand')}">${Craft.t('neo', 'Expand')}</a></li>`)
}

elementHtml.push(`
<li><a data-icon="disabled" data-action="disable" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Disable')}">${Craft.t('neo', 'Disable')}</a></li>
<li class="hidden"><a data-icon="enabled" data-action="enable" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Enable')}">${Craft.t('neo', 'Enable')}</a></li>
<li class="hidden"><a data-icon="uarr" data-action="moveUp" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Move up')}">${Craft.t('neo', 'Move up')}</a></li>
<li class="hidden"><a data-icon="darr" data-action="moveDown" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Move down')}">${Craft.t('neo', 'Move down')}</a></li>
</ul>
<hr class="padded">
<ul>
<li><a data-icon="plus" data-action="add" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Add block above')}">${Craft.t('neo', 'Add block above')}</a></li>
<li><a data-icon="field" data-action="copy" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Copy')}">${Craft.t('neo', 'Copy')}</a></li>
<li><a data-icon="brush" data-action="paste" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Paste')}">${Craft.t('neo', 'Paste')}</a></li>
<li><a data-icon="share" data-action="duplicate" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Clone')}">${Craft.t('neo', 'Clone')}</a></li>
</ul>`)

if (type.isDeletableByUser()) {
elementHtml.push(`
<hr class="padded">
<ul>
<li><a class="error" data-icon="remove" data-action="delete" href="#" type="button" role="button" aria-label="${Craft.t('neo', 'Delete')}">${Craft.t('neo', 'Delete')}</a></li>
</ul>`)
}

elementHtml.push(`
</div>
</div>
</div>
<a class="block-reorder move icon" title="${Craft.t('neo', 'Reorder')}" aria-label="${Craft.t('neo', 'Reorder')}" role="button" data-neo-b="${this._id}.button.move"></a>
</div>
</div>
</div>`)

if (hasTabs || isParent) {
elementHtml.push(`
<div class="ni_block_body" data-neo-b="${this._id}.container.body">`)

if (hasTabs) {
elementHtml.push(`
<div class="ni_block_content" data-neo-b="${this._id}.container.content">
${this.getHtml()}
</div>`)
}

if (isParent && this._renderOldChildBlocksContainer) {
elementHtml.push(`
<div class="ni_block_children" data-neo-b="${this._id}.container.children">
<div class="ni_blocks" data-neo-b="${this._id}.container.blocks">
</div>
<div data-neo-b="${this._id}.container.buttons" class="hidden"></div>
<div data-neo-b="${this._id}.container.childrenWarnings" class="hidden">
<p class="first warning with-icon">${Craft.t('neo', "This Neo field's maximum number of levels has been reached, so no child blocks can be added here.")}</p>
</div>
</div>`)
}

elementHtml.push(`
</div>`)
}

if (isParent) {
elementHtml.push(`
<div class="ni_block_collapsed-children" data-neo-b="${this._id}.container.collapsedChildren"></div>`)
}

elementHtml.push(`
<div data-neo="container.buttons"></div>`)

const $elementHtml = $(elementHtml.join(''))

if (showHandle) {
$('<div/>')
.addClass('ni_block_topbar_item handle')
.prop('data-neo-b', `${this._id}.container.handle`)
.append(Craft.ui.createCopyTextBtn({
id: `${baseInputId}-${type.getHandle()}-attribute`,
class: ['code', 'small', 'light'],
value: type.getHandle()
}))
.insertAfter($elementHtml.find('.ni_block_topbar_item.title'))
}

return $elementHtml
},

initUi (callInitUiElements = true) {
if (this._initialised) {
// Nothing to do here
return
}

if (callInitUiElements) {
this.$foot = $(this.getJs()).filter(_resourceFilter)
Garnish.$bod.append(this.$foot)
Craft.appendBodyHtml(this._bodyHtml)
Craft.appendHeadHtml(this._headHtml)
Craft.initUiElements(this.$contentContainer)
}

Expand Down Expand Up @@ -458,18 +240,14 @@ export default Garnish.Base.extend({
* @since 3.9.0
*/
getHtml () {
return this._html !== null
? this._html.replace(/__NEOBLOCK__/g, this._id)
: this._blockType.getHtml(this._id)
return this._blockHtml.replace(/__NEOBLOCK__/g, this._id)
},

/**
* @since 3.9.0
*/
getJs () {
return this._js !== null
? this._js.replace(/__NEOBLOCK__/g, this._id)
: this._blockType.getJs(this._id)
return this._bodyHtml.replace(/__NEOBLOCK__/g, this._id)
},

destroy () {
Expand All @@ -494,6 +272,15 @@ export default Garnish.Base.extend({
return this._id
},

/**
* @public
* @returns the block UUID
* @since 5.0.0
*/
getUuid () {
return this._uuid
},

/**
* @public
* @returns the ID of the duplicate block, or the ID of this block if it hasn't been duplicated
Expand Down Expand Up @@ -1280,7 +1067,7 @@ export default Garnish.Base.extend({
const content = Garnish.getPostData(this.$contentContainer)
// Remove keys associated with child block subfields (occurs when using child blocks UI element)
const badKeys = Object.keys(content)
.filter((key) => !key.startsWith(`fields[${this._field.getName()}][blocks][${this._id}]`))
.filter((key) => !key.startsWith(`fields[${this._field.getName()}][blocks][uid:${this._uuid}]`))

for (const key of badKeys) {
delete content[key]
Expand Down
Loading

0 comments on commit 6ca02ec

Please sign in to comment.