JSFiddle - React, Tailwind, and code Playground
by Luke May
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.4/js/tether.js"></script>
<div class="wrapper">
<div class="container root gen0" data-bind="
click: root().onClick,
clickBubble: false
">
<div class="wrapper">
<div class="container alice gen1" data-bind="
click: root().children()[0].onClick,
clickBubble: false
">
<div class="wrapper">
<div class="container amy gen2" data-bind="
click: root().children()[0].children()[0].onClick,
clickBubble: false
">
</div>
</div>
<div class="wrapper">
<div class="container artemis gen2" data-bind="
click: root().children()[0].children()[1].onClick,
clickBubble: false
">
</div>
</div>
</div>
</div>
<div class="wrapper">
<div class="container briana gen1" data-bind="
click: root().children()[1].onClick,
clickBubble: false
">
<div class="wrapper">
<div class="container bridget gen2" data-bind="
click: root().children()[1].children()[0].onClick,
clickBubble: false
">
</div>
</div>
<div class="wrapper">
<div class="container betsy gen2" data-bind="
click: root().children()[1].children()[1].onClick,
...
CSS
body {
position: absolute;
width: calc(100% - 100px);
height: calc(100% - 100px);
padding: 50px;
margin: 0;
background-color: #eeeeee;
}
.container {
display: inline-block;
padding: 10px;
margin: 10px;
border-width: 1px;
border-style: solid;
position: relative;
overflow: auto;
}
.gen0 {
border-color: #c52;
}
.gen1 {
border-color: #25c;
}
.gen2 {
border-color: #2c5;
}
.wrapper {
margin: 0;
padding: 0;
outline: black solid 1px;
overflow: auto;
}
.root {
width: 800px;
height: 600px;
}
.alice {
display: inline-block;
width: 300px;
height: 500px;
}
.amy {
display: inline-block;
width: 100px;
height: 1200px;
}
.artemis {
display: inline-block;
width: 100px;
height: 400px;
}
.briana {
display: inline-block;
width: 300px;
height: 500px;
}
.bridget {
width: 600px;
height: 600px;
}
.betsy {
width: 200px;
height: 200px;
}
.sc-corner {
width: 10px;
height: 10px;
border: solid 1px red;
}
.sc-corner.tether-out-of-bounds {
display: none;
}
JavaScript
var cornerPieces = {};
var tethers = {};
var selectedComponents = {};
var createCornerPieces = function (id) {
var target = selectedComponents[id];
var $target = $(target);;
if (!cornerPieces[id]) {
cornerPieces[id] = {};
cornerPieces[id].tl = $('<div class="sc-corner corner-top-left"></div>');
cornerPieces[id].tr = $('<div class="sc-corner corner-top-right"></div>');
cornerPieces[id].bl = $('<div class="sc-corner corner-bottom-left"></div>');
cornerPieces[id].br = $('<div class="sc-corner corner-bottom-right"></div>');
var $parent = $target.parent().closest(".container");
if (!$parent.length) {
$parent = $target.parent();
}
$parent.append(cornerPieces[id].tl);
$parent.append(cornerPieces[id].tr);
$parent.append(cornerPieces[id].bl);
$parent.append(cornerPieces[id].br);
}
};
var destroyCornerPieces = function (id) {
_.each(cornerPieces[id], function (val) {
if (val && val.remove) {
val.remove();
}
});
delete cornerPieces[id];
};
var createTethers = function (id) {
tethers[id] = {};
tethers[id].tl = new Tether({
element: cornerPieces[id].tl[0],
target: selectedComponents[id][0],
attachment: 'top left',
targetAttachment: 'top left',
constraints: [
{
to: 'scrollParent'
}
]
});
tethers[id].tr = new Tether({
element: cornerPieces[id].tr[0],
target: selectedComponents[id][0],
attachment: 'top right',
targetAttachment: 'top right',
constraints: [
{
to: 'scrollParent'
}
]
});
tethers[id].bl = new Tether({
element: cornerPieces[id].bl[0],
target: selectedComponents[id][0],
attachment: 'bottom left',
targetAttachment: 'bottom left',
constraints: [
{
...