Signal drop!
Relay (operand.online) is unreachable.
Usually, a dropped signal means an upgrade is happening. Hold on!
Sorry, no connección.
Hang in there while we get back on track
gram: shape
> ./src/App.jsx
Lenses
(coming soon!)
import React from 'react';
import logo from './logo.svg';
import DataColumn from './DataColumn.jsx';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.appDiv = React.createRef();
this.state = {
columns: [
{ id: "events", name: "events", visible: false, ref: React.createRef(), children: new Set(), formulaType: "javascript", query: "[]" },
{
id: 1,
name: "UI",
visible: true,
ref: React.createRef(),
children: new Set(),
formulaType: "html",
query:
`<div>
New todo:
<input value={$newTodo.text || ""} />
<button
value={$newTodo.text}
metadata="addBtn">add</button>
<div>
{$todos.map (t => {
return <div>{t.text}</div>
})}
</div>
</div>`
},
{
id: 2,
name: "newTodo",
visible: true,
ref: React.createRef(),
children: new Set(),
formulaType: "javascript",
query:
`{ text: $events.reduce((_, e) => {
// update text when user types in box
if (e.type === "input") { return e.value }
// clear input box when "add" is clicked
else if (e.type === "click" &&
e.metadata === "addBtn"){
return ""
}
// ignore other events
else { return _ }
}, "")}`
},
{ id: 3,
name: "todos",
visible: true,
ref: React.createRef(),
children: new Set(),
formulaType: "html",
query:
`$events
.reduce((list, e) => {
// When "add" is clicked, add todo
if (e.type === "click" &&
e.metadata === "addBtn"){
return list.concat({text: e.value})
}
else { return list }
}, [])`
},
{ id: 4, name: "c4", visible: true, ref: React.createRef(), children: new Set(), formulaType: "javascript", query: "" },
{ id: 5, name: "c5", visible: true, ref: React.createRef(), children: new Set(), formulaType: "javascript", query: "" },
{ id: 6, name: "c6", visible: true, ref: React.createRef(), children: new Set(), formulaType: "javascript", query: "" },
{ id: 7, name: "c7", visible: true, ref: React.createRef(), children: new Set(), formulaType: "javascript", query: "" },
{ id: 8, name: "c8", visible: true, ref: React.createRef(), children: new Set(), formulaType: "javascript", query: "" },
{ id: 9, name: "c9", visible: true, ref: React.createRef(), children: new Set(), formulaType: "javascript", query: "" },
{ id: 10, name: "c10", visible: true, ref: React.createRef(), children: new Set(), formulaType: "javascript", query: "" }
],
context: {},
events: []
}
}
handleColOutputChange = (colId, output, deps) => {
let updatedCol = this.state.columns.find(c => c.id === colId)
this.setState(state => {
let columns = state.columns.map ((c) => {
if (c === updatedCol) {
// this is the column that got updated;
// update output and dependencies
return { ...c, output: output, deps: deps }
} else if (deps.includes(c.name)) {
// this column should now have the updated column
// as a child to update when it updates
let newChildren = c.children.add(updatedCol.id)
// console.log(c.name, "-> register child ->", updatedCol.name, ", new children: ", newChildren)
return { ...c, children: newChildren }
} else {
return c
}
})
let context = {}
columns.forEach(c => {
context[c.name] = c.output
})
return {
columns: columns,
context: context
}
},
() => {
// update the context on this cell itself (but don't re-evaluate!)
updatedCol.ref.current.manualUpdate(this.state.context, false)
// update context on child cells, and propagate changes forward
this.state.columns.filter(c => updatedCol.children.has(c.id)).forEach(c => {
console.log(updatedCol.name, "-> trigger ->", c.name)
c.ref.current && c.ref.current.manualUpdate(this.state.context, true)
})
}
)
}
handleColNameChange = (colId, name) => {
this.setState(state => {
let columns = state.columns.map ((c) => {
if (c.id === colId) {
return { ...c, name: name }
} else { return c; }
})
let context = {}
columns.forEach(c => {
context[c.name] = c.output
})
return {
columns: columns,
context: context
}
})
}
// React doesn't use actual DOM events;
// this is a wrapper to unwrap React events
// and then add them to the magic $events variable
// (note: this is no longer used since we stopped using react event handlers
// to respond to DOM events)
// addSyntheticEventToEventsColumn = (e) => {
// e.persist()
// this.addNativeEventToEventsColumn(e.nativeEvent)
// }
addNativeEventToEventsColumn = (e) => {
let metadata =
e.target &&
e.target.getAttribute("metadata")
// metadata = JSON.parse(metadata)
let nativeEvent = {
type: e.type,
// References to DOM objects get weird when we try to JSON output them...
// todo: keep DOM nodes around longer, just don't print them
// target: e.nativeEvent.target.attributes,
// srcElement: e.nativeEvent.srcElement,
metadata: metadata,
value: e.target.value,
x: e.x,
y: e.y
}
// Important note:
// we can't push to the events array in place--
// we have to make a copy,
// in order to trigger re-evaluation of the events array
// in other places (like the ReactJSON component)
// console.log("processing", e.nativeEvent)
this.state.events = this.state.events.concat(nativeEvent)
this.handleColOutputChange("events", this.state.events, [])
}
handleChange = (e) => {
this.addNativeEventToEventsColumn(e)
}
// a utility function to help with updating all columns.
// runs a manual update on a given column;
// once that finishes up, use a callback to recursively
// run updates on all the remaining columns.
updateColumnAndSuccessors = (c) => {
c.ref.current && c.ref.current.manualUpdate(this.state.context, true, () => {
let nextCol = this.state.columns.find(next => next.id === c.id + 1)
if (nextCol) {
this.updateColumnAndSuccessors(nextCol)
}
})
}
componentDidMount() {
// cycle through all the columns once and trigger updates,
// to correctly initialize the state of the sheet
this.updateColumnAndSuccessors(this.state.columns[0])
}
render() {
let dataColumns = this.state.columns.map((c) => {
return <div className={"data-column " + (c.visible ? '' : 'hidden')}>
<DataColumn
key={c.id}
colId={c.id}
visible={c.visible} // todo: actually use this in the datacol
handleColOutputChange={this.handleColOutputChange}
handleColNameChange={this.handleColNameChange}
eventHandlers={{ click: this.addNativeEventToEventsColumn, input: this.addNativeEventToEventsColumn }}
ref={c.ref}
formulaType={c.formulaType}
query={c.query}
name={c.name}
/>
</div>
})
return (
<div>
<div className="app" ref={this.appDiv}>
{dataColumns}
</div>
</div>
);
}
}
export default App;