Published
Edited
Jul 16, 2020
Importers
1 star
Insert cell
Insert cell
value
Insert cell
viewof value = dropZone({
message: 'text only!',
validate: item => item.kind === 'string',
process: item => new Promise(resolve => item.getAsString(resolve))
})
Insert cell
function dropZone({ message, validate, process, render, enableFile = false }) {
const id = DOM.uid().id;
const urls = [];

const area = html`<div id=${id} tabindex=-1 data-error-message="${message ||
'invalid input'}">`;
area.ondragover = e => {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
};

const input = html`<input type="file">`;
if (enableFile) {
area.appendChild(html`<span class="${id}-or">or</span>`);

const button = html`<button>Select a file`;
button.onmousedown = () => {
setTimeout(() => area.blur());
};
button.onclick = () => {
input.click();
};

area.appendChild(button);
}

function updateValue(items) {
const value = (() => {
for (const item of items) {
if (!validate || validate(item)) {
return process ? process(item) : item;
}
}
})();
if (value) {
area.innerHTML = '';
view.value = value;
render && render(area, value);
setTimeout(() => view.dispatchEvent(new CustomEvent('input')), 100);
area.blur();
} else {
area.classList.add('invalid');
setTimeout(() => area.classList.remove('invalid'), 2000);
}
}

area.ondrop = e => {
e.preventDefault();
updateValue(e.dataTransfer.items);
};
area.onpaste = e => {
updateValue(e.clipboardData.items);
};
input.onchange = () => {
updateValue(input.files);
};

const view = html`<div>${area}<style>${style(id)}</style></div>`;
return view;
}
Insert cell
style = id => `
@keyframes ${id}-flash {
0% { opacity: 0 }
}

@keyframes ${id}-wiggle {
${(100 / 6) * 0}% { left: 0 }
${(100 / 6) * 1}% { left: 1em }
${(100 / 6) * 2}% { left: 0 }
${(100 / 6) * 3}% { left: -1em }
${(100 / 6) * 4}% { left: 0 }
${(100 / 6) * 5}% { left: 1em }
${(100 / 6) * 6}% { left: 0 }
}

#${id} {
--color: hsl(150,0%,60%);
--bg: hsla(150,0%,60%,50%);
width: 100%;
box-sizing: border-box;
height: 350px;
max-width: 100%;
padding: 50px;
border: 3px solid var(--color);
border-radius: 1vmin;
background: repeating-linear-gradient(-45deg, transparent 0 50px, var(--bg) 0 100px);
outline: none;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
#${id}:focus, #${id} button { --color: hsl(224, 53%, 49%); --bg: hsla(224, 53%, 49%, 50%); }
#${id}.invalid { --color: hsl(0, 53%, 49%); --bg: hsla(0, 53%, 49%, 50%); }
#${id}:focus::before { content: "Paste away!"; }


#${id}::before, #${id} button {
content: "click or drop";
display: inline-block;
vertical-align: middle;
text-align: center;
font-size: 1.5em;
font-family: sans-serif;
color: white;
background: var(--color);
font-weight:bold;
text-transform: uppercase;
padding: .2em .5em;
border-radius: 20px;
position: relative;
border: none;
}
#${id}.invalid::before, #${id}.invalid button {
animation: 0.3s ease-out ${id}-wiggle;
content: attr(data-error-message);
}

#${id} button {
margin-top: 1em;
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}

.${id}-or {
font-family: sans-serif;
text-transform: uppercase;
font-size: 1.5em;
margin-top: 1em;
color: #666;
}
`
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