initial
This commit is contained in:
commit
c66017e0e0
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 318 B |
513
index.html
Normal file
513
index.html
Normal file
@ -0,0 +1,513 @@
|
||||
<span style='
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: block; max-width: 720px; margin-left: auto; margin-right: auto;
|
||||
'>
|
||||
<span contenteditable autofocus onfocus="(function(){
|
||||
document.title = 'OnlySpans.net';
|
||||
[...document.getElementsByTagName('SPAN')].forEach(function(n) {
|
||||
/* send a synthetic 'dblclick' event to each span */
|
||||
var evt = document.createEvent('MouseEvents');
|
||||
evt.initEvent('dblclick', true, true);
|
||||
n.dispatchEvent(evt);
|
||||
|
||||
/* NOTE: styles and event handler can be added inline. These are added in
|
||||
an event handler to demonstrate how to emulate CSS classes */
|
||||
|
||||
var S = n.style;
|
||||
|
||||
/* configure 'details' spans */
|
||||
if(n.classList.contains('details')) {
|
||||
/* styling */
|
||||
S['font-weight'] = 700;
|
||||
S['background-color'] = '#54c7ec26';
|
||||
S['border-radius'] = '5px';
|
||||
S['padding'] = '5px';
|
||||
S['border'] = '1px solid #4cb3d4';
|
||||
S['display'] = 'inline-block';
|
||||
n.innerHTML = '▶ Code (click to show)';
|
||||
/* click handler */
|
||||
n.addEventListener('click', function(){
|
||||
if(/click to hide/.test(n.innerText)) {
|
||||
n.nextElementSibling.style.display = 'none';
|
||||
n.innerHTML = '▶ Code (click to show)';
|
||||
} else {
|
||||
n.nextElementSibling.style.display = 'inline-block';
|
||||
n.innerHTML = '▼ Code (click to hide)';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* configure 'codeblock' spans */
|
||||
if(n.classList.contains('codeblock')) {
|
||||
S['padding'] = '5px';
|
||||
S['font-family'] = 'monospace';
|
||||
S['display'] = 'none';
|
||||
S['background-color'] = '#efefef';
|
||||
S['border-radius'] = '5px';
|
||||
S['border'] = '1px solid #d4d5d8';
|
||||
}
|
||||
|
||||
/* configure spans with 'href' attribute */
|
||||
if(n.hasAttribute('href')) {
|
||||
S['white-space'] = 'pre-wrap';
|
||||
S['cursor'] = 'pointer';
|
||||
S['color'] = 'blue';
|
||||
n.addEventListener('click', function() {
|
||||
window.location.href = n.getAttribute('href');
|
||||
});
|
||||
}
|
||||
|
||||
/* configure spans with 'heading' role */
|
||||
if(n.hasAttribute('role')) {
|
||||
switch(n.getAttribute('role')) {
|
||||
case 'heading': switch(n.getAttribute('aria-level')) {
|
||||
case '2': {
|
||||
S['font-weight'] = '700';
|
||||
S['font-size'] = '1.5rem';
|
||||
} break;
|
||||
case '3': {
|
||||
S['font-weight'] = '700';
|
||||
S['font-size'] = '1.25rem';
|
||||
S['white-space'] = 'pre-wrap';
|
||||
} break;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
});
|
||||
/* hide element (this probably should be removed) */
|
||||
event.target.style.display = 'none';
|
||||
})()"></span>
|
||||
|
||||
<span role="heading" aria-level="1" style="
|
||||
white-space: pre;
|
||||
font-weight: 700;
|
||||
font-size: 2rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: block;
|
||||
">Only <SPAN>s</span>
|
||||
|
||||
<span style="
|
||||
white-space: pre-wrap;
|
||||
">
|
||||
Each element on this page is a SPAN element. No elements are dynamically added to the page.
|
||||
</span>
|
||||
|
||||
<span style="
|
||||
white-space: pre-wrap;
|
||||
">
|
||||
<span><span role="heading" aria-level="2">Styles
|
||||
</span>
|
||||
<span>Each SPAN specifies styling properties through the `style` attribute.</span>
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
">This is <span style="font-weight: 700">bold</span>
|
||||
This is <span style="font-style: italic">italicized</span>
|
||||
<span style="text-decoration: line-through">These effects are impossible</span>
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
This is <span style="font-weight: 700">bold</span>
|
||||
This is <span style="font-style: italic">italicized</span>
|
||||
<span style="text-decoration: line-through">These effects are impossible</span>
|
||||
```</span></span></span>
|
||||
<span><span role="heading" aria-level="3">Line Breaks</span></span>
|
||||
|
||||
Newline characters are normally treated like spaces. They do not force a line break. `BR` elements force line breaks.
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
"><span style="white-space: normal;">Normally, newline characters
|
||||
do not actually break lines. They
|
||||
are treated like space characters.</span>
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span style="white-space: normal;">Normally, newline characters
|
||||
do not actually break lines. They
|
||||
are treated like space characters.</span>
|
||||
```</span></span></span>
|
||||
<span>When SPAN elements have the styling property <span style="font-family: monospace">white-space: pre-wrap</span>, newline characters in the HTML code are treated as line breaks.</span>
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
"><span style="white-space: pre-wrap;">With pre-wrap, newline characters
|
||||
actually break lines and
|
||||
are not treated like space characters</span>
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span style="white-space: pre-wrap;">With pre-wrap, newline characters
|
||||
actually break lines and
|
||||
are not treated like space characters</span>
|
||||
```</span></span></span></span>
|
||||
<span><span role="heading" aria-level="3">CSS Tables</span>
|
||||
|
||||
<span style="
|
||||
white-space: pre-wrap;
|
||||
">CSS Level 3 supports special values for the `display` styling property:</span>
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
"><span style="display: table">
|
||||
<span style="display: table-row">
|
||||
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; text-align:center; display:inline-block; width: 100%">Value</span></span>
|
||||
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; text-align:center; display:inline-block; width: 100%">Behavior</span></span>
|
||||
</span>
|
||||
<span style="display: table-row">
|
||||
<span style="display: table-cell; padding: 3px;"><span style="font-family:monospace; width: 100%">table</span></span>
|
||||
<span style="display: table-cell; padding: 3px;"><span style="width: 100%">Acts like <span style="font-family:monospace">TABLE</span></span></span>
|
||||
</span>
|
||||
<span style="display: table-row">
|
||||
<span style="display: table-cell; padding: 3px;"><span style="font-family:monospace; width: 100%">table-row</span></span>
|
||||
<span style="display: table-cell; padding: 3px;"><span style="width: 100%">Acts like <span style="font-family:monospace">TR</span></span></span>
|
||||
</span>
|
||||
<span style="display: table-row">
|
||||
<span style="display: table-cell; padding: 3px;"><span style="font-family:monospace; width: 100%">table-cell</span></span>
|
||||
<span style="display: table-cell; padding: 3px;"><span style="width: 100%">Acts like <span style="font-family:monospace">TD</span></span></span>
|
||||
</span>
|
||||
</span>
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span style="display: table">
|
||||
<span style="display: table-row">
|
||||
<span style="display: table-cell">Value</span>
|
||||
<span style="display: table-cell">Behavior</span>
|
||||
</span>
|
||||
<span style="display: table-row">
|
||||
<span style="display: table-cell">table</span>
|
||||
<span style="display: table-cell">Acts like TABLE</span>
|
||||
</span>
|
||||
</span>
|
||||
```</span></span></span>
|
||||
<span>Using these special style properties, SPAN elements can create simple table layouts. Unfortunately there is no equivalent of `colspan` and `rowspan` ("merge cells" in Excel parlance)
|
||||
</span></span>
|
||||
<span><span role="heading" aria-level="3">Images</span>
|
||||
|
||||
<span style="
|
||||
white-space: pre-wrap;
|
||||
">Images can be added to pages using the `background-image` inline style. To ensure spans are displayed, the `width`, `height` and `display` attributes are set:
|
||||
</span>
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
"><span style="
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
background-image: url('https://sheetjs.com/sketch128.png');
|
||||
display:inline-block;
|
||||
"></span>
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span style="
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
background-image: url('https://sheetjs.com/sketch128.png');
|
||||
display: inline-block;
|
||||
"></span>
|
||||
```</span></span></span>
|
||||
</span></span>
|
||||
|
||||
<span style="
|
||||
white-space: pre-wrap;
|
||||
">
|
||||
<span><span role="heading" aria-level="2">Dynamic Elements</span>
|
||||
|
||||
<span>Inline event handlers allow for some scripting without introducing explicit SCRIPT tags.</span>
|
||||
|
||||
<span>The following example uses the `onclick` inline event handler to increment a counter:</span>
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
">The blue span has been clicked <span style="
|
||||
font-family: monospace;
|
||||
font-weight:700;
|
||||
background-color: #54c7ec26;
|
||||
border-radius: 5px;
|
||||
padding:5px; border: 1px solid #4cb3d4;
|
||||
display: inline-block;
|
||||
" onclick="(function() {
|
||||
event.target.innerText = (+event.target.innerText) + 1;
|
||||
})()">0</span> times
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span onclick="(function() {
|
||||
event.target.innerText = (+event.target.innerText) + 1;
|
||||
})()">0</span>
|
||||
```</span></span></span>
|
||||
<span><span role="heading" aria-level="3">Effectuating `onload`</span></span>
|
||||
|
||||
<span>SPAN elements do not receive a `load` event. Instead, this page uses a `contenteditable` SPAN with an `autofocus` attribute. This ensures the `onfocus` inline handler is called.</span>
|
||||
|
||||
<span>If multiple elements have the `autofocus` attribute, only the first element will receive focus.</span>
|
||||
|
||||
<span>To simplify the page, the `contenteditable autofocus` SPAN will send a synthetic double click (`dblclick`) event to each SPAN element on the page before hiding itself:</span>
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
">The relevant SPAN is at the top of the page (line 6 in the source). If it were placed here, the browser would scroll to this SPAN.
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span contenteditable autofocus onfocus="(function(){
|
||||
[...document.getElementsByTagName('SPAN')].forEach(function(n) {
|
||||
var evt = document.createEvent('MouseEvents');
|
||||
evt.initEvent('dblclick', true, true);
|
||||
n.dispatchEvent(evt);
|
||||
});
|
||||
event.target.style.visibility = 'hidden';
|
||||
})()"></span>
|
||||
```</span></span></span>
|
||||
<span>Within the `ondblclick` inline handler, the SPAN DOM element can be referenced with the `<span style="font-family: monospace">target</span>` property of the `<span style="font-family: monospace">event</span>` global.</span>
|
||||
|
||||
<span>The following example shows the current time. The `ondblclick` event handler uses `setInterval` to set up a loop that updates the time. Clearing the `ondblclick` property ensures that a real double click will not invoke the original handler.</span>
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
">The time is now <span style="
|
||||
font-family: monospace;
|
||||
font-weight:700;
|
||||
background-color: #efefef;
|
||||
border-radius: 5px;
|
||||
padding:5px; border: 1px solid #d4d5d8;
|
||||
display: inline-block;
|
||||
" ondblclick="(function() {
|
||||
var last = new Date().toString(), tgt = event.target;
|
||||
tgt.innerText = last;
|
||||
setInterval(function() {
|
||||
var cur = new Date().toString();
|
||||
if(last != cur) tgt.innerText = last = cur;
|
||||
}, 100);
|
||||
tgt.ondblclick = null;
|
||||
})()"></span>
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span style="font-family: monospace" ondblclick="(function() {
|
||||
var last = new Date().toString(), tgt = event.target;
|
||||
tgt.innerText = last;
|
||||
setInterval(function() {
|
||||
var cur = new Date().toString();
|
||||
if(last != cur) tgt.innerText = last = cur;
|
||||
}, 100);
|
||||
tgt.ondblclick = null;
|
||||
})()"></span>
|
||||
```</span></span></span></span>
|
||||
<span><span role="heading" aria-level="3">Emulating CSS Classes</span>
|
||||
|
||||
<span>Within the `onfocus` handler of the `autofocus` SPAN, every SPAN element on the page is visited. The `classList` property is a list of class names. Code can manually apply styling properties.</span>
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
">The code blocks that are displayed with the "Show Code" buttons use the `codeblock` class.
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span contenteditable autofocus onfocus="(function(){
|
||||
[...document.getElementsByTagName('SPAN')].forEach(function(n) {
|
||||
/* configure 'codeblock' spans */
|
||||
if(n.classList.contains('codeblock')) {
|
||||
var S = n.style;
|
||||
S['font-family'] = 'monospace';
|
||||
S['display'] = 'none';
|
||||
}
|
||||
})()"></span>
|
||||
```</span></span></span></span>
|
||||
<span><span role="heading" aria-level="3">Fetching Data</span>
|
||||
|
||||
<span>The `ondblclick` handler can fetch data. The following example fetches a text file:</span>
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
">The current version of <span href="https://sheetjs.com" role="link">SheetJS Community Edition</span> is <span style="
|
||||
font-family: monospace;
|
||||
font-weight:700;
|
||||
background-color: #efefef;
|
||||
border-radius: 5px;
|
||||
padding:5px;
|
||||
border: 1px solid #d4d5d8;
|
||||
display: inline-block;
|
||||
" ondblclick="(async function() {
|
||||
const versions = await (await fetch('https://cdn.sheetjs.com/xlsx.lst')).text();
|
||||
event.target.innerText = versions.trim().split('\n').reverse()[0];
|
||||
})()"></span>
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span ondblclick="(async function() {
|
||||
const versions = await (await fetch('https://cdn.sheetjs.com/xlsx.lst')).text();
|
||||
event.target.innerText = versions.trim().split('\n').reverse()[0];
|
||||
})()"></span>
|
||||
```</span></span></span></span>
|
||||
<span><span role="heading" aria-level="3">Editable Elements</span>
|
||||
|
||||
<span>The `contenteditable` attribute allows users to edit the content of SPAN elements. An `input` event is dispatched when the content has changed.</span>
|
||||
|
||||
<span>The following example includes one `contenteditable` SPAN and one non-editable SPAN. The `oninput` event handler of the first SPAN changes the `innerText` property of the second SPAN.
|
||||
</span>
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
">Click the text span below and start typing:
|
||||
|
||||
<span contenteditable style="background-color: white; border-radius: 5px; padding:5px; border: 1px solid #009400; display: inline-block;" oninput="(function() {
|
||||
/* output is the ID of the span that will be changed */
|
||||
output.innerText = event.target.innerText||' ';
|
||||
})()">Click here and start typing!</span>
|
||||
|
||||
The following span will update when the previous span is edited:
|
||||
|
||||
<span><span id="output" style="background-color: #efefef; border-radius: 5px; padding:5px; border: 1px solid #d4d5d8; display: inline-block;">Edit the previous span</span></span></span></span>
|
||||
<span><span role="heading" aria-level="3">External Scripts</span>
|
||||
|
||||
<span>External scripts can be fetched and evaluated using `eval`.</span>
|
||||
|
||||
<span>The following example fetches the <span href="https://docs.sheetjs.com/docs/constellation/ssf" role="link">SheetJS SSF Library</span> and displays the version number.
|
||||
</span>
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
">The current version of the SheetJS SSF number formatting library is <span style="
|
||||
font-family: monospace; font-weight:700;
|
||||
background-color: #efefef;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
border: 1px solid #d4d5d8;
|
||||
display: inline-block;
|
||||
" ondblclick="(async function() {
|
||||
const url = 'https://cdn.sheetjs.com/ssf-0.11.3/ssf.js';
|
||||
const code = await (await fetch(url)).text();
|
||||
eval(code);
|
||||
window.SSF = SSF;
|
||||
event.target.innerText = SSF.version;
|
||||
})()"></span>
|
||||
|
||||
<span><span class="details">Show Code</span>
|
||||
<span class="codeblock">```html
|
||||
<span ondblclick="(async function() {
|
||||
const url = 'https://cdn.sheetjs.com/ssf-0.11.3/ssf.js';
|
||||
const code = await (await fetch(url)).text();
|
||||
eval(ssf_text);
|
||||
event.target.innerText = SSF.version;
|
||||
})()"></span>
|
||||
```</span></span></span>
|
||||
<span>This example uses the <span href="https://docs.sheetjs.com/docs/constellation/ssf" role="link">SheetJS SSF Library</span> to format values. The SPAN elements with green borders are editable. When either SPAN is changed, the "Number Value" SPAN content is interpreted as a JS `Number` and formatted using the Format Code.</span>
|
||||
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
border-radius: 10px;
|
||||
">The "Formatted Text" is automatically calculated from the "Format Code" and "Number Value". Try setting the "Format Code" to <span style="font-family: monospace; font-weight: 700">#,##0.00</span> and the value to <span style="font-family: monospace; font-weight: 700">314159.265</span>
|
||||
|
||||
<span style="display: table">
|
||||
<span style="display: table-row">
|
||||
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; display:inline-block; width: 100%">Format Code</span></span>
|
||||
<span style="display: table-cell; padding: 3px;"><span id="fmt" contenteditable style="font-family: monospace; width: 100%; background-color: white; border-radius: 5px; padding:5px; border: 1px solid #009400; display: inline-block; min-width: 11ch;" oninput="(function() {
|
||||
var evt = document.createEvent('MouseEvents');
|
||||
evt.initEvent('dblclick', true, true);
|
||||
outssf.dispatchEvent(evt);
|
||||
})()">$0.00</span></span>
|
||||
</span>
|
||||
<span style="display: table-row">
|
||||
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; display:inline-block; width: 100%">Number Value</span></span>
|
||||
<span style="display: table-cell; padding: 3px;"><span id="val" contenteditable style="font-family: monospace; width: 100%; background-color: white; border-radius: 5px; padding:5px; border: 1px solid #009400; display: inline-block;" oninput="(function() {
|
||||
var evt = document.createEvent('MouseEvents');
|
||||
evt.initEvent('dblclick', true, true);
|
||||
outssf.dispatchEvent(evt);
|
||||
})()">3.5</span></span>
|
||||
</span>
|
||||
<span style="display: table-row"><span style="display: table-cell;"> </span></span>
|
||||
<span style="display: table-row">
|
||||
<span style="display: table-cell; padding: 3px;"><span style="font-weight: 700; display:inline-block; width: 100%">Formatted Text</span></span>
|
||||
<span style="display: table-cell; padding: 3px;"><span id="outssf" style="font-family: monospace; font-weight: 700; width: 100%; background-color: #54c7ec26; border-radius: 5px; padding:5px; border: 1px solid #4cb3d4; display: inline-block;" ondblclick="(function() {
|
||||
if(typeof SSF == 'undefined') return;
|
||||
/* simple debounce wrapper */
|
||||
function debounce(f) {
|
||||
let id;
|
||||
return function() {
|
||||
if(id) window.cancelAnimationFrame(id);
|
||||
id = window.requestAnimationFrame(() => f.call(this, ...arguments));
|
||||
}
|
||||
}
|
||||
debounce(() => {
|
||||
const v = Number(val.innerText), S = outssf.style;
|
||||
try {
|
||||
if(!isFinite(v)) throw 'Could not parse ' + val.innerText + ' as a finite number!';
|
||||
outssf.innerText = SSF.format(fmt.innerText, Number(val.innerText));
|
||||
S['background-color'] = '#54c7ec26';
|
||||
S['border-color'] = '#4cb3d4';
|
||||
} catch(e) {
|
||||
outssf.innerText = 'ERROR: ' + (e && e.message || e);
|
||||
S['background-color'] = '#fa383e26';
|
||||
S['border-color'] = '#e13238';
|
||||
}
|
||||
})();
|
||||
})()">$3.50</span></span>
|
||||
</span>
|
||||
</span></span></span>
|
||||
</span>
|
||||
|
||||
<span style="
|
||||
white-space: pre-wrap;
|
||||
">
|
||||
<span style="
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
display: block;
|
||||
background-color: #e6f6e6;
|
||||
border-radius: 10px;
|
||||
"><span href="https://sheetjs.com" role="link">SheetJS LLC</span> has been "making sheet happen" with creative use of HTML, CSS and JS since 2012. If pushing the limits of the web tickles your fancy, <span href="https://sheetjs.com/careers" role="link">consider joining us!</span></span>
|
||||
|
||||
</span>
|
||||
</span>
|
Loading…
Reference in New Issue
Block a user