ƒ

calculang ✍ī¸ editable and dangerous! 🧙‍♂ī¸âš ī¸
dev tools 🧰

select-F9 🧙‍♂ī¸ javascript


introspection:

reactive workings
complete javascript ✨ generated from calculang âŦ†ī¸
đŸ¤Ģ

// USE ONE FROM lifelib-to-calculang

REACTIVE FORMULAS:

REACTIVE INPUTS:

Loan Checker

important disclaimers ❗

⚠ī¸ The assumptions, methodology and limitations of a model should be carefully considered for any purpose you apply it to. I haven't even listed these here, nor otherwise documented the model fully/clearly. Proceed with caution 🙏

⚠ī¸ This is not a complete model/narrative and you should not rely on it.

⚠ī¸ Not recommended to enter real personal or sensitive details. Contact me with real-world calculation or model requirements instead. đŸ—Ŗ✅

⚠ī¸ This validator/scenario tool isn't/won't be financial advice etc.

💡 Click/drag to see workings (buggy suspected due to limitations in vega-lite; todo move to vega)

const spec = ({
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "width": 400,
  datasets: {'source': [] },
  "encoding": {
        "x": {"field": "year_in"},
        "y": {"field": "value", "type": "quantitative"},
  },
  "layer": [
    {
      "transform": [{"filter": "datum.formula != 'balance'"}],
      "mark": {"type": "bar", "tooltip": true},
      "encoding": {
        "color": {"field": "formula", "type": "nominal", legend: {orient: 'top'}}
      },
  params: [
    {
      name: "formula",
      //value: { ray_angle_in: 0 },
      select: {
        type: "point",
        //on: "mousemove{0,50}",
        on: "[touchdown, touchup] > touchmove, mouseup, touchup, [mousedown, mouseup] > mousemove",
        //on: "[touchdown, touchup] > touchmove, [mousedown, mouseup] > mousemove", //{10, 100}",
        //        on: "[mousedown, mouseup] > mousemove",
        nearest: true,
        toggle: false,
        clear: false,
        encodings: ["x", "color"]
      }
    }
  ],
    },
    {
      "transform": [{"filter": "datum.formula == 'balance'"}],
      "mark": {"type": "line", "tooltip": true, "point": false},
      "encoding": {
        "color": {"value": "red"},
        "y": {"field": "value", "type": "quantitative", "title": "balance"}
      }
    },
    {
      "transform": [{"filter": "datum.formula == 'balance'"}],
      "mark": {"type": "point", "tooltip": true, filled:true, opacity: 0.7},
      "encoding": {
        "color": {"value": "red", legend: true},
        size: {value: 200},
                "x": {"field": "year_in"},

        "y": {"field": "value", "type": "quantitative", "title": "balance", zero: true}
      },
  /*params: [
    {
      name: "balance",
      //value: { ray_angle_in: 0 },
      select: {
        type: "point",
        //on: "mousemove{0,50}",
        on: "[touchdown, touchup] > touchmove, mouseup, touchup, [mousedown, mouseup] > mousemove",
        //on: "[touchdown, touchup] > touchmove, [mousedown, mouseup] > mousemove", //{10, 100}",
        //        on: "[mousedown, mouseup] > mousemove",
        nearest: true,
        toggle: false,
        clear: false,
        encodings: ["x", "y"]
      }
    }
  ],*/
    }
  ],
  "resolve": {"scale": {"y": "independent"}}
})



// ({
//   // vega-lite
//     encoding: {
//     x: { field: 'formula', type: 'nominal', "axis": {"labelAngle": -30, "orient": "top", title:null, "labelLimit": 300, labelFontSize: 16} },
//     y: { field: 'i_in' },
//     color: {field: 'value', legend: false},
//     text: {field: 'value', },
//     size: {value: 12}
//   },
//   layer: [

//     {
//       mark: {type:'text', fontWeight:'bold', dx:2, dy:2},
//       transform: [{filter: "datum.highlight"}],
//       encoding: {
//             size: {value: 15},
//         color: {value:'yellow'}
//       }
//     },
//     {
//       mark: {type:'text', tooltip:false, from: 'data'},
//             //transform: [{filter: "!datum.current"}], // messes selection; better out or using conditional size
//       encoding: {
//         size: { value: 16, condition: {test: 'datum.current', value: 1} }
//       },
//         params: [
//     {
//       name: "formula",
//       //value: { ray_angle_in: 0 },
//       select: {
//         type: "point",
//         //on: "mousemove{0,50}",
//         on: "[touchdown, touchup] > touchmove, mouseup, touchup, [mousedown, mouseup] > mousemove",
//         //on: "[touchdown, touchup] > touchmove, [mousedown, mouseup] > mousemove", //{10, 100}",
//         //        on: "[mousedown, mouseup] > mousemove",
//         nearest: true,
//         toggle: false,
//         clear: false,
//         encodings: ["x", "y"]
//       }
//     }
//   ]

//     },
//     {
//       mark: {type:'text', fontWeight:'bold', dx:0, dy:0},
//       transform: [{filter: "datum.current"}],
//       encoding: {
//         color: {value:'black'},
//         size: {value: 20}
//       }
//     },
//   ],

//   data: { name: "data" },
//   autosize: { "type": "fit", "contains": "padding"},
//   // no reactive w/h due to https://github.com/observablehq/framework/issues/1194
//   width: 450,//Math.min(500,content_width-30),//Math.min(400,content_width),
//   height: 600,//Math.min(500,content_width-30)/1.2,//Math.min(400,content_width-30),
//   background:'rgba(0,0,0,0)',
// })

// interactivity via vega signals and listeners
const viz = embed('#viz', spec)
// is this data_source_with_highlights data flow ok ... ?
viz.view.data("source", data_source_with_highlights).resize().run(); // turn off resize
const data_source = calcudata({
  models: [model],
  input_domains: {year_in:_.range(-1,cursor.term_in+0.1)},
  input_cursors: [cursor],
  outputs: ["capital_repayment", "interest_repayment", "balance"],
  pivot: false
});

display(data_source)
viz.view.addSignalListener("formula", (a, b) => {
  console.log('formula signal', a, b)
  //alert(a, b)
  // no modularity here:
  //const newScope = +(fns_fromDefinition.find(d => (d.cul_scope_id == 0 && d.name == b.formula[0])).fromDefinition.split('_')[0])
  //if (newScope !== editor.scope)
    //editor.setScope(+(fns_fromDefinition.find(d => (d.cul_scope_id == 0 && d.name == b.formula[0])).fromDefinition.split('_')[0]))
  setCursor('year_in', b.year_in[0])
  setFormula(/*b.formula[0]*/'repayment')
         document.querySelector('.calculang_f_'+/*b.formula[0]*/'repayment').scrollIntoView(scrollIntoViewOpts)
  set_formulae_visible([/*b.formula[0]*/'repayment'])
});

/* not going to work: I need to use Vega
viz.view.addSignalListener("balance", (a, b) => {
  console.log('formula signal', a, b)

  alert('ih')
    setCursor('year_in', b.year_in[0])
  setFormula('balance')
         document.querySelector('.calculang_f_'+'balance').scrollIntoView(scrollIntoViewOpts)
  set_formulae_visible(['balance'])

})*/