motimeline 2.7.2 → 2.7.4

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/README.md CHANGED
@@ -357,8 +357,14 @@ No framework option needed. Wrap the `<ul>` inside a Bootstrap `.container`:
357
357
 
358
358
  ## Changelog
359
359
 
360
+ ### v2.7.4
361
+ - Fix: wrong column placement when adjacent left and right items share the same bottom y-coordinate ([#3](https://github.com/MattOpen/moTimeline/issues/3)) — adds a 1 px tolerance to the column algorithm to absorb `offsetHeight`/`offsetTop` rounding mismatches
362
+
363
+ ### v2.7.3
364
+ - Fix: cards misaligned on first load when items contain images ([#2](https://github.com/MattOpen/moTimeline/issues/2)) — layout now re-runs automatically after each unloaded image fires its `load` event, so column placement is correct once images are rendered
365
+
360
366
  ### v2.7.2
361
- - Fix: cards misaligned on first load ([#2](https://github.com/MattOpen/moTimeline/issues/2)) — newly-appended items have `offsetTop = 0` until the browser paints; a `requestAnimationFrame` corrective refresh ensures correct column placement after first paint
367
+ - Fix: cards misaligned on first load ([#2](https://github.com/MattOpen/moTimeline/issues/2)) — reverted to sequential `offsetTop`-based column algorithm; the batch `offsetHeight` fill-shorter approach produced non-alternating columns
362
368
 
363
369
  ### v2.7.1
364
370
  - Fix: resize listener not attached when container is empty at init time ([#1](https://github.com/MattOpen/moTimeline/issues/1)) — `addItems()` on an empty timeline now correctly responds to window resize
@@ -1,6 +1,6 @@
1
1
  "use strict";var _=Object.defineProperty;var y=(o,e,t)=>e in o?_(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var p=(o,e,t)=>y(o,typeof e!="symbol"?e+"":e,t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});/*!
2
- * moTimeline v2.7.2
2
+ * moTimeline v2.7.4
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
6
- */const c=new WeakMap,v={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px"},b="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";function w(){const o=window.innerWidth;return o<600?"xs":o<992?"sm":o<1200?"md":"lg"}function C(o,e=100){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>o(...s),e)}}function m(o){return o?{o:o.offsetTop,h:o.offsetHeight,gppu:o.offsetTop+o.offsetHeight}:{o:0,h:0,gppu:0}}function E(o,e){const t=[];let s=o.previousElementSibling;for(;s;)(!e||s.matches(e))&&t.push(s),s=s.previousElementSibling;return t}const l=class l{constructor(e,t={}){if(typeof e=="string"&&(e=document.querySelector(e)),!e)throw new Error("moTimeline: element not found");this.element=e,this.settings=Object.assign({},v,t),this.settings.columnCount=Object.assign({},v.columnCount,t.columnCount),this._resizeHandler=C(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(c.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});c.set(e,t),l.instances.add(this),e.classList.add("mo-timeline"),t.theme&&e.classList.add("mo-theme"),e.style.setProperty("--mo-card-border-radius",t.cardBorderRadius),e.style.setProperty("--mo-avatar-size",t.avatarSize),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){l.instances.forEach(e=>{const t=e.element,s=c.get(t);s&&(s.col=s.columnCount[w()],e._setDivider(),Array.from(t.children).forEach(r=>{e._setPostPosition(r)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}destroy(){window.removeEventListener("resize",this._resizeHandler),c.delete(this.element),l.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.classList.remove("mo-timeline","mo-theme","mo-twocol"),Array.from(this.element.children).forEach(e=>{e.classList.remove("mo-item","js-mo-item","mo-inverted","js-mo-inverted","mo-offset"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}_getData(){return c.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[w()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const s=t.lastItemIdx,r=Array.from(e.children),n=r.slice(s);n.length!==0&&(n.forEach((i,a)=>{i.id||(i.id="moT"+crypto.randomUUID()+"_"+(a+s)),i.classList.add("mo-item","js-mo-item")}),this._setDivider(),n.forEach((i,a)=>{t.showBadge&&this._createBadge(i,a+s+1),t.showArrow&&this._createArrow(i)}),t.lastItemIdx=r.length,c.set(e,t),this.refresh(),requestAnimationFrame(()=>this.refresh()))}_setPostPosition(e){const t=this._getLeftOrRight(e);t&&(e.classList.toggle("mo-inverted",t.lr>0),e.classList.toggle("js-mo-inverted",t.lr>0),e.classList.toggle("mo-offset",t.badge_offset>0))}_getLeftOrRight(e){if(!e)return null;const t=this._getData();if(!t)return null;const s=t.col,r=E(e,".js-mo-inverted")[0]||null,n=E(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,i=m(n),a=m(r),u=m(e);let h=0,f=0;if(s>1){i.gppu>u.o&&(h=1),a.gppu>i.gppu&&(h=0);const g=e.previousElementSibling;g&&Math.abs(u.o-m(g).o)<40&&(f=1)}return{lr:h,badge_offset:f}}_createBadge(e,t){const s=this._getData(),r=document.createElement("span");if(r.className="mo-badge js-mo-badge",s.showCounterStyle==="none")r.style.opacity="0";else if(s.showCounterStyle==="image"){const n=document.createElement("img");n.className="mo-badge-icon",n.alt="",n.src=e.dataset.moIcon||b,r.appendChild(n)}else r.textContent=t;e.prepend(r)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon);const s=document.createElement("div");if(s.className="mo-card",e.banner){const n=document.createElement("div");n.className="mo-card-image";const i=document.createElement("img");if(i.className="mo-banner",i.src=e.banner,i.alt="",n.appendChild(i),e.avatar){const a=document.createElement("img");a.className="mo-avatar",a.src=e.avatar,a.alt="",n.appendChild(a)}s.appendChild(n)}const r=document.createElement("div");if(r.className="mo-card-body",e.title){const n=document.createElement("h3");n.textContent=e.title,r.appendChild(n)}if(e.meta){const n=document.createElement("p");n.className="mo-meta",n.textContent=e.meta,r.appendChild(n)}if(e.text){const n=document.createElement("p");n.textContent=e.text,r.appendChild(n)}return s.appendChild(r),t.appendChild(s),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}};p(l,"instances",new Set);let d=l;exports.MoTimeline=d;exports.default=d;
6
+ */const c=new WeakMap,v={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px"},b="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";function w(){const o=window.innerWidth;return o<600?"xs":o<992?"sm":o<1200?"md":"lg"}function C(o,e=100){let t;return(...n)=>{clearTimeout(t),t=setTimeout(()=>o(...n),e)}}function m(o){return o?{o:o.offsetTop,h:o.offsetHeight,gppu:o.offsetTop+o.offsetHeight}:{o:0,h:0,gppu:0}}function E(o,e){const t=[];let n=o.previousElementSibling;for(;n;)(!e||n.matches(e))&&t.push(n),n=n.previousElementSibling;return t}const l=class l{constructor(e,t={}){if(typeof e=="string"&&(e=document.querySelector(e)),!e)throw new Error("moTimeline: element not found");this.element=e,this.settings=Object.assign({},v,t),this.settings.columnCount=Object.assign({},v.columnCount,t.columnCount),this._resizeHandler=C(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(c.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});c.set(e,t),l.instances.add(this),e.classList.add("mo-timeline"),t.theme&&e.classList.add("mo-theme"),e.style.setProperty("--mo-card-border-radius",t.cardBorderRadius),e.style.setProperty("--mo-avatar-size",t.avatarSize),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){l.instances.forEach(e=>{const t=e.element,n=c.get(t);n&&(n.col=n.columnCount[w()],e._setDivider(),Array.from(t.children).forEach(r=>{e._setPostPosition(r)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}destroy(){window.removeEventListener("resize",this._resizeHandler),c.delete(this.element),l.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.classList.remove("mo-timeline","mo-theme","mo-twocol"),Array.from(this.element.children).forEach(e=>{e.classList.remove("mo-item","js-mo-item","mo-inverted","js-mo-inverted","mo-offset"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}_getData(){return c.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[w()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const n=t.lastItemIdx,r=Array.from(e.children),s=r.slice(n);s.length!==0&&(s.forEach((i,a)=>{i.id||(i.id="moT"+crypto.randomUUID()+"_"+(a+n)),i.classList.add("mo-item","js-mo-item")}),this._setDivider(),s.forEach((i,a)=>{t.showBadge&&this._createBadge(i,a+n+1),t.showArrow&&this._createArrow(i)}),t.lastItemIdx=r.length,c.set(e,t),this.refresh(),s.forEach(i=>{i.querySelectorAll("img").forEach(a=>{a.complete||a.addEventListener("load",this._resizeHandler,{once:!0})})}))}_setPostPosition(e){const t=this._getLeftOrRight(e);t&&(e.classList.toggle("mo-inverted",t.lr>0),e.classList.toggle("js-mo-inverted",t.lr>0),e.classList.toggle("mo-offset",t.badge_offset>0))}_getLeftOrRight(e){if(!e)return null;const t=this._getData();if(!t)return null;const n=t.col,r=E(e,".js-mo-inverted")[0]||null,s=E(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,i=m(s),a=m(r),u=m(e);let h=0,f=0;if(n>1){i.gppu>u.o+1&&(h=1),a.gppu>i.gppu&&(h=0);const g=e.previousElementSibling;g&&Math.abs(u.o-m(g).o)<40&&(f=1)}return{lr:h,badge_offset:f}}_createBadge(e,t){const n=this._getData(),r=document.createElement("span");if(r.className="mo-badge js-mo-badge",n.showCounterStyle==="none")r.style.opacity="0";else if(n.showCounterStyle==="image"){const s=document.createElement("img");s.className="mo-badge-icon",s.alt="",s.src=e.dataset.moIcon||b,r.appendChild(s)}else r.textContent=t;e.prepend(r)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon);const n=document.createElement("div");if(n.className="mo-card",e.banner){const s=document.createElement("div");s.className="mo-card-image";const i=document.createElement("img");if(i.className="mo-banner",i.src=e.banner,i.alt="",s.appendChild(i),e.avatar){const a=document.createElement("img");a.className="mo-avatar",a.src=e.avatar,a.alt="",s.appendChild(a)}n.appendChild(s)}const r=document.createElement("div");if(r.className="mo-card-body",e.title){const s=document.createElement("h3");s.textContent=e.title,r.appendChild(s)}if(e.meta){const s=document.createElement("p");s.className="mo-meta",s.textContent=e.meta,r.appendChild(s)}if(e.text){const s=document.createElement("p");s.textContent=e.text,r.appendChild(s)}return n.appendChild(r),t.appendChild(n),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}};p(l,"instances",new Set);let d=l;exports.MoTimeline=d;exports.default=d;
@@ -1,3 +1,3 @@
1
1
  /*!
2
- * moTimeline v2.7.2 — CSS
2
+ * moTimeline v2.7.4 — CSS
3
3
  */:root{--mo-line-color: #dde1e7;--mo-badge-bg: #4f46e5;--mo-badge-color: #fff;--mo-badge-size: 26px;--mo-badge-font-size: 12px;--mo-arrow-color: #dde1e7}.mo-timeline{display:block;list-style:none;margin:0;padding:0;position:relative;width:100%}.mo-timeline:after{content:"";display:table;clear:both}.mo-timeline.mo-twocol:before{background-color:var(--mo-line-color);bottom:0;content:"";left:50%;margin-left:-1.5px;position:absolute;top:0;width:3px;z-index:0}.mo-timeline>.mo-item{box-sizing:border-box;display:block;float:left;position:relative;width:50%}.mo-timeline:not(.mo-twocol)>.mo-item{float:none;width:100%}.mo-timeline>.mo-item.mo-inverted{float:right}.mo-badge{align-items:center;background:var(--mo-badge-bg);border-radius:50%;color:var(--mo-badge-color);display:flex;font-size:var(--mo-badge-font-size);font-weight:700;height:var(--mo-badge-size);justify-content:center;min-width:var(--mo-badge-size);overflow:hidden;position:absolute;top:18px;z-index:2}.mo-badge .mo-badge-icon{border-radius:50%;height:100%;object-fit:cover;width:100%}.mo-timeline.mo-twocol>.mo-item:not(.mo-inverted) .mo-badge{left:auto;right:calc(var(--mo-badge-size) / -2)}.mo-timeline.mo-twocol>.mo-item.mo-inverted .mo-badge{left:calc(var(--mo-badge-size) / -2);right:auto}.mo-timeline.mo-twocol>.mo-item.mo-offset .mo-badge{top:calc(18px + var(--mo-badge-size) + 10px)}.mo-timeline.mo-twocol>.mo-item.mo-offset .mo-arrow{top:calc(26px + var(--mo-badge-size) + 10px)}.mo-timeline:not(.mo-twocol)>.mo-item .mo-badge{display:none}.mo-arrow{border:8px solid transparent;display:block;height:0;position:absolute;top:26px;width:0;z-index:1}.mo-timeline.mo-twocol>.mo-item:not(.mo-inverted) .mo-arrow{border-left:8px solid var(--mo-arrow-color);border-right:none;left:auto;right:0}.mo-timeline.mo-twocol>.mo-item.mo-inverted .mo-arrow{border-right:8px solid var(--mo-arrow-color);border-left:none;left:0;right:auto}.mo-timeline:not(.mo-twocol)>.mo-item .mo-arrow{display:none}.mo-theme{--mo-arrow-color: #fff}.mo-theme>.mo-item .mo-card{background:#fff;border-radius:var(--mo-card-border-radius, 8px);box-shadow:0 2px 14px #0000001a;margin:.5rem 1.25rem .5rem .5rem;position:relative}.mo-theme>.mo-item.mo-inverted .mo-card{margin:.5rem .5rem .5rem 1.25rem}.mo-theme>.mo-item .mo-banner{border-radius:var(--mo-card-border-radius, 8px) var(--mo-card-border-radius, 8px) 0 0;display:block;max-height:240px;object-fit:cover;width:100%}.mo-theme>.mo-item .mo-card-image{overflow:visible;position:relative}.mo-theme>.mo-item .mo-avatar{border:3px solid #fff;border-radius:50%;bottom:calc(var(--mo-avatar-size, 50px) * -.44);box-shadow:0 2px 8px #0000002e;height:var(--mo-avatar-size, 50px);object-fit:cover;position:absolute;right:14px;width:var(--mo-avatar-size, 50px);z-index:1}.mo-theme>.mo-item .mo-card-body{padding:1.75rem 1rem 1rem}.mo-theme>.mo-item .mo-card-body h3{font-size:1rem;font-weight:700;margin:0 0 .3rem}.mo-theme>.mo-item .mo-card-body .mo-meta{color:#9ca3af;font-size:.75rem;margin-bottom:.5rem}.mo-theme>.mo-item .mo-card-body p{color:#6b7280;font-size:.875rem;line-height:1.55;margin:0}.mo-theme.mo-twocol>.mo-item:not(.mo-inverted) .mo-arrow{border-left-color:var(--mo-arrow-color);right:12px}.mo-theme.mo-twocol>.mo-item.mo-inverted .mo-arrow{border-right-color:var(--mo-arrow-color);left:12px}.mo-theme .mo-badge{background:#fff;border:2px solid var(--mo-line-color);box-shadow:0 2px 6px #0000001a;color:#374151}
@@ -1,13 +1,13 @@
1
1
  var _ = Object.defineProperty;
2
2
  var y = (o, e, t) => e in o ? _(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
3
- var g = (o, e, t) => y(o, typeof e != "symbol" ? e + "" : e, t);
3
+ var p = (o, e, t) => y(o, typeof e != "symbol" ? e + "" : e, t);
4
4
  /*!
5
- * moTimeline v2.7.2
5
+ * moTimeline v2.7.4
6
6
  * Responsive two-column timeline layout library
7
7
  * https://github.com/MattOpen/moTimeline
8
8
  * MIT License
9
9
  */
10
- const c = /* @__PURE__ */ new WeakMap(), p = {
10
+ const c = /* @__PURE__ */ new WeakMap(), g = {
11
11
  columnCount: { xs: 1, sm: 2, md: 2, lg: 2 },
12
12
  showBadge: !1,
13
13
  showArrow: !1,
@@ -23,8 +23,8 @@ function v() {
23
23
  }
24
24
  function b(o, e = 100) {
25
25
  let t;
26
- return (...s) => {
27
- clearTimeout(t), t = setTimeout(() => o(...s), e);
26
+ return (...n) => {
27
+ clearTimeout(t), t = setTimeout(() => o(...n), e);
28
28
  };
29
29
  }
30
30
  function m(o) {
@@ -36,15 +36,15 @@ function m(o) {
36
36
  }
37
37
  function w(o, e) {
38
38
  const t = [];
39
- let s = o.previousElementSibling;
40
- for (; s; )
41
- (!e || s.matches(e)) && t.push(s), s = s.previousElementSibling;
39
+ let n = o.previousElementSibling;
40
+ for (; n; )
41
+ (!e || n.matches(e)) && t.push(n), n = n.previousElementSibling;
42
42
  return t;
43
43
  }
44
44
  const l = class l {
45
45
  constructor(e, t = {}) {
46
46
  if (typeof e == "string" && (e = document.querySelector(e)), !e) throw new Error("moTimeline: element not found");
47
- this.element = e, this.settings = Object.assign({}, p, t), this.settings.columnCount = Object.assign({}, p.columnCount, t.columnCount), this._resizeHandler = b(() => this.refresh(), 100), this._initialized = !1, this.init();
47
+ this.element = e, this.settings = Object.assign({}, g, t), this.settings.columnCount = Object.assign({}, g.columnCount, t.columnCount), this._resizeHandler = b(() => this.refresh(), 100), this._initialized = !1, this.init();
48
48
  }
49
49
  init() {
50
50
  const e = this.element;
@@ -57,8 +57,8 @@ const l = class l {
57
57
  }
58
58
  refresh() {
59
59
  l.instances.forEach((e) => {
60
- const t = e.element, s = c.get(t);
61
- s && (s.col = s.columnCount[v()], e._setDivider(), Array.from(t.children).forEach((r) => {
60
+ const t = e.element, n = c.get(t);
61
+ n && (n.col = n.columnCount[v()], e._setDivider(), Array.from(t.children).forEach((r) => {
62
62
  e._setPostPosition(r);
63
63
  }));
64
64
  });
@@ -94,12 +94,16 @@ const l = class l {
94
94
  _initItems() {
95
95
  const e = this.element, t = this._getData();
96
96
  if (!t) return;
97
- const s = t.lastItemIdx, r = Array.from(e.children), n = r.slice(s);
98
- n.length !== 0 && (n.forEach((i, a) => {
99
- i.id || (i.id = "moT" + crypto.randomUUID() + "_" + (a + s)), i.classList.add("mo-item", "js-mo-item");
100
- }), this._setDivider(), n.forEach((i, a) => {
101
- t.showBadge && this._createBadge(i, a + s + 1), t.showArrow && this._createArrow(i);
102
- }), t.lastItemIdx = r.length, c.set(e, t), this.refresh(), requestAnimationFrame(() => this.refresh()));
97
+ const n = t.lastItemIdx, r = Array.from(e.children), s = r.slice(n);
98
+ s.length !== 0 && (s.forEach((a, i) => {
99
+ a.id || (a.id = "moT" + crypto.randomUUID() + "_" + (i + n)), a.classList.add("mo-item", "js-mo-item");
100
+ }), this._setDivider(), s.forEach((a, i) => {
101
+ t.showBadge && this._createBadge(a, i + n + 1), t.showArrow && this._createArrow(a);
102
+ }), t.lastItemIdx = r.length, c.set(e, t), this.refresh(), s.forEach((a) => {
103
+ a.querySelectorAll("img").forEach((i) => {
104
+ i.complete || i.addEventListener("load", this._resizeHandler, { once: !0 });
105
+ });
106
+ }));
103
107
  }
104
108
  _setPostPosition(e) {
105
109
  const t = this._getLeftOrRight(e);
@@ -109,22 +113,22 @@ const l = class l {
109
113
  if (!e) return null;
110
114
  const t = this._getData();
111
115
  if (!t) return null;
112
- const s = t.col, r = w(e, ".js-mo-inverted")[0] || null, n = w(e, ".js-mo-item:not(.js-mo-inverted)")[0] || null, i = m(n), a = m(r), h = m(e);
116
+ const n = t.col, r = w(e, ".js-mo-inverted")[0] || null, s = w(e, ".js-mo-item:not(.js-mo-inverted)")[0] || null, a = m(s), i = m(r), h = m(e);
113
117
  let d = 0, f = 0;
114
- if (s > 1) {
115
- i.gppu > h.o && (d = 1), a.gppu > i.gppu && (d = 0);
118
+ if (n > 1) {
119
+ a.gppu > h.o + 1 && (d = 1), i.gppu > a.gppu && (d = 0);
116
120
  const u = e.previousElementSibling;
117
121
  u && Math.abs(h.o - m(u).o) < 40 && (f = 1);
118
122
  }
119
123
  return { lr: d, badge_offset: f };
120
124
  }
121
125
  _createBadge(e, t) {
122
- const s = this._getData(), r = document.createElement("span");
123
- if (r.className = "mo-badge js-mo-badge", s.showCounterStyle === "none")
126
+ const n = this._getData(), r = document.createElement("span");
127
+ if (r.className = "mo-badge js-mo-badge", n.showCounterStyle === "none")
124
128
  r.style.opacity = "0";
125
- else if (s.showCounterStyle === "image") {
126
- const n = document.createElement("img");
127
- n.className = "mo-badge-icon", n.alt = "", n.src = e.dataset.moIcon || C, r.appendChild(n);
129
+ else if (n.showCounterStyle === "image") {
130
+ const s = document.createElement("img");
131
+ s.className = "mo-badge-icon", s.alt = "", s.src = e.dataset.moIcon || C, r.appendChild(s);
128
132
  } else
129
133
  r.textContent = t;
130
134
  e.prepend(r);
@@ -132,38 +136,38 @@ const l = class l {
132
136
  _createItemElement(e) {
133
137
  const t = document.createElement("li");
134
138
  e.icon && (t.dataset.moIcon = e.icon);
135
- const s = document.createElement("div");
136
- if (s.className = "mo-card", e.banner) {
137
- const n = document.createElement("div");
138
- n.className = "mo-card-image";
139
- const i = document.createElement("img");
140
- if (i.className = "mo-banner", i.src = e.banner, i.alt = "", n.appendChild(i), e.avatar) {
141
- const a = document.createElement("img");
142
- a.className = "mo-avatar", a.src = e.avatar, a.alt = "", n.appendChild(a);
139
+ const n = document.createElement("div");
140
+ if (n.className = "mo-card", e.banner) {
141
+ const s = document.createElement("div");
142
+ s.className = "mo-card-image";
143
+ const a = document.createElement("img");
144
+ if (a.className = "mo-banner", a.src = e.banner, a.alt = "", s.appendChild(a), e.avatar) {
145
+ const i = document.createElement("img");
146
+ i.className = "mo-avatar", i.src = e.avatar, i.alt = "", s.appendChild(i);
143
147
  }
144
- s.appendChild(n);
148
+ n.appendChild(s);
145
149
  }
146
150
  const r = document.createElement("div");
147
151
  if (r.className = "mo-card-body", e.title) {
148
- const n = document.createElement("h3");
149
- n.textContent = e.title, r.appendChild(n);
152
+ const s = document.createElement("h3");
153
+ s.textContent = e.title, r.appendChild(s);
150
154
  }
151
155
  if (e.meta) {
152
- const n = document.createElement("p");
153
- n.className = "mo-meta", n.textContent = e.meta, r.appendChild(n);
156
+ const s = document.createElement("p");
157
+ s.className = "mo-meta", s.textContent = e.meta, r.appendChild(s);
154
158
  }
155
159
  if (e.text) {
156
- const n = document.createElement("p");
157
- n.textContent = e.text, r.appendChild(n);
160
+ const s = document.createElement("p");
161
+ s.textContent = e.text, r.appendChild(s);
158
162
  }
159
- return s.appendChild(r), t.appendChild(s), t;
163
+ return n.appendChild(r), t.appendChild(n), t;
160
164
  }
161
165
  _createArrow(e) {
162
166
  const t = document.createElement("span");
163
167
  t.className = "mo-arrow js-mo-arrow", e.prepend(t);
164
168
  }
165
169
  };
166
- g(l, "instances", /* @__PURE__ */ new Set());
170
+ p(l, "instances", /* @__PURE__ */ new Set());
167
171
  let E = l;
168
172
  export {
169
173
  E as MoTimeline,
@@ -1,6 +1,6 @@
1
- (function(a,o){typeof exports=="object"&&typeof module<"u"?o(exports):typeof define=="function"&&define.amd?define(["exports"],o):(a=typeof globalThis<"u"?globalThis:a||self,o(a.MoTimeline={}))})(this,function(a){"use strict";var C=Object.defineProperty;var I=(a,o,m)=>o in a?C(a,o,{enumerable:!0,configurable:!0,writable:!0,value:m}):a[o]=m;var E=(a,o,m)=>I(a,typeof o!="symbol"?o+"":o,m);/*!
2
- * moTimeline v2.7.2
1
+ (function(a,o){typeof exports=="object"&&typeof module<"u"?o(exports):typeof define=="function"&&define.amd?define(["exports"],o):(a=typeof globalThis<"u"?globalThis:a||self,o(a.MoTimeline={}))})(this,function(a){"use strict";var C=Object.defineProperty;var I=(a,o,d)=>o in a?C(a,o,{enumerable:!0,configurable:!0,writable:!0,value:d}):a[o]=d;var y=(a,o,d)=>I(a,typeof o!="symbol"?o+"":o,d);/*!
2
+ * moTimeline v2.7.4
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
6
- */const o=new WeakMap,m={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px"},_="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";function p(){const c=window.innerWidth;return c<600?"xs":c<992?"sm":c<1200?"md":"lg"}function b(c,e=100){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>c(...s),e)}}function h(c){return c?{o:c.offsetTop,h:c.offsetHeight,gppu:c.offsetTop+c.offsetHeight}:{o:0,h:0,gppu:0}}function g(c,e){const t=[];let s=c.previousElementSibling;for(;s;)(!e||s.matches(e))&&t.push(s),s=s.previousElementSibling;return t}const d=class d{constructor(e,t={}){if(typeof e=="string"&&(e=document.querySelector(e)),!e)throw new Error("moTimeline: element not found");this.element=e,this.settings=Object.assign({},m,t),this.settings.columnCount=Object.assign({},m.columnCount,t.columnCount),this._resizeHandler=b(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(o.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});o.set(e,t),d.instances.add(this),e.classList.add("mo-timeline"),t.theme&&e.classList.add("mo-theme"),e.style.setProperty("--mo-card-border-radius",t.cardBorderRadius),e.style.setProperty("--mo-avatar-size",t.avatarSize),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){d.instances.forEach(e=>{const t=e.element,s=o.get(t);s&&(s.col=s.columnCount[p()],e._setDivider(),Array.from(t.children).forEach(i=>{e._setPostPosition(i)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}destroy(){window.removeEventListener("resize",this._resizeHandler),o.delete(this.element),d.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.classList.remove("mo-timeline","mo-theme","mo-twocol"),Array.from(this.element.children).forEach(e=>{e.classList.remove("mo-item","js-mo-item","mo-inverted","js-mo-inverted","mo-offset"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}_getData(){return o.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[p()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const s=t.lastItemIdx,i=Array.from(e.children),n=i.slice(s);n.length!==0&&(n.forEach((r,l)=>{r.id||(r.id="moT"+crypto.randomUUID()+"_"+(l+s)),r.classList.add("mo-item","js-mo-item")}),this._setDivider(),n.forEach((r,l)=>{t.showBadge&&this._createBadge(r,l+s+1),t.showArrow&&this._createArrow(r)}),t.lastItemIdx=i.length,o.set(e,t),this.refresh(),requestAnimationFrame(()=>this.refresh()))}_setPostPosition(e){const t=this._getLeftOrRight(e);t&&(e.classList.toggle("mo-inverted",t.lr>0),e.classList.toggle("js-mo-inverted",t.lr>0),e.classList.toggle("mo-offset",t.badge_offset>0))}_getLeftOrRight(e){if(!e)return null;const t=this._getData();if(!t)return null;const s=t.col,i=g(e,".js-mo-inverted")[0]||null,n=g(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,r=h(n),l=h(i),v=h(e);let u=0,w=0;if(s>1){r.gppu>v.o&&(u=1),l.gppu>r.gppu&&(u=0);const y=e.previousElementSibling;y&&Math.abs(v.o-h(y).o)<40&&(w=1)}return{lr:u,badge_offset:w}}_createBadge(e,t){const s=this._getData(),i=document.createElement("span");if(i.className="mo-badge js-mo-badge",s.showCounterStyle==="none")i.style.opacity="0";else if(s.showCounterStyle==="image"){const n=document.createElement("img");n.className="mo-badge-icon",n.alt="",n.src=e.dataset.moIcon||_,i.appendChild(n)}else i.textContent=t;e.prepend(i)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon);const s=document.createElement("div");if(s.className="mo-card",e.banner){const n=document.createElement("div");n.className="mo-card-image";const r=document.createElement("img");if(r.className="mo-banner",r.src=e.banner,r.alt="",n.appendChild(r),e.avatar){const l=document.createElement("img");l.className="mo-avatar",l.src=e.avatar,l.alt="",n.appendChild(l)}s.appendChild(n)}const i=document.createElement("div");if(i.className="mo-card-body",e.title){const n=document.createElement("h3");n.textContent=e.title,i.appendChild(n)}if(e.meta){const n=document.createElement("p");n.className="mo-meta",n.textContent=e.meta,i.appendChild(n)}if(e.text){const n=document.createElement("p");n.textContent=e.text,i.appendChild(n)}return s.appendChild(i),t.appendChild(s),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}};E(d,"instances",new Set);let f=d;a.MoTimeline=f,a.default=f,Object.defineProperties(a,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
6
+ */const o=new WeakMap,d={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px"},_="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";function p(){const c=window.innerWidth;return c<600?"xs":c<992?"sm":c<1200?"md":"lg"}function b(c,e=100){let t;return(...n)=>{clearTimeout(t),t=setTimeout(()=>c(...n),e)}}function h(c){return c?{o:c.offsetTop,h:c.offsetHeight,gppu:c.offsetTop+c.offsetHeight}:{o:0,h:0,gppu:0}}function g(c,e){const t=[];let n=c.previousElementSibling;for(;n;)(!e||n.matches(e))&&t.push(n),n=n.previousElementSibling;return t}const m=class m{constructor(e,t={}){if(typeof e=="string"&&(e=document.querySelector(e)),!e)throw new Error("moTimeline: element not found");this.element=e,this.settings=Object.assign({},d,t),this.settings.columnCount=Object.assign({},d.columnCount,t.columnCount),this._resizeHandler=b(()=>this.refresh(),100),this._initialized=!1,this.init()}init(){const e=this.element;if(o.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});o.set(e,t),m.instances.add(this),e.classList.add("mo-timeline"),t.theme&&e.classList.add("mo-theme"),e.style.setProperty("--mo-card-border-radius",t.cardBorderRadius),e.style.setProperty("--mo-avatar-size",t.avatarSize),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){m.instances.forEach(e=>{const t=e.element,n=o.get(t);n&&(n.col=n.columnCount[p()],e._setDivider(),Array.from(t.children).forEach(i=>{e._setPostPosition(i)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}destroy(){window.removeEventListener("resize",this._resizeHandler),o.delete(this.element),m.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.classList.remove("mo-timeline","mo-theme","mo-twocol"),Array.from(this.element.children).forEach(e=>{e.classList.remove("mo-item","js-mo-item","mo-inverted","js-mo-inverted","mo-offset"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}_getData(){return o.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[p()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const n=t.lastItemIdx,i=Array.from(e.children),s=i.slice(n);s.length!==0&&(s.forEach((r,l)=>{r.id||(r.id="moT"+crypto.randomUUID()+"_"+(l+n)),r.classList.add("mo-item","js-mo-item")}),this._setDivider(),s.forEach((r,l)=>{t.showBadge&&this._createBadge(r,l+n+1),t.showArrow&&this._createArrow(r)}),t.lastItemIdx=i.length,o.set(e,t),this.refresh(),s.forEach(r=>{r.querySelectorAll("img").forEach(l=>{l.complete||l.addEventListener("load",this._resizeHandler,{once:!0})})}))}_setPostPosition(e){const t=this._getLeftOrRight(e);t&&(e.classList.toggle("mo-inverted",t.lr>0),e.classList.toggle("js-mo-inverted",t.lr>0),e.classList.toggle("mo-offset",t.badge_offset>0))}_getLeftOrRight(e){if(!e)return null;const t=this._getData();if(!t)return null;const n=t.col,i=g(e,".js-mo-inverted")[0]||null,s=g(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,r=h(s),l=h(i),v=h(e);let u=0,w=0;if(n>1){r.gppu>v.o+1&&(u=1),l.gppu>r.gppu&&(u=0);const E=e.previousElementSibling;E&&Math.abs(v.o-h(E).o)<40&&(w=1)}return{lr:u,badge_offset:w}}_createBadge(e,t){const n=this._getData(),i=document.createElement("span");if(i.className="mo-badge js-mo-badge",n.showCounterStyle==="none")i.style.opacity="0";else if(n.showCounterStyle==="image"){const s=document.createElement("img");s.className="mo-badge-icon",s.alt="",s.src=e.dataset.moIcon||_,i.appendChild(s)}else i.textContent=t;e.prepend(i)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon);const n=document.createElement("div");if(n.className="mo-card",e.banner){const s=document.createElement("div");s.className="mo-card-image";const r=document.createElement("img");if(r.className="mo-banner",r.src=e.banner,r.alt="",s.appendChild(r),e.avatar){const l=document.createElement("img");l.className="mo-avatar",l.src=e.avatar,l.alt="",s.appendChild(l)}n.appendChild(s)}const i=document.createElement("div");if(i.className="mo-card-body",e.title){const s=document.createElement("h3");s.textContent=e.title,i.appendChild(s)}if(e.meta){const s=document.createElement("p");s.className="mo-meta",s.textContent=e.meta,i.appendChild(s)}if(e.text){const s=document.createElement("p");s.textContent=e.text,i.appendChild(s)}return n.appendChild(i),t.appendChild(n),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}};y(m,"instances",new Set);let f=m;a.MoTimeline=f,a.default=f,Object.defineProperties(a,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "motimeline",
3
- "version": "2.7.2",
3
+ "version": "2.7.4",
4
4
  "description": "Responsive two-column timeline layout library. Plain JavaScript, no dependencies.",
5
5
  "main": "./dist/moTimeline.cjs",
6
6
  "module": "./dist/moTimeline.js",
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * moTimeline v2.7.2 — CSS
2
+ * moTimeline v2.7.4 — CSS
3
3
  */
4
4
 
5
5
  /* ── CSS custom properties (easy override) ─────────────────── */
package/src/moTimeline.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * moTimeline v2.7.2
2
+ * moTimeline v2.7.4
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
@@ -9,12 +9,6 @@ import './moTimeline.css';
9
9
 
10
10
  const instanceData = new WeakMap();
11
11
 
12
- const BREAKPOINTS = {
13
- xs: 0,
14
- sm: 600,
15
- md: 992,
16
- lg: 1200,
17
- };
18
12
 
19
13
  const DEFAULTS = {
20
14
  columnCount: { xs: 1, sm: 2, md: 2, lg: 2 },
@@ -201,12 +195,17 @@ export class MoTimeline {
201
195
  data.lastItemIdx = allChildren.length;
202
196
  instanceData.set(el, data);
203
197
 
204
- // Sync pass (best-effort) + corrective rAF pass after paint.
205
- // Items appended just before this call may have offsetTop = 0 until the
206
- // browser lays them out, causing the column-fill algorithm to misplace
207
- // cards. The rAF ensures at least one correct layout after first paint.
208
198
  this.refresh();
209
- requestAnimationFrame(() => this.refresh());
199
+
200
+ // Re-layout after any unloaded images finish, because offsetHeight is
201
+ // based on text-only height until images are ready.
202
+ newItems.forEach((item) => {
203
+ item.querySelectorAll('img').forEach((img) => {
204
+ if (!img.complete) {
205
+ img.addEventListener('load', this._resizeHandler, { once: true });
206
+ }
207
+ });
208
+ });
210
209
  }
211
210
 
212
211
  _setPostPosition(el) {
@@ -237,12 +236,9 @@ export class MoTimeline {
237
236
  let bo = 0;
238
237
 
239
238
  if (col > 1) {
240
- if (l.gppu > e.o) pos = 1;
239
+ if (l.gppu > e.o + 1) pos = 1; // +1px tolerance for offsetHeight/offsetTop rounding mismatch
241
240
  if (r.gppu > l.gppu) pos = 0;
242
241
 
243
- // Badge collision: the LATER item (current, higher DOM index) gets the
244
- // offset — never the earlier one. Compare against the immediately
245
- // preceding sibling regardless of which column it is in.
246
242
  const prev = el.previousElementSibling;
247
243
  if (prev && Math.abs(e.o - getPosition(prev).o) < 40) bo = 1;
248
244
  }