Published
Edited
Mar 2, 2022
6 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
selectorMacros = {
// Utility. Turns a combination of class names into a selector string.
const cc = str => str.split(' ').map(str => '.' + str).join('');
// Utility. Produces a selector that matches exactly one class.
const cs = str => `[class="${str}"]`;
const cn = str => str.split(' ').map(str => ':not(.' + str + ')').join('');

return {
// Outer shared structure.
'$PAGE': 'div' + cc('w-100 mw8 ph3 center mt4'),

'$TOOLBAR': 'div' + cc('w-100 mw8 ph3 center flex justify-between pv2 items-center'),
'$TOOLBAR_LOGO': 'a' + cc('flex items-center ph2-ns pv2 mr3 mr2-ns no-underline'),
'$TOOLBAR_SEARCH': 'input' + cc('w5 f6 ba b--black-10 input-reset bg-white outline-0 search mr1 mr2-m mr3-l pl4 pr3 br2 fw6'),
'$TOOLBAR_BUTTON': 'a' + cc('inline-flex pa2 mr3-ns mr2 f6 fw6 no-underline br2 bn'),
'$CONTAINER': 'div' + cc('relative mh0-ns nr3 nl3 mt5') + ' > div' + cc('relative outline-0'),
// Cells container.
'$EDITOR_AREA': 'div' + cc('absolute w-100 black pen'),
// Output container.
'$SANDBOX_AREA': 'iframe' + cc('w-100 bn'),
// ???
'$UNKNOWN_AREA': 'div.vh-25',
// A single cell input, containing label, actions and editor.
'$CELL': '$EDITOR_AREA > div' + cs('relative'),
// Like above, but without the parent. Used in sibling selectors.
'$CELL_REL': 'div' + cs('relative'),
// Insert/Merge button.
'$ACTION_INSERT': 'a' + cc('absolute pointer pea hover-dark-blue hover-bg-near-white br-pill'),
'$ACTION_INSERT_ACTIVE': '$ACTION_INSERT' + cc('black-30'),
'$ACTION_INSERT_INACTIVE': '$ACTION_INSERT' + cn('black-30'),
//
'$ASIDE_WRAP': 'div' + cc('absolute pointer pea tr top-0 br'),
'$ASIDE_WRAP_ACTIVE': '$ASIDE_WRAP.b--black',

'$ASIDE_LABEL': '$ASIDE_WRAP > div' + cs('absolute'),
'$ASIDE_LABEL_REL': '> div' + cs('absolute'),
'$ASIDE_ICON_ARROW': '$ASIDE_WRAP > svg',
'$ASIDE_ACTIONS': 'div' + cc('absolute right-0 ma1'),
'$ASIDE_ICON_LINK': '$ASIDE_ACTIONS > a' + cc('dib black-30 hover-black') + '[title^="Link"]',
'$ASIDE_ACTION_PIN': '$ASIDE_ACTIONS > a' + cc('dib br-pill v-top') + '[title*="pin"]',

'$EDITOR_WRAP': 'div' + cc('relative transparent hover-black-30 bg-near-white pea'),
'$EDITOR_WRAP_INACTIVE': '$ACTION_INSERT_INACTIVE ~ $EDITOR_WRAP',
'$EDITOR_ACTION_SAVE': 'a' + cc('absolute top-0 right-0 pa2 z-2 pointer'),
'$EDITOR_CODE': 'div.CodeMirror',
'$EDITOR_CODE_INACTIVE': 'div.CodeMirror:not(.CodeMirror-focused)',
};
}
Insert cell
Insert cell
Insert cell
function init(selectors, plugins, options) {
// Code runs outside sandbox, invoked via bookmarklet.
'use strict';
if(window.TABS_UI_UNLOAD) return window.TABS_UI_UNLOAD();
var doc = document,
select = doc.querySelector.bind(doc),
main = select('#app'),
iframe = select(selectors.$SANDBOX_AREA),
worker = iframe ? iframe.contentWindow : null,
handlers = {};

if(!main || !main._reactRootContainer) return console.warn('Editor not found.');
if(!worker) return console.warn('Sandbox not found.');
var pluginApi = {
selectors: selectors,
document: doc,
root: doc.documentElement,
worker: worker,
select: select,
create: doc.createElement.bind(doc),
append: doc.body.appendChild.bind(doc.body),
randomId: randomId,

on: function(type, callback) {
if(!handlers[type]) handlers[type] = new Set();
handlers[type].add(callback);
},
off: function(type, callback) {
if(handlers[type]) handlers[type].delete(callback);
},
trigger: function(type, data) {
if(handlers[type]) handlers[type].forEach(function(fn) { fn(data); });
}
};
plugins.forEach(function(fn) { fn(pluginApi, options); });
pluginApi.on('unload', function() {
delete window.TABS_UI_UNLOAD;
});
window.TABS_UI_UNLOAD = pluginApi.trigger.bind(null, 'unload');
pluginApi.trigger('load');
function randomId() {
return +('' + Date.now() + ~~(1000 * Math.random()));
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof editorStyles = print(`
body {
overflow: hidden !important;
}

$PAGE {
margin: 0;
max-width: initial;
--tabsui-editor-width: 70%;
--tabscol-shade: rgba(0, 0, 0, .05);
--tabscol-lightest: #f7f7f9;
--tabscol-light: hsl(240, 14%, 93%);
--tabscol-darkest: #6f7c90;
--tabscol-accent: #266bd9;
}

$TOOLBAR {
max-width: initial;
padding: 2px 6px;
z-index: 2;
box-shadow: 0 2px 4px 2px rgba(0,0,0,.1);
padding-bottom: 4px;
}
$TOOLBAR_LOGO {
padding: 0;
}

$CONTAINER {
display: flex;
flex-direction: row;
position: fixed;
width: 100%;
top: 40px;
bottom: 0;
left: 0;
z-index: 1;
background: white;
overflow: hidden;
}
$CONTAINER:fullscreen,
$CONTAINER:-moz-full-screen,
$CONTAINER:-webkit-full-screen {
top: 0;
}
/* Resize handle. */
$CONTAINER:after {
content: "";
display: block;
flex: 0 0 6px;
order: 1;
background: #ccc;
cursor: move;
}

$EDITOR_AREA,
$SANDBOX_AREA {
position: relative !important;
height: 100% !important;
overflow: auto;
overflow-y: scroll;
}
$SANDBOX_AREA {
order: 2;
flex: 0 0 50vw;
}

$EDITOR_AREA {
order: 0;
flex: 1 1 auto;
pointer-events: initial;
width: initial !important;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
align-content: flex-start;
counter-reset: index 0;
padding: 9px 3px 6px 6px;
}

/* Separates tabs and editor */
$EDITOR_AREA:after {
content: "";
order: 2;
height: 1px;
background: var(--tabscol-shade);
margin: 0 -3px 18px -6px;
display: block;
flex: 1 0 auto;
width: 100%;
}

$CELL {
margin-top: auto !important;
margin-bottom: 50px !important;
padding-top: initial !important;
position: relative;
display: contents;
}

$ACTION_INSERT,
$ASIDE_WRAP,
$EDITOR_WRAP {
order: 1;
}

$ACTION_INSERT {
flex: 0 0 auto;
opacity: 0;
display: block;

top: 0 !important;
left: 0 !important;
width: 13px !important;
height: auto !important;
line-height: 32px;
padding: 0 !important;
z-index: 101;
overflow: hidden;

margin: 0 0 0 -13px;

position: relative;
background: var(--tabscol-darkest);
border-radius: initial;

color: white;
}
$ACTION_INSERT:hover {
opacity: 1;
}
$CELL:first-child $ACTION_INSERT {
display: none;
}

$ACTION_INSERT > svg {
transform: rotate(90deg);
position:relative;
left: -1px;
vertical-align: -4px;
}

$ASIDE_WRAP {
flex: 1 0 auto;
width: initial !important;
height: initial !important;
top: 0 !important;
left: initial !important;
position: relative !important;
z-index: 1;

white-space: nowrap;
padding: 6px 28px 2px 10px!important;
margin: 0 6px 6px 0;
background: var(--tabscol-lightest);
color: var(--tabscol-darkest);
border: none;
border-bottom: 2px solid transparent;
line-height: 18px;
text-align: left;
outline: 2px solid #fff;
}
$CELL:not([id]) $ASIDE_WRAP {
}
$CELL:hover $ASIDE_WRAP {
position: sticky !important;
z-index: 100;
}
$ASIDE_WRAP_ACTIVE {
border-bottom-color: var(--tabscolr-darkest);
}

$ASIDE_LABEL {
padding: 0;
text-align: left;
display: inline-block;
position: static;
text-overflow: ellipsis;
overflow: hidden;
counter-increment: index;
color: var(--tabscol-darkest);
font-family: -apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica,helvetica neue,ubuntu,roboto,noto,segoe ui,arial,sans-serif;
font-weight: 600;
}
$ASIDE_LABEL:empty {
}
$ASIDE_WRAP_ACTIVE $ASIDE_LABEL_REL,
$ASIDE_WRAP:hover $ASIDE_LABEL_REL {
color: black;
}

$ASIDE_LABEL:empty:after {
content: "#"counter(index);
}

$ASIDE_ACTIONS {
display: inline;
position: static;
padding: 0;
margin: 0;
}

$ASIDE_ICON_ARROW,
$ASIDE_ICON_LINK {
display: none;
}

$ASIDE_ACTION_PIN {
display: block;
padding: 0 !important;
right: 4px;
top: 8px;
position: absolute;
}

$EDITOR_WRAP {
box-sizing: border-box;
order: 3;
margin: 9px 0 9px 6px;
flex: 0 0 auto;
width: calc(100% - 12px - 9px);
position: relative;
}

$EDITOR_ACTION_SAVE {
z-index: 99;
}

$EDITOR_CODE {
display: block;
position: relative;
margin-bottom: 0 !important;
}
/* Lefthand stroke on active editor area */
$EDITOR_CODE:before {
content: "";
position: absolute;
left: 0;
width: 3px;
background-color: var(--tabscol-darkest);
height: 100%;
}
$EDITOR_WRAP_INACTIVE $EDITOR_CODE:not(:focus):before {
display: none;
}
$EDITOR_WRAP_INACTIVE $EDITOR_CODE {
max-height: 30px;
}
$EDITOR_WRAP_INACTIVE $EDITOR_CODE:after {
content: "";
position: absolute;
width: 0;
height: 0;
right: -.5px; /* Counters rounding issues */
bottom: -.5px; /* Counters rounding issues */
border-style: solid;
border-color: transparent white white transparent;
border-width: 6px 10px;
}

$UNKNOWN_AREA {
display: none;
}

$SANDBOX_AREA {
max-height: 100vh;
width: 100%;
}

$CELL:hover $ASIDE_WRAP,
$CELL:hover $EDITOR_CODE_INACTIVE {
background: var(--tabscol-light);
}

/* Option: Keep editors open */
html[data-tabsui-options~="codeNoCollapse"] $EDITOR_CODE_INACTIVE { max-height: none; }
html[data-tabsui-options~="codeNoCollapse"] $EDITOR_CODE_INACTIVE:after { display: none; }

/* Option: Disable sticky tabs */
html[data-tabsui-options~="tabNoSticky"] $CELL:hover $ASIDE_WRAP { position: relative !important; }

/* Option: List named tabs first */
html[data-tabsui-options~="tabNamedFirst"] $CELL[id] $ASIDE_WRAP { order: 0; }

/* Option: Hide unnamed cells */
html[data-tabsui-options~="tabHideUnnamed"] $CELL:not([id]) { display: none; }


`)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more