JSFiddle - React, Tailwind, and code Playground

Collapsible

by tonytlwu

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.10/handlebars.min.js"></script>
<script type="text/html" id="template-collapsible">
  <div class="panel panel-default" data-collapse-id="{{id}}">
    <h4 class="panel-title collapsed" target="#collapse-{{id}}" data-toggle="collapse">{{title}}</h4>
    <div class="panel-collapse collapse" id="collapse-{{id}}">
      <div class="panel-body"></div>
    </div>
  </div>
</script>
<p class="item-1">Paragraph 1</p>
<p class="item-2">Paragraph 2</p>
<div data-fl-widget-instance><div data-collapsible-start-id="34">Paragraphs 3 - 4</div></div>
<p class="item-3">Paragraph 3</p>
<p class="item-4">Paragraph 4</p>
<div data-fl-widget-instance><div data-collapsible-start-id="56">Paragraphs 5 - 6</div></div>
<p class="item-5">Paragraph 5</p>
<div class="widget-5-1" data-fl-widget-instance></div>
<p class="item-5-2">Paragraph 5.2</p>
<div class="widget-5-3" data-fl-widget-instance></div>
<p class="item-6">Paragraph 6</p>
<div data-fl-widget-instance><div data-collapsible-end-id></div></div>
<p class="item-7">Paragraph 7</p>
<p class="item-8">Paragraph 8</p>
<div data-fl-widget-instance><div data-collapsible-start-id="910">Paragraphs 9 - 10</div></div>
<p class="item-9">Paragraph 9</p>
<p class="item-10">Paragraph 10</p>

CSS

[data-collapsible-start-id] {
  font-weight: bold;
  padding-bottom: 4px;
  border-bottom: 1px solid green;
}

[data-collapsible-end-id] {
  border-bottom: 1px solid red;
}

.panel {
  border: 1px solid #ddd;
  border-radius: 4px;
  margin-bottom: 20px;
  padding: 15px;
}

JavaScript

+ function($) {
  'use strict';

  // COLLAPSIBLE CLASS DEFINITION
  // ======================

  var Collapsible = function(el) {
    // Plugin initialization
  }
  
  Collapsible.SELECTORS = {
    start: '[data-collapsible-start-id]',
    end: '[data-collapsible-end-id]',
    widget: '[data-fl-widget-instance]'
  }
    
  Collapsible.prototype.widgetIsCollapsibleStart = function ($widget){
    return $widget.find(Collapsible.SELECTORS.start).length > 0;
  }

  Collapsible.prototype.widgetIsCollapsibleEnd = function ($widget){
    return $widget.find(Collapsible.SELECTORS.end).length > 0;
  }

  Collapsible.prototype.widgetIsCollapsible = function ($widget){
    return $widget.find(Collapsible.SELECTORS.start).length > 0
    	|| $widget.find(Collapsible.SELECTORS.end).length > 0;
  }

  // COLLAPSIBLE INITIALIZATION
  // =====================

  $.fn.collapsify = function (){
    return this.each(function () {
    	// Find the widget container for Collapsible Start
      var $collapsibleStart = $(this);
      var id = $collapsibleStart.data('collapsible-start-id').toString();
      var $startWidget = $collapsibleStart.closest(Collapsible.SELECTORS.widget);
      var title = $collapsibleStart.html();
      
      // Find all subsequent content nodes
      var $content = $startWidget.nextUntil(Collapsible.SELECTORS.widget);
      var $next;
      if ($content.length) {
        $next = $content.last().next();
      } else {
        $next = $startWidget.next();
      }
      
      while ($next.length && !Collapsible.prototype.widgetIsCollapsible($next)) {
      	$content = $content.add($next);
      	$content = $content.add($next.nextUntil(Collapsible.SELECTORS.widget));
        $next = $content.last().next();
      }
      if (Collapsible.prototype.widgetIsCollapsibleEnd($next)) {
      	$next.remove();
      }

      //
      var collapsibleTemplate = Handlebars.compile($('#template-collapsible').html());
      var data = {
      	id: id,
        title: title
   ...