reactioning
Version:
Just a simple library to show beautiful reactions in your app.
543 lines (542 loc) • 22.4 kB
JavaScript
import { jsxs as z, jsx as J } from "react/jsx-runtime";
import * as u from "react";
import '../assets/reaction-container.css';const _ = (n, t, e) => {
const i = document.createElement(n), [s, a] = Array.isArray(t) ? [void 0, t] : [t, e];
return s && Object.assign(i, s), a == null || a.forEach((r) => i.appendChild(r)), i;
}, Y = (n, t) => {
var e;
return t === "left" ? n.offsetLeft : (((e = n.offsetParent instanceof HTMLElement ? n.offsetParent : null) == null ? void 0 : e.offsetWidth) ?? 0) - n.offsetWidth - n.offsetLeft;
}, Z = (n) => n.offsetWidth > 0 && n.offsetHeight > 0, G = (n, t) => {
customElements.get(n) !== t && customElements.define(n, t);
};
function q(n, t, { reverse: e = !1 } = {}) {
const i = n.length;
for (let s = e ? i - 1 : 0; e ? s >= 0 : s < i; e ? s-- : s++)
t(n[s], s);
}
const K = String.raw, Q = typeof CSS < "u" && CSS.supports && CSS.supports("animation-timing-function", "linear(1,2)"), tt = typeof CSS < "u" && CSS.supports && CSS.supports("line-height", "mod(1,1)"), T = typeof matchMedia < "u" ? matchMedia("(prefers-reduced-motion: reduce)") : null, C = "--_number-flow-d-opacity", j = "--_number-flow-d-width", R = "--_number-flow-dx", U = "--_number-flow-d", et = (() => {
try {
return CSS.registerProperty({
name: C,
syntax: "<number>",
inherits: !1,
initialValue: "0"
}), CSS.registerProperty({
name: R,
syntax: "<length>",
inherits: !0,
initialValue: "0px"
}), CSS.registerProperty({
name: j,
syntax: "<number>",
inherits: !1,
initialValue: "0"
}), CSS.registerProperty({
name: U,
syntax: "<number>",
inherits: !0,
initialValue: "0"
}), !0;
} catch {
return !1;
}
})(), it = "var(--number-flow-char-height, 1em)", y = "var(--number-flow-mask-height, 0.25em)", M = `calc(${y} / 2)`, A = "var(--number-flow-mask-width, 0.5em)", v = `calc(${A} / var(--scale-x))`, b = "#000 0, transparent 71%", F = K`:host{display:inline-block;direction:ltr;white-space:nowrap;isolation:isolate;line-height:${it} !important}.number,.number__inner{display:inline-block;transform-origin:left top}:host([data-will-change]) :is(.number,.number__inner,.section,.digit,.digit__num,.symbol){will-change:transform}.number{--scale-x:calc(1 + var(${j}) / var(--width));transform:translateX(var(${R})) scaleX(var(--scale-x));margin:0 calc(-1 * ${A});position:relative;-webkit-mask-image:linear-gradient(to right,transparent 0,#000 ${v},#000 calc(100% - ${v}),transparent ),linear-gradient(to bottom,transparent 0,#000 ${y},#000 calc(100% - ${y}),transparent 100% ),radial-gradient(at bottom right,${b}),radial-gradient(at bottom left,${b}),radial-gradient(at top left,${b}),radial-gradient(at top right,${b});-webkit-mask-size:100% calc(100% - ${y} * 2),calc(100% - ${v} * 2) 100%,${v} ${y},${v} ${y},${v} ${y},${v} ${y};-webkit-mask-position:center,center,top left,top right,bottom right,bottom left;-webkit-mask-repeat:no-repeat}.number__inner{padding:${M} ${A};transform:scaleX(calc(1 / var(--scale-x))) translateX(calc(-1 * var(${R})))}:host > :not(.number){z-index:5}.section,.symbol{display:inline-block;position:relative;isolation:isolate}.section::after{content:'\200b';display:inline-block}.section--justify-left{transform-origin:center left}.section--justify-right{transform-origin:center right}.section > [inert],.symbol > [inert]{margin:0 !important;position:absolute !important;z-index:-1}.digit{display:inline-block;position:relative;--c:var(--current) + var(${U})}.digit__num,.number .section::after{padding:${M} 0}.digit__num{display:inline-block;--offset-raw:mod(var(--length) + var(--n) - mod(var(--c),var(--length)),var(--length));--offset:calc( var(--offset-raw) - var(--length) * round(down,var(--offset-raw) / (var(--length) / 2),1) );--y:clamp(-100%,var(--offset) * 100%,100%);transform:translateY(var(--y))}.digit__num[inert]{position:absolute;top:0;left:50%;transform:translateX(-50%) translateY(var(--y))}.digit:not(.is-spinning) .digit__num[inert]{display:none}.symbol__value{display:inline-block;mix-blend-mode:plus-lighter;white-space:pre}.section--justify-left .symbol > [inert]{left:0}.section--justify-right .symbol > [inert]{right:0}.animate-presence{opacity:calc(1 + var(${C}))}`, st = HTMLElement, nt = tt && Q && et;
let S;
class D extends st {
constructor() {
super(), this.created = !1, this.manual = !1;
const { animated: t, ...e } = this.constructor.defaultProps;
this._animated = this.computedAnimated = t, Object.assign(this, e);
}
get animated() {
return this._animated;
}
set animated(t) {
var e;
this.animated !== t && (this._animated = t, (e = this.shadowRoot) == null || e.getAnimations().forEach((i) => i.finish()));
}
set data(t) {
var e;
if (t == null)
return;
const { pre: i, integer: s, fraction: a, post: r, value: o } = t;
if (this.created) {
const l = this._data;
this._data = t, this.computedTrend = typeof this.trend == "function" ? this.trend(l.value, o) : this.trend, this.computedAnimated = nt && this._animated && (!this.respectMotionPreference || !(T != null && T.matches)) && // https://github.com/barvian/number-flow/issues/9
Z(this), (e = this.plugins) == null || e.forEach((c) => {
var p;
return (p = c.onUpdate) == null ? void 0 : p.call(c, t, l, this);
}), this.manual || this.willUpdate(), this._pre.update(i), this._num.update({ integer: s, fraction: a }), this._post.update(r), this.manual || this.didUpdate();
} else {
if (this._data = t, this.attachShadow({ mode: "open" }), typeof CSSStyleSheet < "u" && this.shadowRoot.adoptedStyleSheets)
S || (S = new CSSStyleSheet(), S.replaceSync(F)), this.shadowRoot.adoptedStyleSheets = [S];
else {
const l = document.createElement("style");
l.textContent = F, this.shadowRoot.appendChild(l);
}
this._pre = new O(this, i, {
justify: "right",
part: "left"
}), this.shadowRoot.appendChild(this._pre.el), this._num = new at(this, s, a), this.shadowRoot.appendChild(this._num.el), this._post = new O(this, r, {
justify: "left",
part: "right"
}), this.shadowRoot.appendChild(this._post.el);
}
this.created = !0;
}
willUpdate() {
this._pre.willUpdate(), this._num.willUpdate(), this._post.willUpdate();
}
didUpdate() {
if (!this.computedAnimated)
return;
this._abortAnimationsFinish ? this._abortAnimationsFinish.abort() : this.dispatchEvent(new Event("animationsstart")), this._pre.didUpdate(), this._num.didUpdate(), this._post.didUpdate();
const t = new AbortController();
Promise.all(this.shadowRoot.getAnimations().map((e) => e.finished)).then(() => {
t.signal.aborted || (this.dispatchEvent(new Event("animationsfinish")), this._abortAnimationsFinish = void 0);
}), this._abortAnimationsFinish = t;
}
}
D.defaultProps = {
transformTiming: {
duration: 900,
// Make sure to keep this minified:
easing: "linear(0,.005,.019,.039,.066,.096,.129,.165,.202,.24,.278,.316,.354,.39,.426,.461,.494,.526,.557,.586,.614,.64,.665,.689,.711,.731,.751,.769,.786,.802,.817,.831,.844,.856,.867,.877,.887,.896,.904,.912,.919,.925,.931,.937,.942,.947,.951,.955,.959,.962,.965,.968,.971,.973,.976,.978,.98,.981,.983,.984,.986,.987,.988,.989,.99,.991,.992,.992,.993,.994,.994,.995,.995,.996,.996,.9963,.9967,.9969,.9972,.9975,.9977,.9979,.9981,.9982,.9984,.9985,.9987,.9988,.9989,1)"
},
spinTiming: void 0,
opacityTiming: { duration: 450, easing: "ease-out" },
animated: !0,
trend: (n, t) => Math.sign(t - n),
respectMotionPreference: !0,
plugins: void 0,
digits: void 0
};
class at {
constructor(t, e, i, { className: s, ...a } = {}) {
this.flow = t, this._integer = new L(t, e, {
justify: "right",
part: "integer"
}), this._fraction = new L(t, i, {
justify: "left",
part: "fraction"
}), this._inner = _("span", {
className: "number__inner"
}, [this._integer.el, this._fraction.el]), this.el = _("span", {
...a,
part: "number",
className: `number ${s ?? ""}`
}, [this._inner]);
}
willUpdate() {
this._prevWidth = this.el.offsetWidth, this._prevLeft = this.el.getBoundingClientRect().left, this._integer.willUpdate(), this._fraction.willUpdate();
}
update({ integer: t, fraction: e }) {
this._integer.update(t), this._fraction.update(e);
}
didUpdate() {
const t = this.el.getBoundingClientRect();
this._integer.didUpdate(), this._fraction.didUpdate();
const e = this._prevLeft - t.left, i = this.el.offsetWidth, s = this._prevWidth - i;
this.el.style.setProperty("--width", String(i)), this.el.animate({
[R]: [`${e}px`, "0px"],
[j]: [s, 0]
}, {
...this.flow.transformTiming,
composite: "accumulate"
});
}
}
class W {
constructor(t, e, { justify: i, className: s, ...a }, r) {
this.flow = t, this.children = /* @__PURE__ */ new Map(), this.onCharRemove = (l) => () => {
this.children.delete(l);
}, this.justify = i;
const o = e.map((l) => this.addChar(l).el);
this.el = _("span", {
...a,
className: `section section--justify-${i} ${s ?? ""}`
}, r ? r(o) : o);
}
addChar(t, { startDigitsAtZero: e = !1, ...i } = {}) {
const s = t.type === "integer" || t.type === "fraction" ? new H(this, t.type, e ? 0 : t.value, t.pos, {
...i,
onRemove: this.onCharRemove(t.key)
}) : new rt(this, t.type, t.value, {
...i,
onRemove: this.onCharRemove(t.key)
});
return this.children.set(t.key, s), s;
}
unpop(t) {
t.el.removeAttribute("inert"), t.el.style.top = "", t.el.style[this.justify] = "";
}
pop(t) {
t.forEach((e) => {
e.el.style.top = `${e.el.offsetTop}px`, e.el.style[this.justify] = `${Y(e.el, this.justify)}px`;
}), t.forEach((e) => {
e.el.setAttribute("inert", ""), e.present = !1;
});
}
addNewAndUpdateExisting(t) {
const e = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map(), s = this.justify === "left", a = s ? "prepend" : "append";
if (q(t, (r) => {
let o;
this.children.has(r.key) ? (o = this.children.get(r.key), i.set(r, o), this.unpop(o), o.present = !0) : (o = this.addChar(r, { startDigitsAtZero: !0, animateIn: !0 }), e.set(r, o)), this.el[a](o.el);
}, { reverse: s }), this.flow.computedAnimated) {
const r = this.el.getBoundingClientRect();
e.forEach((o) => {
o.willUpdate(r);
});
}
e.forEach((r, o) => {
r.update(o.value);
}), i.forEach((r, o) => {
r.update(o.value);
});
}
willUpdate() {
const t = this.el.getBoundingClientRect();
this._prevOffset = t[this.justify], this.children.forEach((e) => e.willUpdate(t));
}
didUpdate() {
const t = this.el.getBoundingClientRect();
this.children.forEach((s) => s.didUpdate(t));
const e = t[this.justify], i = this._prevOffset - e;
i && this.children.size && this.el.animate({
transform: [`translateX(${i}px)`, "none"]
}, {
...this.flow.transformTiming,
composite: "accumulate"
});
}
}
class L extends W {
update(t) {
const e = /* @__PURE__ */ new Map();
this.children.forEach((i, s) => {
t.find((a) => a.key === s) || e.set(s, i), this.unpop(i);
}), this.addNewAndUpdateExisting(t), e.forEach((i) => {
i instanceof H && i.update(0);
}), this.pop(e);
}
}
class O extends W {
update(t) {
const e = /* @__PURE__ */ new Map();
this.children.forEach((i, s) => {
t.find((a) => a.key === s) || e.set(s, i);
}), this.pop(e), this.addNewAndUpdateExisting(t);
}
}
class E {
constructor(t, e, { onRemove: i, animateIn: s = !1 } = {}) {
this.flow = t, this.el = e, this._present = !0, this._remove = () => {
var a;
this.el.remove(), (a = this._onRemove) == null || a.call(this);
}, this.el.classList.add("animate-presence"), this.flow.computedAnimated && s && this.el.animate({
[C]: [-0.9999, 0]
}, {
...this.flow.opacityTiming,
composite: "accumulate"
}), this._onRemove = i;
}
get present() {
return this._present;
}
set present(t) {
if (this._present !== t) {
if (this._present = t, t ? this.el.removeAttribute("inert") : this.el.setAttribute("inert", ""), !this.flow.computedAnimated) {
t || this._remove();
return;
}
this.el.style.setProperty("--_number-flow-d-opacity", t ? "0" : "-.999"), this.el.animate({
[C]: t ? [-0.9999, 0] : [0.999, 0]
}, {
...this.flow.opacityTiming,
composite: "accumulate"
}), t ? this.flow.removeEventListener("animationsfinish", this._remove) : this.flow.addEventListener("animationsfinish", this._remove, {
once: !0
});
}
}
}
class X extends E {
constructor(t, e, i, s) {
super(t.flow, i, s), this.section = t, this.value = e, this.el = i;
}
}
class H extends X {
constructor(t, e, i, s, a) {
var r, o;
const l = (((o = (r = t.flow.digits) == null ? void 0 : r[s]) == null ? void 0 : o.max) ?? 9) + 1, c = Array.from({ length: l }).map((m, f) => {
const g = _("span", { className: "digit__num" }, [
document.createTextNode(String(f))
]);
return f !== i && g.setAttribute("inert", ""), g.style.setProperty("--n", String(f)), g;
}), p = _("span", {
part: `digit ${e}-digit`,
className: "digit"
}, c);
p.style.setProperty("--current", String(i)), p.style.setProperty("--length", String(l)), super(t, i, p, a), this.pos = s, this._onAnimationsFinish = () => {
this.el.classList.remove("is-spinning");
}, this._numbers = c, this.length = l;
}
willUpdate(t) {
const e = this.el.getBoundingClientRect();
this._prevValue = this.value;
const i = e[this.section.justify] - t[this.section.justify], s = e.width / 2;
this._prevCenter = this.section.justify === "left" ? i + s : i - s;
}
update(t) {
this.el.style.setProperty("--current", String(t)), this._numbers.forEach((e, i) => i === t ? e.removeAttribute("inert") : e.setAttribute("inert", "")), this.value = t;
}
didUpdate(t) {
const e = this.el.getBoundingClientRect(), i = e[this.section.justify] - t[this.section.justify], s = e.width / 2, a = this.section.justify === "left" ? i + s : i - s, r = this._prevCenter - a;
r && this.el.animate({
transform: [`translateX(${r}px)`, "none"]
}, {
...this.flow.transformTiming,
composite: "accumulate"
});
const o = this.getDelta();
o && (this.el.classList.add("is-spinning"), this.el.animate({
[U]: [-o, 0]
}, {
...this.flow.spinTiming ?? this.flow.transformTiming,
composite: "accumulate"
}), this.flow.addEventListener("animationsfinish", this._onAnimationsFinish, { once: !0 }));
}
getDelta() {
var t;
if (this.flow.plugins)
for (const s of this.flow.plugins) {
const a = (t = s.getDelta) == null ? void 0 : t.call(s, this.value, this._prevValue, this);
if (a != null)
return a;
}
const e = this.value - this._prevValue, i = this.flow.computedTrend || Math.sign(e);
return i < 0 && this.value > this._prevValue ? this.value - this.length - this._prevValue : i > 0 && this.value < this._prevValue ? this.length - this._prevValue + this.value : e;
}
}
class rt extends X {
constructor(t, e, i, s) {
const a = _("span", {
className: "symbol__value",
textContent: i
});
super(t, i, _("span", {
part: `symbol ${e}`,
className: "symbol"
}, [a]), s), this.type = e, this._children = /* @__PURE__ */ new Map(), this._onChildRemove = (r) => () => {
this._children.delete(r);
}, this._children.set(i, new E(this.flow, a, {
onRemove: this._onChildRemove(i)
}));
}
willUpdate(t) {
if (this.type === "decimal")
return;
const e = this.el.getBoundingClientRect();
this._prevOffset = e[this.section.justify] - t[this.section.justify];
}
update(t) {
if (this.value !== t) {
const e = this._children.get(this.value);
if (e.present = !1, this._children.has(t)) {
const i = this._children.get(t);
i.present = !0;
} else {
const i = _("span", {
className: "symbol__value",
textContent: t
});
this.el.appendChild(i), this._children.set(t, new E(this.flow, i, {
animateIn: !0,
onRemove: this._onChildRemove(t)
}));
}
}
this.value = t;
}
didUpdate(t) {
if (this.type === "decimal")
return;
const e = this.el.getBoundingClientRect()[this.section.justify] - t[this.section.justify], i = this._prevOffset - e;
i && this.el.animate({
transform: [`translateX(${i}px)`, "none"]
}, { ...this.flow.transformTiming, composite: "accumulate" });
}
}
function ot(n, t, e, i) {
const s = t.formatToParts(n);
e && s.unshift({ type: "prefix", value: e }), i && s.push({ type: "suffix", value: i });
const a = [], r = [], o = [], l = [], c = {}, p = (d) => `${d}:${c[d] = (c[d] ?? -1) + 1}`;
let m = "", f = !1, g = !1;
for (const d of s) {
m += d.value;
const h = d.type === "minusSign" || d.type === "plusSign" ? "sign" : d.type;
h === "integer" ? (f = !0, r.push(...d.value.split("").map(($) => ({ type: h, value: parseInt($) })))) : h === "group" ? r.push({ type: h, value: d.value }) : h === "decimal" ? (g = !0, o.push({ type: h, value: d.value, key: p(h) })) : h === "fraction" ? o.push(...d.value.split("").map(($) => ({
type: h,
value: parseInt($),
key: p(h),
pos: -1 - c[h]
}))) : (f || g ? l : a).push({
type: h,
value: d.value,
key: p(h)
});
}
const w = [];
for (let d = r.length - 1; d >= 0; d--) {
const h = r[d];
w.unshift(h.type === "integer" ? {
...h,
key: p(h.type),
pos: c[h.type]
} : {
...h,
key: p(h.type)
});
}
return {
pre: a,
integer: w,
fraction: o,
post: l,
valueAsString: m,
value: typeof n == "string" ? parseFloat(n) : n
};
}
var I;
const lt = parseInt((I = u.version.match(/^(\d+)\./)) == null ? void 0 : I[1]), k = lt >= 19, ht = [
"data",
"digits"
];
class N extends D {
attributeChangedCallback(t, e, i) {
this[t] = JSON.parse(i);
}
}
N.observedAttributes = k ? [] : ht;
G("number-flow-react", N);
const x = {}, V = k ? (n) => n : JSON.stringify;
function B(n) {
const { transformTiming: t, spinTiming: e, opacityTiming: i, animated: s, respectMotionPreference: a, trend: r, plugins: o, ...l } = n;
return [
{
transformTiming: t,
spinTiming: e,
opacityTiming: i,
animated: s,
respectMotionPreference: a,
trend: r,
plugins: o
},
l
];
}
class dt extends u.Component {
// Update the non-`data` props to avoid JSON serialization
// Data needs to be set in render still:
updateProperties(t) {
if (!this.el) return;
this.el.manual = !this.props.isolate;
const [e] = B(this.props);
Object.entries(e).forEach(([i, s]) => {
this.el[i] = s ?? N.defaultProps[i];
}), t != null && t.onAnimationsStart && this.el.removeEventListener("animationsstart", t.onAnimationsStart), this.props.onAnimationsStart && this.el.addEventListener("animationsstart", this.props.onAnimationsStart), t != null && t.onAnimationsFinish && this.el.removeEventListener("animationsfinish", t.onAnimationsFinish), this.props.onAnimationsFinish && this.el.addEventListener("animationsfinish", this.props.onAnimationsFinish);
}
componentDidMount() {
this.updateProperties(), k && this.el && (this.el.digits = this.props.digits, this.el.data = this.props.data);
}
getSnapshotBeforeUpdate(t) {
var e;
if (this.updateProperties(t), t.data !== this.props.data) {
if (this.props.group)
return this.props.group.willUpdate(), () => {
var i;
return (i = this.props.group) == null ? void 0 : i.didUpdate();
};
if (!this.props.isolate)
return (e = this.el) == null || e.willUpdate(), () => {
var i;
return (i = this.el) == null ? void 0 : i.didUpdate();
};
}
return null;
}
componentDidUpdate(t, e, i) {
i == null || i();
}
handleRef(t) {
this.props.innerRef && (this.props.innerRef.current = t), this.el = t;
}
render() {
const [t, { innerRef: e, className: i, data: s, willChange: a, isolate: r, group: o, digits: l, onAnimationsStart: c, onAnimationsFinish: p, ...m }] = B(this.props);
return (
// @ts-expect-error missing types
/* @__PURE__ */ u.createElement("number-flow-react", {
ref: this.handleRef,
"data-will-change": a ? "" : void 0,
// Have to rename this:
class: i,
"aria-label": s.valueAsString,
...m,
role: "img",
dangerouslySetInnerHTML: {
__html: ""
},
suppressHydrationWarning: !0,
digits: V(l),
// Make sure data is set last, everything else is updated:
data: V(s)
})
);
}
constructor(t) {
super(t), this.handleRef = this.handleRef.bind(this);
}
}
const ct = /* @__PURE__ */ u.forwardRef(function({ value: t, locales: e, format: i, prefix: s, suffix: a, ...r }, o) {
u.useImperativeHandle(o, () => l.current, []);
const l = u.useRef(), c = u.useContext(pt);
c == null || c.useRegister(l);
const p = u.useMemo(() => e ? JSON.stringify(e) : "", [
e
]), m = u.useMemo(() => i ? JSON.stringify(i) : "", [
i
]), f = u.useMemo(() => {
var w;
const g = x[w = `${p}:${m}`] ?? (x[w] = new Intl.NumberFormat(e, i));
return ot(t, g, s, a);
}, [
t,
p,
m,
s,
a
]);
return /* @__PURE__ */ u.createElement(dt, {
...r,
group: c,
data: f,
innerRef: l
});
}), pt = /* @__PURE__ */ u.createContext(void 0), ut = "_root_1xovx_3", mt = "_selected_1xovx_26", P = {
root: ut,
selected: mt
}, gt = ({ children: n, reaction: t, ...e }) => /* @__PURE__ */ z(
"div",
{
className: `${P.root} ${t.selected ? P.selected : ""}`,
"aria-details": "reaction-container",
...e,
children: [
n,
/* @__PURE__ */ J(ct, { value: t.count })
]
}
);
export {
gt as ReactionContainer
};