@decidables/discountable-elements 0.1.0
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/CHANGELOG.md +39 -0
- package/LICENSE.md +1003 -0
- package/README.md +1405 -0
- package/lib/discountableElements.esm.js +23333 -0
- package/lib/discountableElements.esm.js.map +1 -0
- package/lib/discountableElements.esm.min.js +1625 -0
- package/lib/discountableElements.esm.min.js.map +1 -0
- package/lib/discountableElements.umd.js +23353 -0
- package/lib/discountableElements.umd.js.map +1 -0
- package/lib/discountableElements.umd.min.js +1625 -0
- package/lib/discountableElements.umd.min.js.map +1 -0
- package/package.json +69 -0
- package/src/components/discountable-control.js +173 -0
- package/src/components/discountable-response.js +283 -0
- package/src/components/htd-calculation.js +230 -0
- package/src/components/htd-curves.js +1073 -0
- package/src/components/htd-fit-worker.js +71 -0
- package/src/components/htd-fit.js +197 -0
- package/src/components/index.js +9 -0
- package/src/components/itc-choice.js +134 -0
- package/src/components/itc-option.js +173 -0
- package/src/components/itc-task.js +253 -0
- package/src/discountable-element.js +105 -0
- package/src/equations/adk2v.js +135 -0
- package/src/equations/htd-equation.js +202 -0
- package/src/equations/index.js +3 -0
- package/src/examples/htd-example.js +73 -0
- package/src/examples/human.js +154 -0
- package/src/examples/index.js +4 -0
- package/src/examples/interactive.js +123 -0
- package/src/examples/model.js +179 -0
- package/src/index.js +6 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/* eslint no-restricted-globals: ["off", "self"] */
|
|
2
|
+
|
|
3
|
+
// Needed for d3 in WebWorker!
|
|
4
|
+
import 'regenerator-runtime/runtime';
|
|
5
|
+
|
|
6
|
+
import * as BayesDistributions from 'bayes.js/distributions';
|
|
7
|
+
import * as BayesMcmc from 'bayes.js/mcmc';
|
|
8
|
+
import * as d3 from 'd3';
|
|
9
|
+
import HTDMath from '@decidables/discountable-math';
|
|
10
|
+
|
|
11
|
+
self.onmessage = (event) => {
|
|
12
|
+
const params = {
|
|
13
|
+
k: {type: 'real', lower: 0, upper: 100},
|
|
14
|
+
luce: {type: 'real', lower: 0, upper: 100},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const logPost = (state, data) => {
|
|
18
|
+
let lp = 0;
|
|
19
|
+
|
|
20
|
+
// Priors
|
|
21
|
+
const kMean = 2;
|
|
22
|
+
const kShape = 3;
|
|
23
|
+
lp += BayesDistributions.gamma(
|
|
24
|
+
state.k,
|
|
25
|
+
kShape,
|
|
26
|
+
kShape / kMean,
|
|
27
|
+
);
|
|
28
|
+
// lp += BayesDistributions.unif(state.k, 0, 100);
|
|
29
|
+
|
|
30
|
+
const luceMean = 2;
|
|
31
|
+
const luceShape = 3;
|
|
32
|
+
lp += BayesDistributions.gamma(
|
|
33
|
+
state.luce,
|
|
34
|
+
luceShape,
|
|
35
|
+
luceShape / luceMean,
|
|
36
|
+
);
|
|
37
|
+
// lp += BayesDistributions.unif(state.luce, 0, 100);
|
|
38
|
+
|
|
39
|
+
// Likelihood
|
|
40
|
+
data.forEach((choice) => {
|
|
41
|
+
// Values
|
|
42
|
+
const vs = HTDMath.adk2v(choice.as, choice.ds, state.k);
|
|
43
|
+
const vl = HTDMath.adk2v(choice.al, choice.dl, state.k);
|
|
44
|
+
|
|
45
|
+
// Choice of sooner or later is sampled from a Bernoulli distribution
|
|
46
|
+
// Luce choice rule is used to compute probability of waiting! (0 = sooner, 1 = later)
|
|
47
|
+
const binval = 1 / (1 + Math.exp(state.luce * (vs - vl)));
|
|
48
|
+
|
|
49
|
+
// Actual response
|
|
50
|
+
const response = (choice.response === 'first') ? 0 : 1;
|
|
51
|
+
|
|
52
|
+
lp += BayesDistributions.bern(response, binval);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return lp;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Initializing the sampler
|
|
59
|
+
const sampler = new BayesMcmc.AmwgSampler(params, logPost, event.data);
|
|
60
|
+
// Burning some samples to the MCMC gods and sampling 5000 draws
|
|
61
|
+
sampler.burn(1000);
|
|
62
|
+
const samples = sampler.sample(5000);
|
|
63
|
+
|
|
64
|
+
// Extract summary stats
|
|
65
|
+
const results = {
|
|
66
|
+
k: d3.median(samples.k),
|
|
67
|
+
luce: d3.median(samples.luce),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
self.postMessage({results: results, samples: samples});
|
|
71
|
+
};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
|
|
2
|
+
import {html, css} from 'lit';
|
|
3
|
+
import * as Plot from '@observablehq/plot';
|
|
4
|
+
|
|
5
|
+
// Special Web Worker import for rollup-plugin-web-worker-loader
|
|
6
|
+
import HTDFitWorker from 'web-worker:./htd-fit-worker'; /* eslint-disable-line import/no-unresolved */
|
|
7
|
+
|
|
8
|
+
import DiscountableElement from '../discountable-element';
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
HTDFit element
|
|
12
|
+
<htd-fit>
|
|
13
|
+
|
|
14
|
+
Attributes:
|
|
15
|
+
interactive: true/false
|
|
16
|
+
|
|
17
|
+
*/
|
|
18
|
+
export default class HTDFit extends DiscountableElement {
|
|
19
|
+
static get properties() {
|
|
20
|
+
return {
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
|
|
27
|
+
this.k = 0.05;
|
|
28
|
+
|
|
29
|
+
this.choices = [];
|
|
30
|
+
this.samples = null;
|
|
31
|
+
|
|
32
|
+
this.working = false;
|
|
33
|
+
this.queued = false;
|
|
34
|
+
this.worker = new HTDFitWorker();
|
|
35
|
+
|
|
36
|
+
this.worker.onmessage = (event) => {
|
|
37
|
+
this.working = false;
|
|
38
|
+
this.samples = event.data.samples;
|
|
39
|
+
this.k = event.data.results.k;
|
|
40
|
+
this.requestUpdate();
|
|
41
|
+
|
|
42
|
+
this.dispatchEvent(new CustomEvent('htd-fit-update', {
|
|
43
|
+
detail: {
|
|
44
|
+
k: this.k,
|
|
45
|
+
},
|
|
46
|
+
bubbles: true,
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
if (this.queued) {
|
|
50
|
+
this.fit();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
this.fit();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fit() {
|
|
58
|
+
if (!this.working) {
|
|
59
|
+
this.worker.postMessage(this.choices);
|
|
60
|
+
this.working = true;
|
|
61
|
+
this.queued = false;
|
|
62
|
+
} else {
|
|
63
|
+
this.queued = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
clear() {
|
|
68
|
+
this.choices = [];
|
|
69
|
+
|
|
70
|
+
this.fit();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get(name = 'default') {
|
|
74
|
+
const choice = this.choices.find((item) => {
|
|
75
|
+
return (item.name === name);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return (choice === undefined) ? null : choice;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
set(as, ds, al, dl, response, name = '', label = '') {
|
|
82
|
+
const choice = this.choices.find((item) => {
|
|
83
|
+
return (item.name === name);
|
|
84
|
+
});
|
|
85
|
+
if (choice === undefined) {
|
|
86
|
+
this.choices.push({
|
|
87
|
+
as: as,
|
|
88
|
+
ds: ds,
|
|
89
|
+
al: al,
|
|
90
|
+
dl: dl,
|
|
91
|
+
response: response,
|
|
92
|
+
name: name,
|
|
93
|
+
label: label,
|
|
94
|
+
});
|
|
95
|
+
} else {
|
|
96
|
+
choice.as = as;
|
|
97
|
+
choice.ds = ds;
|
|
98
|
+
choice.al = al;
|
|
99
|
+
choice.dl = dl;
|
|
100
|
+
choice.response = response;
|
|
101
|
+
choice.label = label;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.fit();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static get styles() {
|
|
108
|
+
return [
|
|
109
|
+
super.styles,
|
|
110
|
+
css`
|
|
111
|
+
/* :host {
|
|
112
|
+
display: inline-block;
|
|
113
|
+
} */
|
|
114
|
+
|
|
115
|
+
figure {
|
|
116
|
+
margin: 0.625rem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
figure h2 {
|
|
120
|
+
margin: 0.25rem 0;
|
|
121
|
+
|
|
122
|
+
font-size: 1.125rem;
|
|
123
|
+
font-weight: 600;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.trace,
|
|
127
|
+
.hist {
|
|
128
|
+
display: inline-block;
|
|
129
|
+
}
|
|
130
|
+
`,
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
render() {
|
|
135
|
+
return html`
|
|
136
|
+
<div>
|
|
137
|
+
<div>After ${this.choices.length} trials:</div>
|
|
138
|
+
<div>Current:
|
|
139
|
+
<var class="math-var k">k</var> = ${this.k.toFixed(2)}
|
|
140
|
+
</div>
|
|
141
|
+
<div class="param">
|
|
142
|
+
<div class="trace k"></div>
|
|
143
|
+
<div class="hist k"></div>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="param">
|
|
146
|
+
<div class="trace luce"></div>
|
|
147
|
+
<div class="hist luce"></div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
plotParam(param) {
|
|
154
|
+
this.shadowRoot.querySelector(`.hist.${param}`).replaceChildren(
|
|
155
|
+
Plot.plot({
|
|
156
|
+
title: `Posterior of ${param}`,
|
|
157
|
+
x: {label: `${param}`},
|
|
158
|
+
width: 320,
|
|
159
|
+
height: 240,
|
|
160
|
+
style: 'font-size: 0.75rem; font-family: var(---font-family-base);',
|
|
161
|
+
marks: [
|
|
162
|
+
Plot.rectY(
|
|
163
|
+
this.samples[param],
|
|
164
|
+
Plot.binX({y: 'count'}, {x: Plot.identity}),
|
|
165
|
+
),
|
|
166
|
+
],
|
|
167
|
+
}),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
this.shadowRoot.querySelector(`.trace.${param}`).replaceChildren(
|
|
171
|
+
Plot.plot({
|
|
172
|
+
title: `Traceplot of ${param}`,
|
|
173
|
+
x: {label: 'Samples'},
|
|
174
|
+
y: {label: `${param}`},
|
|
175
|
+
width: 320,
|
|
176
|
+
height: 240,
|
|
177
|
+
style: 'font-size: 0.75rem; font-family: var(---font-family-base);',
|
|
178
|
+
marks: [
|
|
179
|
+
Plot.lineY(
|
|
180
|
+
this.samples[param],
|
|
181
|
+
),
|
|
182
|
+
],
|
|
183
|
+
}),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
update(changedProperties) {
|
|
188
|
+
super.update(changedProperties);
|
|
189
|
+
|
|
190
|
+
if (this.samples !== null) {
|
|
191
|
+
this.plotParam('k');
|
|
192
|
+
this.plotParam('luce');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
customElements.define('htd-fit', HTDFit);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
export {default as DiscountableControl} from './discountable-control';
|
|
3
|
+
export {default as DiscountableResponse} from './discountable-response';
|
|
4
|
+
export {default as HTDCalculation} from './htd-calculation';
|
|
5
|
+
export {default as HTDCurves} from './htd-curves';
|
|
6
|
+
export {default as HTDFit} from './htd-fit';
|
|
7
|
+
export {default as ITCChoice} from './itc-choice';
|
|
8
|
+
export {default as ITCOption} from './itc-option';
|
|
9
|
+
export {default as ITCTask} from './itc-task';
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
|
|
2
|
+
import {html, css} from 'lit';
|
|
3
|
+
|
|
4
|
+
import DiscountableElement from '../discountable-element';
|
|
5
|
+
import './itc-option';
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
ITCChoice element
|
|
9
|
+
<itc-choice>
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
*/
|
|
13
|
+
export default class ITCChoice extends DiscountableElement {
|
|
14
|
+
static get properties() {
|
|
15
|
+
return {
|
|
16
|
+
state: {
|
|
17
|
+
attribute: 'state',
|
|
18
|
+
type: String,
|
|
19
|
+
reflect: true,
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
as: {
|
|
23
|
+
attribute: 'amount-ss',
|
|
24
|
+
type: Number,
|
|
25
|
+
reflect: true,
|
|
26
|
+
},
|
|
27
|
+
ds: {
|
|
28
|
+
attribute: 'delay-ss',
|
|
29
|
+
type: Number,
|
|
30
|
+
reflect: true,
|
|
31
|
+
},
|
|
32
|
+
al: {
|
|
33
|
+
attribute: 'amount-ll',
|
|
34
|
+
type: Number,
|
|
35
|
+
reflect: true,
|
|
36
|
+
},
|
|
37
|
+
dl: {
|
|
38
|
+
attribute: 'delay-ll',
|
|
39
|
+
type: Number,
|
|
40
|
+
reflect: true,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
constructor() {
|
|
46
|
+
super();
|
|
47
|
+
|
|
48
|
+
this.states = ['choice', 'fixation', 'blank']; // Possible states
|
|
49
|
+
this.state = 'choice'; // Current state
|
|
50
|
+
|
|
51
|
+
this.as = 10;
|
|
52
|
+
this.ds = 5;
|
|
53
|
+
this.al = 40;
|
|
54
|
+
this.dl = 30;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static get styles() {
|
|
58
|
+
return [
|
|
59
|
+
super.styles,
|
|
60
|
+
css`
|
|
61
|
+
:host {
|
|
62
|
+
display: inline-block;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.holder {
|
|
66
|
+
user-select: none;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.holder > * {
|
|
70
|
+
vertical-align: middle;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.query {
|
|
74
|
+
margin: 0 0.5rem;
|
|
75
|
+
|
|
76
|
+
font-family: var(--font-family-code);
|
|
77
|
+
font-size: 1.75rem;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
itc-option {
|
|
81
|
+
width: 10rem;
|
|
82
|
+
height: 10rem;
|
|
83
|
+
}
|
|
84
|
+
`,
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
sendEvent() {
|
|
89
|
+
this.dispatchEvent(new CustomEvent('itc-choice-change', {
|
|
90
|
+
detail: {
|
|
91
|
+
as: this.as,
|
|
92
|
+
ds: this.ds,
|
|
93
|
+
al: this.al,
|
|
94
|
+
dl: this.dl,
|
|
95
|
+
},
|
|
96
|
+
bubbles: true,
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
ssChange(event) {
|
|
101
|
+
this.as = parseFloat(event.detail.a);
|
|
102
|
+
this.ds = parseFloat(event.detail.d);
|
|
103
|
+
this.sendEvent();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
llChange(event) {
|
|
107
|
+
this.al = parseFloat(event.detail.a);
|
|
108
|
+
this.dl = parseFloat(event.detail.d);
|
|
109
|
+
this.sendEvent();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
render() {
|
|
113
|
+
return html`
|
|
114
|
+
<div class="holder">
|
|
115
|
+
<itc-option
|
|
116
|
+
state=${this.state}
|
|
117
|
+
?interactive=${this.interactive}
|
|
118
|
+
amount="${this.as}"
|
|
119
|
+
delay="${this.ds}"
|
|
120
|
+
@itc-option-change=${this.ssChange.bind(this)}>
|
|
121
|
+
</itc-option><span class="query"
|
|
122
|
+
>${(this.state === 'choice') ? '?' : (this.state === 'fixation') ? '+' : html`∙`}</span
|
|
123
|
+
><itc-option
|
|
124
|
+
state=${this.state}
|
|
125
|
+
?interactive=${this.interactive}
|
|
126
|
+
amount="${this.al}"
|
|
127
|
+
delay="${this.dl}"
|
|
128
|
+
@itc-option-change=${this.llChange.bind(this)}>
|
|
129
|
+
</itc-option>
|
|
130
|
+
</div>`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
customElements.define('itc-choice', ITCChoice);
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
|
|
2
|
+
import {html, css} from 'lit';
|
|
3
|
+
|
|
4
|
+
import DiscountableElement from '../discountable-element';
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
ITCOption element
|
|
8
|
+
<itc-option>
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
State
|
|
12
|
+
Amount, Delay
|
|
13
|
+
*/
|
|
14
|
+
export default class ITCOption extends DiscountableElement {
|
|
15
|
+
static get properties() {
|
|
16
|
+
return {
|
|
17
|
+
state: {
|
|
18
|
+
attribute: 'state',
|
|
19
|
+
type: String,
|
|
20
|
+
reflect: true,
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
a: {
|
|
24
|
+
attribute: 'amount',
|
|
25
|
+
type: Number,
|
|
26
|
+
reflect: true,
|
|
27
|
+
},
|
|
28
|
+
d: {
|
|
29
|
+
attribute: 'delay',
|
|
30
|
+
type: Number,
|
|
31
|
+
reflect: true,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
super();
|
|
38
|
+
|
|
39
|
+
this.states = ['choice', 'fixation', 'blank']; // Possible states
|
|
40
|
+
this.state = 'choice'; // Current state
|
|
41
|
+
|
|
42
|
+
this.a = 0;
|
|
43
|
+
this.d = 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static get styles() {
|
|
47
|
+
return [
|
|
48
|
+
super.styles,
|
|
49
|
+
css`
|
|
50
|
+
:host {
|
|
51
|
+
display: inline-block;
|
|
52
|
+
|
|
53
|
+
width: 10rem;
|
|
54
|
+
height: 10rem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.holder {
|
|
58
|
+
display: flex;
|
|
59
|
+
|
|
60
|
+
flex-flow: column nowrap;
|
|
61
|
+
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
|
|
65
|
+
width: 100%;
|
|
66
|
+
height: 100%;
|
|
67
|
+
overflow: visible;
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
background: var(---color-element-background);
|
|
71
|
+
border: 2px solid var(---color-element-emphasis);
|
|
72
|
+
border-radius: 50%;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.interactive,
|
|
76
|
+
.static {
|
|
77
|
+
font-size: 1.75rem;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.interactive {
|
|
81
|
+
--decidables-spinner-font-size: 1.75rem;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.static {
|
|
85
|
+
padding: 0 0.25rem;
|
|
86
|
+
|
|
87
|
+
border-radius: var(---border-radius);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.amount {
|
|
91
|
+
--decidables-spinner-prefix: "$";
|
|
92
|
+
background-color: var(---color-a-light);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.amount.interactive {
|
|
96
|
+
--decidables-spinner-input-width: 4rem;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.delay {
|
|
100
|
+
background-color: var(---color-d-light);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.delay.interactive {
|
|
104
|
+
--decidables-spinner-input-width: 6.75rem;
|
|
105
|
+
--decidables-spinner-postfix: "days";
|
|
106
|
+
--decidables-spinner-postfix-padding: 3.75rem;
|
|
107
|
+
}
|
|
108
|
+
`,
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
sendEvent() {
|
|
113
|
+
this.dispatchEvent(new CustomEvent('itc-option-change', {
|
|
114
|
+
detail: {
|
|
115
|
+
a: this.a,
|
|
116
|
+
d: this.d,
|
|
117
|
+
},
|
|
118
|
+
bubbles: true,
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
aInput(event) {
|
|
123
|
+
this.a = parseFloat(event.target.value);
|
|
124
|
+
this.sendEvent();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
dInput(event) {
|
|
128
|
+
this.d = parseFloat(event.target.value);
|
|
129
|
+
this.sendEvent();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
render() { /* eslint-disable-line class-methods-use-this */
|
|
133
|
+
return html`
|
|
134
|
+
<div class="holder">
|
|
135
|
+
${(this.state === 'choice')
|
|
136
|
+
? this.interactive
|
|
137
|
+
? html`<decidables-spinner
|
|
138
|
+
class="amount interactive"
|
|
139
|
+
?disabled=${!this.interactive}
|
|
140
|
+
step="1"
|
|
141
|
+
.value="${this.a}"
|
|
142
|
+
@input=${this.aInput.bind(this)}
|
|
143
|
+
></decidables-spinner>`
|
|
144
|
+
: html`<div
|
|
145
|
+
class="amount static"
|
|
146
|
+
>$${this.a}</div>`
|
|
147
|
+
: ''
|
|
148
|
+
}
|
|
149
|
+
${(this.state === 'choice')
|
|
150
|
+
? html`<div class="in">in</div>`
|
|
151
|
+
: ''
|
|
152
|
+
}
|
|
153
|
+
${(this.state === 'choice')
|
|
154
|
+
? this.interactive
|
|
155
|
+
? html`<decidables-spinner
|
|
156
|
+
class="delay interactive"
|
|
157
|
+
?disabled=${!this.interactive}
|
|
158
|
+
min="0"
|
|
159
|
+
step="1"
|
|
160
|
+
.value="${this.d}"
|
|
161
|
+
@input=${this.dInput.bind(this)}
|
|
162
|
+
></decidables-spinner>`
|
|
163
|
+
: html`<div
|
|
164
|
+
class="delay static"
|
|
165
|
+
>${this.d} days</div>`
|
|
166
|
+
: ''
|
|
167
|
+
}
|
|
168
|
+
</div>
|
|
169
|
+
`;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
customElements.define('itc-option', ITCOption);
|