Implement list import/export

This commit is contained in:
Thomas Weber committed 2021-07-01 21:04:20 -05:00
1 parent 1fbca7dde4
commit fbd510ce73
3 files changed
+158 -7

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
import styles from './style.css';
class ContextMenu {
constructor (parent) {
this.parent = parent;
this.root = document.createElement('div');
this.root.className = styles.contextMenu;
}
_onmousedown (e) {
if (!this.root.contains(e.target)) {
this.destroy();
}
}
add (option) {
const item = document.createElement('button');
item.className = styles.contextMenuItem;
item.textContent = option.text;
item.addEventListener('click', () => {
this.destroy();
option.callback();
});
item.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
this.root.appendChild(item);
}
show (mouseEvent) {
document.addEventListener('mousedown', this._onmousedown.bind(this));
const layersRect = this.parent.layersRect;
const x = mouseEvent.clientX - layersRect.left + 1;
const y = mouseEvent.clientY - layersRect.top + 1;
this.root.style.transform = `translate(${x}px, ${y}px)`;
this.parent._addLayer(this.root);
}
destroy () {
this.root.remove();
}
}
export default ContextMenu;
+80 -7
View File
@@ -1,3 +1,4 @@
import ContextMenu from './context-menu';
import styles from './style.css';
class Monitor {
@@ -80,6 +81,18 @@ class Monitor {
return label;
}
getTarget () {
if (this.targetId) {
return this.parent.vm.runtime.getTargetById(this.targetId);
}
return this.parent.vm.runtime.getTargetForStage();
}
getVmVariable () {
const target = this.getTarget();
return target.variables[this.id];
}
update (monitor) {
// TODO: don't touch if not changed
this.x = monitor.get('x');
@@ -144,13 +157,7 @@ class VariableMonitor extends Monitor {
}
setVariableValue (value) {
let target;
if (this.targetId) {
target = this.parent.vm.runtime.getTargetById(this.targetId);
} else {
target = this.parent.vm.runtime.getTargetForStage();
}
const variable = target.variables[this.id];
const variable = this.getVmVariable();
variable.value = value;
this._value = value;
this.valueElement.textContent = value;
@@ -254,6 +261,10 @@ class ListMonitor extends Monitor {
this.root.appendChild(this.label);
this.root.appendChild(this.rowsOuter);
this.root.appendChild(this.footer);
this.handleImport = this.handleImport.bind(this);
this.handleExport = this.handleExport.bind(this);
this.root.addEventListener('contextmenu', this._oncontextmenu.bind(this));
}
_onscroll (e) {
@@ -270,6 +281,68 @@ class ListMonitor extends Monitor {
this.updateValue(this.value);
}
_oncontextmenu (e) {
e.preventDefault();
const menu = new ContextMenu(this.parent);
menu.add({
text: 'import',
callback: this.handleImport
});
menu.add({
text: 'export',
callback: this.handleExport
});
menu.show(e);
}
handleImport () {
const fileSelector = document.createElement('input');
fileSelector.type = 'file';
fileSelector.accept = '.txt,.csv,.tsv';
fileSelector.style.display = 'none';
document.body.appendChild(fileSelector);
fileSelector.addEventListener('change', (e) => {
const files = e.target.files;
if (files.length === 0) return;
const file = files[0];
const reader = new FileReader();
reader.onload = () => {
const text = reader.result;
// TODO: this doesn't exactly match Scratch behavior
const lines = text.split(/\r?\n/);
this.setValue(lines);
};
reader.readAsText(file);
});
fileSelector.click();
}
handleExport () {
const value = this.getValue();
const exported = value.join('\n');
const blob = new Blob([exported], {
type: 'text/plain'
});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.download = `${this.getLabel()}.txt`;
link.href = url;
document.body.appendChild(link);
link.click();
link.remove();
}
getValue () {
return this.getVmVariable().value;
}
setValue (value) {
const variable = this.getVmVariable();
variable.value = value;
this.updateValue(value);
}
getRow () {
if (this.rows.length) {
return this.rows.pop();
+33
View File
@@ -211,3 +211,36 @@
align-items: center;
flex-wrap: nowrap;
}
.context-menu {
position: absolute;
background-color: white;
color: black;
display: flex;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
box-shadow: 0px 0px 5px 1px hsla(0, 0%, 0%, 0.15);
min-width: 130px;
}
.context-menu-item {
background: none;
border: none;
border-radius: 0;
cursor: pointer;
text-align: left;
font-family: inherit;
font-size: 0.85em;
padding: 8px 12px;
transition: .1s background-color, .1s color;
}
.context-menu-item:focus {
background-color: #ff4c4c55;
border: none;
outline: none;
}
.context-menu-item:hover,
.context-menu-item:active {
color: white;
background-color: #ff4c4c;
}