Public
Edited
Jun 13
Insert cell
Insert cell
container = html `<div style="height:300px"></div>`
Insert cell
html`
${viewof dropdown_cell}<br>
${viewof dropdown_gene}<br>
${viewof dropdown_img}<br>`
Insert cell
obs_store.dropdown_cell.set('all')
Insert cell
// obs_store.dropdown_cell.set('blue')
Insert cell
Insert cell
// update deck properties
obs_store.newProps.subscribe(() => {

if (obs_store.newProps.get()){

console.log('setting newProps ')

deckgl.setProps({
layers: [obs_store.cellLayer.get(), obs_store.geneLayer.get()],
})

obs_store.newProps.set(false)
}
})
Insert cell
obs_store.propsCheck.subscribe(checklist => {

if (!obs_store.newProps.get()){
const allReady = Object.values(checklist).every(v => v)
if (allReady) {
// safe to run setProps now
console.log('ready to set props', obs_store.propsCheck.get())
obs_store.newProps.set(true)
} else {
console.log('not ready to set props', obs_store.propsCheck.get())
}
}
})

Insert cell
obs_store.newCell.subscribe(() => {

if (obs_store.newCell.get()){

console.log('update cell')

obs_store.cellLayer.set(obs_store.cellLayer.get().clone({
id: 'cell-' + obs_store.dropdown_cell.get()
}))

obs_store.newCell.set(false)

// tell the checklist that the cellLayer is ready
obs_store.propsCheck.set({
...obs_store.propsCheck.get(),
cellLayer: true
})
}
})
Insert cell
obs_store.newGene.subscribe(() => {

if (obs_store.newGene.get()){

console.log('update gene')

obs_store.geneLayer.set(obs_store.geneLayer.get().clone({
id: 'gene-' + obs_store.dropdown_gene.get()
}))

obs_store.newGene.set(false)

// tell the checklist that the gene layer is ready
obs_store.propsCheck.set({
...obs_store.propsCheck.get(),
geneLayer: true
})
}
})
Insert cell
obs_store.propsCheck.get()
Insert cell
obs_store.cellLayer.set( new deck.ScatterplotLayer({
id: 'cell-all',
data: [
{position: [-122.402, 37.79], color: [255, 0, 0], radius: 100, name: 'red'},
{position: [-122.399, 37.79], color: [0, 0, 255], radius: 100, name: 'blue'},
{position: [-122.396, 37.79], color: [0, 0, 0], radius: 100, name: 'black'},
],
getPosition: d => d.position,
getColor: d => {
var inst_color
if (obs_store.dropdown_cell.get() === 'all'){
inst_color = d.color
} else {
if (obs_store.dropdown_cell.get() === d.name)
inst_color = d.color
else {
inst_color = [...d.color, 25]
}
}
return inst_color
},
getRadius: d => d.radius,
opacity: 0.75,
pickable: true,
onClick: d => {
if (obs_store.dropdown_cell.get() !== d.object.name){
obs_store.dropdown_cell.set(d.object.name)
} else {
obs_store.dropdown_cell.set(obs_store.dropdown_cell.getDefault())
}
// tell the layers to update
obs_store.newCell.set(true)
obs_store.newGene.set(true)

// reset the checklist
obs_store.propsCheck.set(obs_store.propsCheck.getDefault())
}
}))
Insert cell
obs_store.geneLayer.set(new deck.ScatterplotLayer({
id: 'gene-all',
data: [
{position: [-122.402, 37.7885], color: [255, 0, 0], radius: 20, name: 'red'},
{position: [-122.399, 37.7885], color: [0, 0, 255], radius: 20, name: 'blue'},
{position: [-122.396, 37.7885], color: [0, 0, 0], radius: 20, name: 'black'},
],
getPosition: d => d.position,
// getColor: d => d.color,
getColor: d => {
var inst_color
if (obs_store.dropdown_gene.get() === 'all'){
inst_color = d.color
} else {
if (obs_store.dropdown_gene.get() === d.name)
inst_color = d.color
else {
inst_color = [...d.color, 25]
}
}
return inst_color
},
getRadius: d => d.radius,
opacity: 0.75,
pickable: true,
onClick: d => {
if (obs_store.dropdown_gene.get() !== d.object.name){
obs_store.dropdown_gene.set(d.object.name)
} else {
obs_store.dropdown_gene.set(obs_store.dropdown_cell.getDefault())
}
// // say that a new layer needs to be made
// obs_store.newCell.set([])

// tell the layers to update
obs_store.newCell.set(true)
obs_store.newGene.set(true)

// reset the checklist
obs_store.propsCheck.set(obs_store.propsCheck.getDefault())
}
}))
Insert cell
deckgl.setProps({
layers: [obs_store.cellLayer.get(), obs_store.geneLayer.get()],
controller: { doubleClickZoom: false },
initialViewState: {
longitude: -122.402,
latitude: 37.79,
zoom: 15,
pitch: 0,
bearing: 0,
},
})
Insert cell
deckgl = new deck.DeckGL({
container,
controller: true,
})
Insert cell
deck = require.alias({
// optional dependencies
h3: {}
})('deck.gl@latest/dist.min.js')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function makeResettableDropdown({ key, label, options, resetTriggers, obs_store }) {
const dropdown = Inputs.select(options, {
label,
value: obs_store[key].getDefault()
});

// Input → Observable
dropdown.addEventListener("input", () => {
// console.log(`user input: ${key}:`, dropdown.value);
obs_store[key].set(dropdown.value);
obs_store.newCell.set(true)
// console.log("");
});

// Observable → Input
obs_store[key].subscribe(val => {
if (dropdown.value !== val) {
dropdown.value = val;
// console.log(`obs input: ${key}:`, val);
}
});

// Reset if triggered by another dropdown
const resetIfNeeded = () => {
if (obs_store.suppressReset.get()) {
// console.log(`suppressed: ${key}`);
return;
}

if (obs_store[key].get() !== obs_store[key].getDefault()) {
obs_store.suppressReset.set(true);
// console.log(`suppressReset: ${key}: on`);
obs_store[key].set(obs_store[key].getDefault());
obs_store.suppressReset.set(false);
// console.log(`suppressReset: ${key}: off`);
} else {
// console.log(`already default: ${key}`);
}
};

// set subscribe function
for (const triggerKey of resetTriggers) {
obs_store[triggerKey].subscribe(resetIfNeeded);
}

return dropdown;
}

Insert cell
Insert cell
function Observable(initialValue) {
let value = initialValue;
const subscribers = new Set()

return {
get: () => value,
getDefault: () => initialValue,
set: newValue => {
if (value !== newValue) {
value = newValue;
subscribers.forEach(fn => fn(value));
}
},
subscribe: fn => {
subscribers.add(fn);
fn(value); // Call immediately with current value
return () => subscribers.delete(fn); // Unsubscribe function
}
}
}
Insert cell
obs_store = ({
dropdown_cell: Observable("all"),
dropdown_gene: Observable("all"),
dropdown_img: Observable("all"),
suppressReset: Observable(false),
newCell: Observable(false),
newGene: Observable(false),
newProps: Observable(false),
cellLayer: Observable(null),
geneLayer: Observable(null),
propsCheck: Observable({cellLayer: false, geneLayer: false})
})
Insert cell
obs_store.dropdown_cell.set('black')
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