@decidables/discountable-elements 0.3.9 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decidables/discountable-elements",
3
- "version": "0.3.9",
3
+ "version": "0.4.1",
4
4
  "description": "discountable-elements: Web Components for visualizing Hyperbolic Temporal Discounting",
5
5
  "keywords": [
6
6
  "web component",
@@ -49,17 +49,17 @@
49
49
  "build": "gulp build"
50
50
  },
51
51
  "devDependencies": {
52
- "gulp": "^5.0.0"
52
+ "gulp": "^5.0.1"
53
53
  },
54
54
  "dependencies": {
55
- "@decidables/decidables-elements": "^0.4.9",
56
- "@decidables/discountable-math": "^0.1.9",
57
- "@lit-labs/motion": "^1.0.7",
58
- "@observablehq/plot": "^0.6.16",
55
+ "@decidables/decidables-elements": "^0.5.1",
56
+ "@decidables/discountable-math": "^0.2.1",
57
+ "@lit-labs/motion": "^1.0.9",
58
+ "@observablehq/plot": "^0.6.17",
59
59
  "bayes.js": "https://github.com/akrawitz/bayes.js.git#commit=c7b091b75f85a86b6a3965a983b31e23d9ef456f",
60
60
  "d3": "^7.9.0",
61
- "lit": "^3.2.1",
61
+ "lit": "^3.3.1",
62
62
  "regenerator-runtime": "^0.14.1"
63
63
  },
64
- "gitHead": "00eb1274a09e86f55fff70b01b446f6729e608f3"
64
+ "gitHead": "f4acb671770a55a5c6d0f87ae8a7bad7fb981c67"
65
65
  }
@@ -145,10 +145,10 @@ export default class DiscountableControl extends DiscountableElement {
145
145
  render() {
146
146
  return html`
147
147
  <div class="holder">
148
- ${this.trials
148
+ ${this.trials != null
149
149
  ? html`<decidables-slider min="1" max="100" step="1" .value=${this.trials} @change=${this.setTrials.bind(this)} @input=${this.setTrials.bind(this)}>Trials</decidables-slider>`
150
150
  : html``}
151
- ${this.duration
151
+ ${this.duration != null
152
152
  ? html`<decidables-slider min="10" max="4000" step="10" .value=${this.duration} @change=${this.setDuration.bind(this)} @input=${this.setDuration.bind(this)}>Duration</decidables-slider>`
153
153
  : html``}
154
154
  ${this.run || this.pause || this.reset
@@ -117,6 +117,30 @@ export default class DiscountableResponse extends DiscountableElement {
117
117
  this.response = undefined;
118
118
  }
119
119
 
120
+ keydown(event) {
121
+ if (this.state === 'waiting') {
122
+ if (event.key === 'ArrowLeft') {
123
+ this.responded('first');
124
+ event.preventDefault();
125
+ } else if (event.key === 'ArrowRight') {
126
+ this.responded('second');
127
+ event.preventDefault();
128
+ }
129
+ }
130
+ }
131
+
132
+ connectedCallback() {
133
+ super.connectedCallback();
134
+
135
+ window.addEventListener('keydown', this.keydown.bind(this));
136
+ }
137
+
138
+ disconnectedCallback() {
139
+ window.removeEventListener('keydown', this.keydown.bind(this));
140
+
141
+ super.disconnectedCallback();
142
+ }
143
+
120
144
  static get styles() {
121
145
  return [
122
146
  super.styles,
@@ -59,7 +59,7 @@ export default class HTDCalculation extends HTDEquation {
59
59
  this.al = 50;
60
60
  this.dl = 40;
61
61
 
62
- this.k = 0.1;
62
+ this.k = HTDMath.k.DEFAULT;
63
63
 
64
64
  this.alignState();
65
65
  }
@@ -149,26 +149,62 @@ export default class HTDCalculation extends HTDEquation {
149
149
  let vl;
150
150
  let vDiff;
151
151
  if (this.numeric) {
152
- as = html`<decidables-spinner class="a as" ?disabled=${!this.interactive} step="1" .value="${this.as}" @input=${this.asInput.bind(this)}>
152
+ as = html`<decidables-spinner class="a as"
153
+ ?disabled=${!this.interactive}
154
+ step="1"
155
+ .value=${this.as}
156
+ @input=${this.asInput.bind(this)}
157
+ >
153
158
  <var class="math-var">A<sub class="subscript">ss</sub></var>
154
159
  </decidables-spinner>`;
155
- ds = html`<decidables-spinner class="d ds" ?disabled=${!this.interactive} min="0" step="1" .value="${this.ds}" @input=${this.dsInput.bind(this)}>
160
+ ds = html`<decidables-spinner class="d ds"
161
+ ?disabled=${!this.interactive}
162
+ min="0"
163
+ step="1"
164
+ .value=${this.ds}
165
+ @input=${this.dsInput.bind(this)}
166
+ >
156
167
  <var class="math-var">D<sub class="subscript">ss</sub></var>
157
168
  </decidables-spinner>`;
158
- al = html`<decidables-spinner class="a al" ?disabled=${!this.interactive} step="1" .value="${this.al}" @input=${this.alInput.bind(this)}>
169
+ al = html`<decidables-spinner class="a al"
170
+ ?disabled=${!this.interactive}
171
+ step="1"
172
+ .value=${this.al}
173
+ @input=${this.alInput.bind(this)}
174
+ >
159
175
  <var class="math-var">A<sub class="subscript">ll</sub></var>
160
176
  </decidables-spinner>`;
161
- dl = html`<decidables-spinner class="d dl" ?disabled=${!this.interactive} min="0" step="1" .value="${this.dl}" @input=${this.dlInput.bind(this)}>
177
+ dl = html`<decidables-spinner class="d dl"
178
+ ?disabled=${!this.interactive}
179
+ min="0"
180
+ step="1"
181
+ .value=${this.dl}
182
+ @input=${this.dlInput.bind(this)}
183
+ >
162
184
  <var class="math-var">D<sub class="subscript">ll</sub></var>
163
185
  </decidables-spinner>`;
164
- k = html`<decidables-spinner class="k" ?disabled=${!this.interactive} min="0" max="100" step=".001" .value="${this.k}" @input=${this.kInput.bind(this)}>
186
+
187
+ k = html`<decidables-spinner class="k"
188
+ ?disabled=${!this.interactive}
189
+ min=${HTDMath.k.MIN}
190
+ max=${HTDMath.k.MAX}
191
+ step=${HTDMath.k.STEP}
192
+ .value=${+this.k.toFixed(3)}
193
+ @input=${this.kInput.bind(this)}
194
+ >
165
195
  <var class="math-var">k</var>
166
196
  </decidables-spinner>`;
167
197
 
168
- vs = html`<decidables-spinner class="v vs" disabled .value="${+this.vs.toFixed(2)}">
198
+ vs = html`<decidables-spinner class="v vs"
199
+ disabled
200
+ .value=${+this.vs.toFixed(2)}
201
+ >
169
202
  <var class="math-var">V<sub class="subscript">ss</sub></var>
170
203
  </decidables-spinner>`;
171
- vl = html`<decidables-spinner class="v vl" disabled .value="${+this.vl.toFixed(2)}">
204
+ vl = html`<decidables-spinner class="v vl"
205
+ disabled
206
+ .value=${+this.vl.toFixed(2)}
207
+ >
172
208
  <var class="math-var">V<sub class="subscript">ll</sub></var>
173
209
  </decidables-spinner>`;
174
210
  vDiff = html`${(this.vDiff > 0)
@@ -74,18 +74,12 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
74
74
  step: 1,
75
75
  round: Math.round,
76
76
  },
77
- discount: {
78
- min: 0,
79
- max: 100,
80
- step: 0.001,
81
- round: (k) => { return +k.toFixed(3); },
82
- },
83
77
  };
84
78
 
85
79
  this.a = null;
86
80
  this.d = null;
87
81
  this.label = '';
88
- this.k = 0.1;
82
+ this.k = HTDMath.k.DEFAULT;
89
83
 
90
84
  this.options = [
91
85
  {
@@ -353,6 +347,19 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
353
347
  /* HACK: This gets Safari to correctly apply the filter! */
354
348
  stroke: #0000ff;
355
349
  }
350
+
351
+ /* Make larger targets for touch users */
352
+ .interactive .touch {
353
+ stroke: #000000;
354
+ stroke-opacity: 0;
355
+ }
356
+
357
+ @media (pointer: coarse) {
358
+ .interactive .touch {
359
+ stroke-linecap: round;
360
+ stroke-width: 12;
361
+ }
362
+ }
356
363
  `,
357
364
  ];
358
365
  }
@@ -549,9 +556,33 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
549
556
  const optionEnter = optionUpdate.enter().append('g')
550
557
  .classed('option', true);
551
558
  // Curve
552
- optionEnter.append('path')
559
+ const curveEnter = optionEnter.append('g')
553
560
  .classed('curve', true)
554
- .attr('clip-path', 'url(#clip-htd-curves)')
561
+ .attr('clip-path', 'url(#clip-htd-curves)');
562
+ curveEnter.append('path')
563
+ .classed('path', true)
564
+ .attr('d', (datum) => {
565
+ const curve = d3.range(xScale(datum.d), xScale(0), -1).map((range) => {
566
+ return {
567
+ d: xScale.invert(range),
568
+ v: HTDMath.adk2v(
569
+ datum.a,
570
+ datum.d - xScale.invert(range),
571
+ this.k,
572
+ ),
573
+ };
574
+ });
575
+ return line(curve);
576
+ })
577
+ .attr('stroke-dasharray', (datum, index, nodes) => {
578
+ if (datum.trial) {
579
+ const length = nodes[index].getTotalLength();
580
+ return `0,${length}`;
581
+ }
582
+ return 'none';
583
+ });
584
+ curveEnter.append('path')
585
+ .classed('path touch', true)
555
586
  .attr('d', (datum) => {
556
587
  const curve = d3.range(xScale(datum.d), xScale(0), -1).map((range) => {
557
588
  return {
@@ -573,8 +604,23 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
573
604
  return 'none';
574
605
  });
575
606
  // Bar
576
- optionEnter.append('line')
577
- .classed('bar', true)
607
+ const barEnter = optionEnter.append('g')
608
+ .classed('bar', true);
609
+ barEnter.append('line')
610
+ .classed('line', true)
611
+ .attr('x1', (datum) => { return xScale(datum.d); })
612
+ .attr('x2', (datum) => { return xScale(datum.d); })
613
+ .attr('y1', yScale(0))
614
+ .attr('y2', (datum) => { return yScale(datum.a); })
615
+ .attr('stroke-dasharray', (datum, index, nodes) => {
616
+ if (datum.trial) {
617
+ const length = nodes[index].getTotalLength();
618
+ return `0,${length}`;
619
+ }
620
+ return 'none';
621
+ });
622
+ barEnter.append('line')
623
+ .classed('line touch', true)
578
624
  .attr('x1', (datum) => { return xScale(datum.d); })
579
625
  .attr('x2', (datum) => { return xScale(datum.d); })
580
626
  .attr('y1', yScale(0))
@@ -599,7 +645,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
599
645
  return 1;
600
646
  });
601
647
  pointEnter.append('circle')
602
- .classed('mark', true);
648
+ .classed('mark touch', true);
603
649
  pointEnter.append('text')
604
650
  .classed('label', true);
605
651
  // MERGE
@@ -609,7 +655,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
609
655
  // Curve
610
656
  optionMerge
611
657
  .filter((datum, index, nodes) => {
612
- return (this.interactive && !nodes[index].classList.contains('interactive'));
658
+ return (this.interactive && !d3.select(nodes[index]).select('.curve').classed('interactive'));
613
659
  })
614
660
  .select('.curve')
615
661
  .classed('interactive', true)
@@ -641,11 +687,11 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
641
687
  ? datum.a
642
688
  : dragV;
643
689
  const k = HTDMath.adv2k(datum.a, d, v);
644
- this.k = (k < this.scale.discount.min)
645
- ? this.scale.discount.min
646
- : (k > this.scale.discount.max)
647
- ? this.scale.discount.max
648
- : this.scale.discount.round(k);
690
+ this.k = (k < HTDMath.k.MIN)
691
+ ? HTDMath.k.MIN
692
+ : (k > HTDMath.k.MAX)
693
+ ? HTDMath.k.MAX
694
+ : k;
649
695
  this.alignState();
650
696
  this.requestUpdate();
651
697
  this.dispatchEvent(new CustomEvent('htd-curves-change', {
@@ -666,26 +712,26 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
666
712
  // Keyboard interaction
667
713
  .on('keydown', (event, datum) => {
668
714
  if (['ArrowUp', 'ArrowDown', 'ArrowRight', 'ArrowLeft'].includes(event.key)) {
669
- let keyK = this.k;
715
+ let {k} = this;
670
716
  switch (event.key) {
671
717
  case 'ArrowUp':
672
718
  case 'ArrowLeft':
673
- keyK *= event.shiftKey ? 0.95 : 0.85;
719
+ k *= event.shiftKey ? 0.95 : 0.85;
674
720
  break;
675
721
  case 'ArrowDown':
676
722
  case 'ArrowRight':
677
- keyK *= event.shiftKey ? 1.05 : 1.15;
723
+ k *= event.shiftKey ? (1 / 0.95) : (1 / 0.85);
678
724
  break;
679
725
  default:
680
726
  // no-op
681
727
  }
682
- keyK = (keyK < this.scale.discount.min)
683
- ? this.scale.discount.min
684
- : (keyK > this.scale.discount.max)
685
- ? this.scale.discount.max
686
- : this.scale.discount.round(keyK);
687
- if (keyK !== this.k) {
688
- this.k = keyK;
728
+ k = (k < HTDMath.k.MIN)
729
+ ? HTDMath.k.MIN
730
+ : (k > HTDMath.k.MAX)
731
+ ? HTDMath.k.MAX
732
+ : k;
733
+ if (k !== this.k) {
734
+ this.k = k;
689
735
  this.alignState();
690
736
  this.requestUpdate();
691
737
  this.dispatchEvent(new CustomEvent('htd-curves-change', {
@@ -705,7 +751,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
705
751
  // Bar
706
752
  optionMerge
707
753
  .filter((datum, index, nodes) => {
708
- return (this.interactive && !datum.trial && !nodes[index].classList.contains('interactive'));
754
+ return (this.interactive && !datum.trial && !d3.select(nodes[index]).select('.bar').classed('interactive'));
709
755
  })
710
756
  .select('.bar')
711
757
  .classed('interactive', true)
@@ -793,7 +839,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
793
839
  // Point
794
840
  optionMerge
795
841
  .filter((datum, index, nodes) => {
796
- return (this.interactive && !datum.trial && !nodes[index].classList.contains('interactive'));
842
+ return (this.interactive && !datum.trial && !d3.select(nodes[index]).select('.point').classed('interactive'));
797
843
  })
798
844
  .select('.point')
799
845
  .classed('interactive', true)
@@ -883,7 +929,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
883
929
  // Curve
884
930
  optionMerge
885
931
  .filter((datum, index, nodes) => {
886
- return (!this.interactive && nodes[index].classList.contains('interactive'));
932
+ return (!this.interactive && d3.select(nodes[index]).select('.curve').classed('interactive'));
887
933
  })
888
934
  .select('.curve')
889
935
  .classed('interactive', false)
@@ -893,7 +939,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
893
939
  // Bar
894
940
  optionMerge
895
941
  .filter((datum, index, nodes) => {
896
- return ((!this.interactive || datum.trial) && nodes[index].classList.contains('interactive'));
942
+ return ((!this.interactive || datum.trial) && d3.select(nodes[index]).select('.bar').classed('interactive'));
897
943
  })
898
944
  .select('.bar')
899
945
  .classed('interactive', false)
@@ -903,7 +949,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
903
949
  // Point
904
950
  optionMerge
905
951
  .filter((datum, index, nodes) => {
906
- return ((!this.interactive || datum.trial) && nodes[index].classList.contains('interactive'));
952
+ return ((!this.interactive || datum.trial) && d3.select(nodes[index]).select('.point').classed('interactive'));
907
953
  })
908
954
  .select('.point')
909
955
  .classed('interactive', false)
@@ -917,7 +963,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
917
963
  .filter((datum) => {
918
964
  return (datum.new);
919
965
  })
920
- .select('.curve').transition()
966
+ .select('.curve .path').transition()
921
967
  .duration(transitionDuration)
922
968
  .delay(transitionDuration + transitionDuration / 10)
923
969
  .ease(d3.easeLinear)
@@ -939,12 +985,35 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
939
985
  bubbles: true,
940
986
  }));
941
987
  });
988
+ optionMerge
989
+ .filter((datum) => {
990
+ return (datum.new);
991
+ })
992
+ .select('.curve .path.touch').transition()
993
+ .duration(transitionDuration)
994
+ .delay(transitionDuration + transitionDuration / 10)
995
+ .ease(d3.easeLinear)
996
+ .attrTween('stroke-dasharray', (datum, index, nodes) => {
997
+ const length = nodes[index].getTotalLength();
998
+ return d3.interpolate(`0,${length}`, `${length},${0}`);
999
+ });
942
1000
  // Bar
943
1001
  optionMerge
944
1002
  .filter((datum) => {
945
1003
  return (datum.new);
946
1004
  })
947
- .select('.bar').transition()
1005
+ .select('.bar .line').transition()
1006
+ .duration(transitionDuration)
1007
+ .ease(d3.easeLinear)
1008
+ .attrTween('stroke-dasharray', (datum, index, nodes) => {
1009
+ const length = nodes[index].getTotalLength();
1010
+ return d3.interpolate(`0,${length}`, `${length},${length}`);
1011
+ });
1012
+ optionMerge
1013
+ .filter((datum) => {
1014
+ return (datum.new);
1015
+ })
1016
+ .select('.bar .line.touch').transition()
948
1017
  .duration(transitionDuration)
949
1018
  .ease(d3.easeLinear)
950
1019
  .attrTween('stroke-dasharray', (datum, index, nodes) => {
@@ -963,7 +1032,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
963
1032
  .attrTween('opacity', () => { return d3.interpolate(0, 1); });
964
1033
 
965
1034
  // All options
966
- optionUpdate.select('.curve').transition()
1035
+ optionUpdate.select('.curve .path').transition()
967
1036
  .duration(this.drag
968
1037
  ? 0
969
1038
  : (this.firstUpdate
@@ -996,7 +1065,80 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
996
1065
  return line(curve);
997
1066
  };
998
1067
  });
999
- optionUpdate.select('.bar').transition()
1068
+ optionUpdate.select('.curve .path.touch').transition()
1069
+ .duration(this.drag
1070
+ ? 0
1071
+ : (this.firstUpdate
1072
+ ? (transitionDuration * 2)
1073
+ : transitionDuration))
1074
+ .ease(d3.easeCubicOut)
1075
+ .attrTween('d', (datum, index, elements) => {
1076
+ const element = elements[index];
1077
+ const interpolateA = d3.interpolate(
1078
+ (element.a !== undefined) ? element.a : datum.a,
1079
+ datum.a,
1080
+ );
1081
+ const interpolateD = d3.interpolate(
1082
+ (element.d !== undefined) ? element.d : datum.d,
1083
+ datum.d,
1084
+ );
1085
+ return (time) => {
1086
+ element.a = interpolateA(time);
1087
+ element.d = interpolateD(time);
1088
+ const curve = d3.range(xScale(element.d), xScale(0), -1).map((range) => {
1089
+ return {
1090
+ d: xScale.invert(range),
1091
+ v: HTDMath.adk2v(
1092
+ element.a,
1093
+ element.d - xScale.invert(range),
1094
+ this.k,
1095
+ ),
1096
+ };
1097
+ });
1098
+ return line(curve);
1099
+ };
1100
+ });
1101
+ optionUpdate.select('.bar .line').transition()
1102
+ .duration(this.drag
1103
+ ? 0
1104
+ : (this.firstUpdate
1105
+ ? (transitionDuration * 2)
1106
+ : transitionDuration))
1107
+ .ease(d3.easeCubicOut)
1108
+ .attrTween('x1', (datum, index, elements) => {
1109
+ const element = elements[index];
1110
+ const interpolateD = d3.interpolate(
1111
+ (element.d !== undefined) ? element.d : datum.d,
1112
+ datum.d,
1113
+ );
1114
+ return (time) => {
1115
+ element.d = interpolateD(time);
1116
+ return `${xScale(element.d)}`;
1117
+ };
1118
+ })
1119
+ .attrTween('x2', (datum, index, elements) => {
1120
+ const element = elements[index];
1121
+ const interpolateD = d3.interpolate(
1122
+ (element.d !== undefined) ? element.d : datum.d,
1123
+ datum.d,
1124
+ );
1125
+ return (time) => {
1126
+ element.d = interpolateD(time);
1127
+ return `${xScale(element.d)}`;
1128
+ };
1129
+ })
1130
+ .attrTween('y2', (datum, index, elements) => {
1131
+ const element = elements[index];
1132
+ const interpolateA = d3.interpolate(
1133
+ (element.a !== undefined) ? element.a : datum.a,
1134
+ datum.a,
1135
+ );
1136
+ return (time) => {
1137
+ element.a = interpolateA(time);
1138
+ return `${yScale(element.a)}`;
1139
+ };
1140
+ });
1141
+ optionUpdate.select('.bar .line.touch').transition()
1000
1142
  .duration(this.drag
1001
1143
  ? 0
1002
1144
  : (this.firstUpdate
@@ -10,7 +10,7 @@ import HTDMath from '@decidables/discountable-math';
10
10
 
11
11
  self.onmessage = (event) => {
12
12
  const params = {
13
- k: {type: 'real', lower: 0, upper: 100},
13
+ k: {type: 'real', lower: HTDMath.k.MIN, upper: HTDMath.k.MAX},
14
14
  luce: {type: 'real', lower: 0, upper: 100},
15
15
  };
16
16
 
@@ -2,6 +2,8 @@
2
2
  import {html, css} from 'lit';
3
3
  import * as Plot from '@observablehq/plot';
4
4
 
5
+ import HTDMath from '@decidables/discountable-math';
6
+
5
7
  // Special Web Worker import for rollup-plugin-web-worker-loader
6
8
  import HTDFitWorker from 'web-worker:./htd-fit-worker'; /* eslint-disable-line import/no-unresolved */
7
9
 
@@ -24,7 +26,7 @@ export default class HTDFit extends DiscountableElement {
24
26
  constructor() {
25
27
  super();
26
28
 
27
- this.k = 0.05;
29
+ this.k = HTDMath.k.DEFAULT;
28
30
 
29
31
  this.choices = [];
30
32
  this.samples = null;
@@ -136,7 +138,7 @@ export default class HTDFit extends DiscountableElement {
136
138
  <div>
137
139
  <div>After ${this.choices.length} trials:</div>
138
140
  <div>Current:
139
- <var class="math-var k">k</var> = ${this.k.toFixed(2)}
141
+ <var class="math-var k">k</var> = ${this.k.toFixed(3)}
140
142
  </div>
141
143
  <div class="param">
142
144
  <div class="trace k"></div>
@@ -161,7 +163,22 @@ export default class HTDFit extends DiscountableElement {
161
163
  marks: [
162
164
  Plot.rectY(
163
165
  this.samples[param],
164
- Plot.binX({y: 'count'}, {x: Plot.identity}),
166
+ Plot.binX(
167
+ {y: 'count'},
168
+ {x: Plot.identity},
169
+ ),
170
+ ),
171
+ Plot.rectY(
172
+ this.samples[param],
173
+ Plot.pointerX(Plot.binX(
174
+ {y: 'count'},
175
+ {
176
+ x: Plot.identity,
177
+ stroke: 'black',
178
+ fill: 'white',
179
+ tip: true,
180
+ },
181
+ )),
165
182
  ),
166
183
  ],
167
184
  }),
@@ -170,7 +187,7 @@ export default class HTDFit extends DiscountableElement {
170
187
  this.shadowRoot.querySelector(`.trace.${param}`).replaceChildren(
171
188
  Plot.plot({
172
189
  title: `Traceplot of ${param}`,
173
- x: {label: 'Samples'},
190
+ x: {label: 'Sample'},
174
191
  y: {label: `${param}`},
175
192
  width: 320,
176
193
  height: 240,
@@ -179,6 +196,17 @@ export default class HTDFit extends DiscountableElement {
179
196
  Plot.lineY(
180
197
  this.samples[param],
181
198
  ),
199
+ Plot.dot(
200
+ this.samples[param],
201
+ Plot.pointer({
202
+ x: Plot.indexOf,
203
+ y: Plot.identity,
204
+ stroke: 'black',
205
+ fill: 'white',
206
+ r: 4,
207
+ tip: true,
208
+ }),
209
+ ),
182
210
  ],
183
211
  }),
184
212
  );