Skip to content

Instantly share code, notes, and snippets.

@kayac-chang
Created March 28, 2021 04:26
Show Gist options
  • Select an option

  • Save kayac-chang/b49b4569479b37c9b156fe53d343d001 to your computer and use it in GitHub Desktop.

Select an option

Save kayac-chang/b49b4569479b37c9b156fe53d343d001 to your computer and use it in GitHub Desktop.
PureJS Data Driven Rerender
function select(key, el = document) {
return el.querySelector(key);
}
function selectAll(key, el = document) {
return Array.from(el.querySelectorAll(key));
}
function replace(oldEl, newEl) {
oldEl.parentNode.replaceChild(newEl, oldEl);
return newEl;
}
function range(start, end) {
return [...Array(end - start).keys()].map((i) => i + start);
}
function RangeSlider(it, { value, step, min, render, onChange }) {
it = replace(it, it.cloneNode(true));
const track = select(".track", it);
track.addEventListener("click", (event) => {
const newValue = Math.ceil((event.offsetX / track.clientWidth) * step);
if (newValue <= min && value === 0) {
return onChange(min);
}
if (newValue <= min) {
return onChange(0);
}
onChange(newValue);
});
select(".progress", it).addEventListener("click", (event) => {
const newValue = Math.floor((event.offsetX / track.clientWidth) * step);
if (newValue <= min && value === 0) {
return onChange(min);
}
if (newValue <= min) {
return onChange(0);
}
onChange(newValue);
});
select(".sub-btn", it).addEventListener("click", () => {
if (value <= 0) {
return;
}
if (value === min) {
return onChange(0);
}
onChange(value - 1);
});
select(".plus-btn", it).addEventListener("click", () => {
if (value >= step) {
return;
}
if (value === 0) {
return onChange(min);
}
onChange(value + 1);
});
render({ it, value, min, step });
}
function Detail(it, { max_miles_redumption, miles_unit, redeem_per_unit }) {
it.innerHTML = `
<div
class="text-secondary position-absolute w-100 d-flex justify-content-between align-items-end detail"
style="bottom: 2rem;"
>
<div>
<div>0 miles</div>
<div>save TWD 0</div>
</div>
<div class="text-right">
<div>${max_miles_redumption} miles</div>
<div>save TWD ${
(max_miles_redumption / miles_unit) * redeem_per_unit
}</div>
<div>Max you can spend</div>
</div>
</div>
`;
}
function Currency(it, value) {
it.textContent = new Intl.NumberFormat().format(value);
}
function Select(it, { value, step, render, onChange }) {
it = replace(it, it.cloneNode(true));
render({ it, value, step });
select(".sp-select-options", it).addEventListener("click", (event) => {
select("input:checked", it).checked = false;
onChange(Number(event.target.value));
});
}
function Control({ value, step, min, ...config }) {
const { miles_unit, redeem_per_unit, balance } = config;
Select(select(".sp-select"), {
value,
step,
render({ it, value, step }) {
select(".sp-select-options", it).innerHTML = [0, ...range(min, step + 1)]
.map((_value) => {
const miles = miles_unit * _value;
const redeem = redeem_per_unit * _value;
const selected = value === _value ? "checked" : "";
return `<button value="${_value}" class="${selected}">${miles} miles save TWD ${redeem}</button>`;
})
.join("");
select("select", it).innerHTML = [0, ...range(min, step + 1)]
.map((_value) => {
const miles = miles_unit * _value;
const redeem = redeem_per_unit * _value;
const selected = value === _value ? "selected" : "";
return `<option value="${_value}" ${selected}>${miles} miles save TWD ${redeem}</option>`;
})
.join("");
},
onChange(value) {
Control({ value, step, min, ...config });
},
});
RangeSlider(select(".range"), {
value,
step,
min,
render({ it, value, min, step }) {
const unit = 100 / step;
const thumb = select(".thumb", it);
thumb.style.left = `${value * unit}%`;
const progress = select(".progress", it);
progress.style.width = `${Math.max(value, min) * unit}%`;
progress.classList.toggle("bg-brown-light", !(value >= min));
const track = select(".track", it);
track.style.background = `
repeating-linear-gradient(
to right,
transparent 0 0.4%,
currentColor 0.4% ${unit}%
)
`;
const miles = miles_unit * value;
const redeem = redeem_per_unit * value;
const checkout = balance - redeem;
const calculator = select(".calculator", it);
calculator.textContent = `${miles} miles save TWD ${redeem}`;
Currency(select(".redeem"), redeem);
Currency(select(".checkout"), checkout);
},
onChange(value) {
Control({ value, step, min, ...config });
},
});
}
function main() {
const config = {
balance: 47000,
min_miles_redumption: 1000,
max_miles_redumption: 2000,
miles_unit: 100,
redeem_per_unit: 10,
};
Detail(select(".detail"), config);
selectAll(".balance").forEach((it) => Currency(it, config.balance));
Control({
value: config.min_miles_redumption / config.miles_unit,
step: config.max_miles_redumption / config.miles_unit,
min: config.min_miles_redumption / config.miles_unit,
...config,
});
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment