Skip to content

Instantly share code, notes, and snippets.

@e1himself
Last active August 29, 2015 14:04
Show Gist options
  • Save e1himself/80f8c0310f77e3cb8eea to your computer and use it in GitHub Desktop.
Save e1himself/80f8c0310f77e3cb8eea to your computer and use it in GitHub Desktop.
SirTrevor block nesting

Nesting

Blocks

So I've created a TabsContainer block, which allows to nest other blocks inside it. I've used the same mechanics as reused code of Columns block does for its columns. It is indended to create Tabs only inside TabsContainer.

Next So I've got a Panel block that is mostly the same: it allows nest any other blocks inside of it. No limitations.

And the last touch: I've hidden all blocks' Create buttons under TabsContainer except the Panel block. (see tabs_styling.scss)

Here is the logic:

  • create a TabsContainer block
  • create child Panels (one Panel = one Tab)
  • create anything you want inside of Panel blocks
  • save

Resulting structure

The structure is something like this:

{
  "data": [
    {
      "type": "TabsContainer",
      "data": {
        "tabs": [
          {
            "type": "Panel",
            "data": { "title": "Panel 1", "blocks": [ ... ] }
          },
          {
            "type": "Panel",
            "data": { "title": "Panel 2", "blocks": [ ... ] }
          }
        ]
      }
    }
  ]
}

Frontend

And finally it's relatively easy to render this stuff to frontend with server-side scripts into tabs width markup.

SirTrevor.Blocks.Panel = SirTrevor.Block.extend {
type: 'panel'
title: 'Panel'
icon_name: 'panel'
controllable: true
styles:
top_border: 'Top border', left_border: 'Left border', filled: 'Filled', exclamation: '!'
colors:
gray: 'Gray', orange: 'Orange', green: 'Green', red: 'Red', blue: 'Blue'
controlsHTML:
'<div class="controls" style="display: none">
<label>Style: <select name="style"/></label>
<span class="sep"/>
<label>Color: <select name="color"/></label>
</div>'
editorHTML: () ->
_.result(this, 'controlsHTML') +
'<div class="rc-panel">
<div class="rc-panel-body"></div>
</div>'
constructor: (data, instance_id, sirTrevor) ->
SirTrevor.Block.apply(this, arguments)
getControls: ->
this.$inner.children('.controls')
toggleControls: ->
this.getControls().slideToggle()
getPanel: ->
this.$inner.children('.rc-panel')
getBlocksContainer: ->
this.$inner.children('.rc-panel').children('.rc-panel-body')
beforeBlockRender: ->
@controls =
configure: @toggleControls
_setBlockInner: ->
SirTrevor.Block.prototype._setBlockInner.apply(this, arguments)
$container = this.getBlocksContainer()
plus = new SirTrevor.FloatingBlockControls($container, @instanceID)
this.listenTo(plus, 'showBlockControls', @sirTrevor.showBlockControls)
$container.prepend(plus.render().$el)
$styles_select = this.findControl('[name=style]')
for own s of @styles
$styles_select.append $('<option>', value: s).text(@styles[s])
$colors_select = this.findControl('[name=color]')
for own c of @colors
$colors_select.append $('<option>', value: c).text(@colors[c])
onBlockRender: ->
to_class = (name, value) -> "rc-panel--#{name}_#{value}"
$styles_select = this.findControl('[name=style]')
$styles_select.on 'change click', =>
for own s of @styles
this.getPanel().toggleClass to_class('style', s), s == $styles_select.val()
$styles_select.trigger 'change'
$colors_select = this.findControl('[name=color]')
$colors_select.on 'change click', =>
for own c of @colors
this.getPanel().toggleClass to_class('color', c), c == $colors_select.val()
$colors_select.trigger 'change'
if this.getBlocksContainer().children('.st-block').length == 0
$parent = this.getBlocksContainer().children('.st-block-controls__top')
@sirTrevor.createBlock('text', {}, $parent)
findControl: (selector) ->
this.getControls().find(selector)
toData: ->
data = { blocks: [] }
this.getBlocksContainer().children('.st-block').each (i, el) =>
block = @sirTrevor.findBlockById el.getAttribute('id')
data.blocks.push block.saveAndReturnData()
this.findControl(':input').each ->
if $(this).is('input[type=checkbox]')
data[this.getAttribute('name')] = if $(this).is(':checked') then $(this).val() else null
else
data[this.getAttribute('name')] = $(this).val()
return true # continue
this.setData data
loadData: (data) ->
for own i, v of data
$input = this.findControl('select, input').filter('[name=' + i + ']')
if $input.is('input[type=checkbox]')
if v then $input.attr('checked', 'true') else $input.removeAttr('checked')
else
$input.val(v)
blocks_data = (data.blocks || [])
$block = null
for block in blocks_data
$parent = if $block then $block.$el else this.getBlocksContainer().children('.st-block-controls__top')
$block = @sirTrevor.createBlock(block.type, block.data, $parent)
};
.st-block[data-type=tabs_container]
{
@include controllable;
> .st-block__inner > .tabs-container-inner {
// ... some stylings here
> .with-st-controls > .st-block-controls {
& > .st-block-control {
display: none;
&[data-type=panel_titled] {
display: block;
}
}
}
}
}
}
SirTrevor.Blocks.TabsContainer = SirTrevor.Block.extend {
type: 'tabs_container'
title: 'Tabs'
icon_name: 'panel'
controllable: true
styles:
tabs: 'Tabs', accordion: 'Accordion'
controlsHTML:
'<div class="controls" style="display: none">
<label>Style: <select name="style"/></label>
</div>'
editorHTML: ->
_.result(this, 'controlsHTML') +
'<div class="tabs-container-inner">
<div class="tabs-container-header"></div>
<div class="tabs-container"/>
</div>'
constructor: (data, instance_id, sirTrevor) ->
SirTrevor.Block.apply(this, arguments)
getControls: ->
this.$inner.children('.controls')
toggleControls: ->
this.getControls().slideToggle()
beforeBlockRender: ->
@controls =
configure: @toggleControls
onBlockRender: ->
to_class = (name, value) -> "rc-tabs--#{name}_#{value}"
$styles_select = this.findControl('[name=style]')
$styles_select.on 'change click', =>
for own s of @styles
this.getWrapper().toggleClass to_class('style', s), s == $styles_select.val()
$styles_select.trigger 'change'
getWrapper: -> this.$inner.children('.tabs-container-inner')
getBlocksContainer: -> this.$inner.find('.tabs-container')
_setBlockInner: ->
SirTrevor.Block.prototype._setBlockInner.apply(this, arguments)
$container = this.getBlocksContainer()
plus = new SirTrevor.FloatingBlockControls($container, @instanceID)
this.listenTo(plus, 'showBlockControls', @sirTrevor.showBlockControls)
$container.prepend(plus.render().$el)
$styles_select = this.findControl('[name=style]')
for own s of @styles
$styles_select.append $('<option>', value: s).text(@styles[s])
findControl: (selector) ->
this.getControls().find(selector)
toData: ->
data = { tabs: [] }
this.getBlocksContainer().children('.st-block').each (i, el) =>
block = @sirTrevor.findBlockById el.getAttribute('id')
data.tabs.push block.saveAndReturnData()
this.findControl(':input').each ->
data[this.getAttribute('name')] = $(this).val()
return true # continue
this.setData(data)
loadData: (data) ->
for own i, v of data
$input = this.findControl('select, input').filter('[name=' + i + ']')
if $input.is('input[type=checkbox]')
if v then $input.attr('checked', 'true') else $input.removeAttr('checked')
else
$input.val(v)
tabs_data = (data.tabs || [])
$block = null
for block in tabs_data
$parent = if $block then $block.$el else this.getBlocksContainer().children('.st-block-controls__top')
$block = @sirTrevor.createBlock(block.type, block.data, $parent)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment