# Crop Sections (collapsible panels) Links: [Home](../index.md) A row of side-by-side panels ("sections") that fills its parent: fixed-width or fluid sections share the available width, each has a header and a scrolling body of articles, and any section collapses into a 48 px vertical tab with a CSS 3D rotation. Sections can be attached, detached and looked up by name; the layout is recomputed every 500 ms. Written by SGApps.IO. ## Loading the Module ```js Application.require("elements/crop-sections").then(function (CropSections) { var panels = CropSections(); document.getElementById('mount').appendChild(panels.node()); // fills #mount (100 % x 100 %) -- give #mount a height var nav = panels.addSection({ name: 'Navigation', fixedWidth: 180 }); nav.header().innerHTML = 'Navigation'; nav.articles()[0].innerHTML = ''; var main = panels.addSection({ name: 'Details' }); // fluid: shares the remaining width main.header().textContent = 'Details'; }); ``` **Dependencies:** `uri-load`, `request` (the two templates are fetched with `request().url(...).response('text')`), `extensions/prototype` (the `_()` chain in the section). The module links `elements/crop-sections/style.css` and, through the sub-module `elements/crop-sections/section.js`, `section/style.css`. The header article carries the theme class `rr-bg-regular` (a background colour in the RegalRoyal theme); define it yourself when the theme is not loaded. ## Markup ```html
panels.node() -- position: relative, 100 % x 100 %
panels.sectionsContainer() -- white-space: nowrap, overflow: hidden
section.node() -- inline-block, margin 10px 5px
sized by section.render(): width - 10, height - 20
section.content()
section.header() -- 28 px line, your title
section.body() -- overflow-y: auto, max-height = height - 60
section.articles()[0] -- your content (add more
s)
...
``` ## API ### `CropSections()` Returns the **platform**, an `ApplicationPrototype`. A `setInterval` of 500 ms calls `render()` for the lifetime of the page (it is never cleared), so size changes of the parent and collapses are picked up automatically. | Method | Returns | Description | |---|---|---| | `node()` | `HTMLElement` | The root `
` | | `sectionsContainer()` | `HTMLElement` | The `.crop-sections--container` the sections live in | | `addSection([config], [detached])` | section | Builds a section from `config` (`{ name, fixedWidth, collapsed }`, all optional), attaches it unless `detached` is `true`, renders. Returns the section | | `sections()` | section[] | A copy of the list of attached sections, in attach order | | `section(name)` | section or `null` | The first attached section whose `config().name` matches | | `render()` | -- | The layout pass: every attached section gets `height(nodeHeight - 25)`; the free width is `containerWidth - 25` minus the real width of every collapsed section and the `fixedWidth` of every fixed one, and is split equally among the remaining (fluid) sections with `width()` | All methods carry the framework's `before` / `on` / `after` hooks. The platform also listens to a `section-collapsed` event on itself (re-render), but nothing in the module emits it -- `render()` runs on the interval instead. ### The section object Built by `elements/crop-sections/section.js` and extended by `addSection()`: | Method | Returns | Description | |---|---|---| | `node()` | `HTMLElement` | `.crop-sections--item-container` | | `content()` | `HTMLElement` | `.crop-sections--item-content` | | `header()` | `HTMLElement` | The `
` inside the `
` -- put the title (and a collapse toggle) here | | `body()` | `HTMLElement` | The inner `
` (scrolls vertically) | | `articles()` | `HTMLElement[]` | The `
` children of the body (one by default; append more to `body()`) | | `width([px])` | number | Setter (`parseInt`; `0` / `NaN` keeps the old value) + re-render; the platform sets it on every layout pass | | `height([px])` | number | Same, set by the platform | | `fixedWidth([px])` | number | `0` (default) = fluid; a positive number = fixed width in px, taken out of the free width | | `collapsed([bool])` | boolean | Collapses / expands the section (`collapsed` class on `.crop-sections--item`: 48 px wide, header rotated 90 degrees, body hidden). The platform redistributes the width on its next pass (within 500 ms) | | `render()` | -- | Applies width / height / collapsed to the DOM (margins 5 px horizontal, 10 px vertical; body `max-height = height - 60`) | | `config()` | object | The configuration object passed to `addSection()` (live) | | `name([name])` | string or `null` | Getter / setter of `config().name` | | `platform()` | platform | The owning `CropSections()` instance | | `attached()` | boolean | In the list **and** in the DOM | | `attach()` | -- | Adds the section to the list and to the container (no-op when attached) | | `detach()` | -- | Removes it from the list and from the DOM; the object stays usable, `attach()` brings it back | `width`, `height`, `fixedWidth` and `collapsed` re-render the section through their `after*` hooks when called with an argument. Two helper classes are honoured by the stylesheet inside the header: `.uncollapsed-hidden` (shown only while collapsed) and `.collapsed-hidden` (hidden while collapsed) -- use them for a toggle that changes its glyph. ## Use Cases ### A three-pane layout with a collapse toggle per section ```js Application.require("elements/crop-sections").then(function (CropSections) { var panels = CropSections(); document.getElementById('mount').appendChild(panels.node()); var addSection = function (name, fixedWidth) { var section = panels.addSection({ name: name, fixedWidth: fixedWidth || 0 }); section.header().innerHTML = '' + name + ' '; section.header().querySelector('.toggle').onclick = function () { section.collapsed(!section.collapsed()); }; return section; }; addSection('Navigation', 180); addSection('Editor'); addSection('Preview'); }); ``` ### Keeping a detached section around ```js var inspector = panels.addSection({ name: 'Inspector', fixedWidth: 260 }, true); // built, not shown inspector.header().textContent = 'Inspector'; document.getElementById('toggle-inspector').onclick = function () { if (inspector.attached()) inspector.detach(); else inspector.attach(); }; ``` ### Several articles in one section ```js var section = panels.section('Details'); ['Summary', 'History', 'Comments'].forEach(function (title) { var article = document.createElement('article'); article.innerHTML = '

' + title + '

'; section.body().appendChild(article); // articles() now returns four entries }); ``` ## Notes - The root node is `100 % x 100 %` of its parent and the height of every section is derived from it: mount the platform in an element with an explicit height. - The layout is recomputed on a 500 ms interval, never on demand -- after `collapsed()`, `fixedWidth()`, `attach()` / `detach()` or a window resize, the new widths appear within half a second; call `panels.render()` yourself for an immediate update. - The free width leaves 25 px for the container's own padding / borders; with only fixed and collapsed sections nothing is resized. - The collapse animation is a 0.5 s CSS transition (`transform`, `width`); the collapsed tab shows the header text rotated and a dashed outline. - Live demo: [playground](playground.html#module=crop-sections ':ignore :target=_blank').