Rebecca Lester, Stanford Graduate School of Business
Plot =import("https://esm.sh/@observablehq/plot@0.6.13")//redivis = require("redivis")//reg = await redivis// .user("stanford_templates")// .dataset("subsidy_map")// .table("state_disclosure_regimes")// .listRows()//reg = FileAttachment("regimes.csv").csv()reg =transpose(regime_data)dir = (int, ext) => ( [int ==="Y"?"internal":"", ext ==="Y"?"external":""].filter(Boolean))regimes = reg.map(obj => ({...obj,year:newDate(obj.year.toString(),0,1),direction:dir(obj.internal, obj.external)}));// map// adapted from https://observablehq.com/@mbostock/u-s-state-mapviewof state = {let value =null;const colors = { excluded:"#ccc",included:"#B83A4B" };const dim =0.6;const svg = d3.create("svg").attr("viewBox", [0,0,975,610])const statesGroup = svg.append("g");// state shapes statesGroup.selectAll("path").data(topojson.feature(us, us.objects.states).features).enter().append("path").attr("d", path).attr("data-id", d => d.id).attr("fill", d => states.get(d.id).included? colors.included: colors.excluded).attr("fill-opacity", dim).on("click", (event, d) => {if (states.get(d.id).included) {// set value to clicked stateconst node = svg.node(); node.value= value = value === d.id?null: d.id; node.dispatchEvent(newEvent("input", {bubbles:true}));// change clicked state opacity statesGroup.selectAll("path").attr("fill-opacity", d => value === d.id?1: dim) } }).on("mouseover", (event, d) => {if (states.get(d.id).included) {// change hovered state opacity statesGroup.select(`path[data-id="${d.id}"]`).attr("fill-opacity",1); } }).on("mouseout", (event, d) => {if (states.get(d.id).included) {// reset hovered state opacity statesGroup.select(`path[data-id="${d.id}"]`).attr("fill-opacity", d => value === d.id?1: dim); } });// text labels statesGroup.selectAll("text").data(topojson.feature(us, us.objects.states).features).enter().append("text").filter(d => states.get(d.id).included).attr("transform", d =>`translate(${path.centroid(d)})`).attr("dy","0.35em").attr("text-anchor","middle").attr("pointer-events","none").text(d => states.get(d.id).abbr);// outlines svg.append("path").datum(topojson.mesh(us, us.objects.states, (a, b) => a !== b)).attr("fill","none").attr("stroke","white").attr("stroke-linejoin","round").attr("pointer-events","none").attr("d", path);returnObject.assign(svg.node(), {value:null});}us =FileAttachment("states-albers-10m.json").json() // map topologyabbr =FileAttachment("states-abbr.json").json() // state names + abbreviationsstates =newMap(us.objects.states.geometries.map( d => [d.id, {"name": d.properties.name,"abbr": abbr[d.properties.name],"included": regimes.map(d => d.state).includes(d.properties.name)}]))path = d3.geoPath()
state_obj = states.get(state)state_name = state_obj ? state_obj["name"] :null// all regimes with selected state variablereg_state = regimes.map(obj => ({...obj,curr_state: state_name ? obj.state=== state_name :true}));// only regimes for selected statereg_curr = reg_state.filter(d => d.curr_state)caption = htl.html`<div class="caption">Click on a highlighted state to view its incentive programs and disclosure regimes.<br>Click again on the selected state to view all regimes.</div>`// dot plotviewof timeline = Plot.plot({height:500,width:400,x: {label:"Year"},color: {range: ["#ccc","#B83A4B"]},marks: [ Plot.dot(reg_state, Plot.dodgeY({x:"year",fill:"curr_state",title: (d) => [d.state, d.disclosure_law].join("\n"),sort:"curr_state",reverse:true,tip:true }) ), ]})// place map and plot side by sidehtml`<div style="display: flex; align-items: center; margin-bottom: 2em;"> <div style="flex-basis:50%"> ${viewof state}${caption} </div> <div style="flex-basis:50%"> ${viewof timeline} </div></div>`
viewof reg_search = Inputs.search(reg_curr)// tableviewof row = Inputs.table(reg_search, {rows:100,// rows displayed at start// sort: "year", reverse: true, // reverse chronological ordermultiple:false,// only select one row at a timerequired:false,// if nothing is selected, selection is emptycolumns: ["state","year","direction","disclosure_law", ],header: {state:"State",year:"Year",direction:"",disclosure_law:"Disclosure law", },format: {year: x => x.getFullYear(),direction: badgify }})badgify = x => htl.html`<div>${x.map(y => htl.html.fragment` <span class="badge ${y}">${y}</span>`)}</div>`
pullout = (head, text) => htl.html.fragment`<b>${head}</b><p>${text}</p>`placeholder = htl.html.fragment`<p class="pullout"><i>Select a row to see more information...</i></p>`viewof pullouts = row ? htl.html.fragment`${pullout("Subsidy program affected", row.program)}${pullout("State statute", row.state_statute)}${pullout("Excerpt", row.excerpt)}`: placeholderhtl.html`<div class="grid-container"> <div> ${viewof row} </div> <div class="pullout"> ${viewof pullouts} </div></div>`