motimeline 2.8.0 → 2.8.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/README.md CHANGED
@@ -19,7 +19,7 @@ Responsive two-column timeline layout library — plain JavaScript, zero depende
19
19
  - **Badges & arrows** — numbered badges on the center line, directional arrows
20
20
  - **Optional theme** — built-in card theme with image banners and overlapping avatars
21
21
  - **CSS custom properties** — override colors and sizes with one line of CSS
22
- - **Dynamic items** — append new `<li>` elements at any time via `initNewItems()`
22
+ - **Dynamic items** — append, insert, or inject `<li>` elements at any time via `initNewItems()`, `addItems()`, or `insertItem()`
23
23
  - **Bootstrap compatible** — wrap the `<ul>` in a Bootstrap `.container`, no config needed
24
24
  - **ESM · CJS · UMD** — works with any bundler or as a plain `<script>` tag
25
25
 
@@ -148,12 +148,28 @@ import 'motimeline/dist/moTimeline.css';
148
148
  ```js
149
149
  const tl = new MoTimeline(elementOrSelector, options);
150
150
 
151
- tl.refresh(); // re-layout all items (called automatically on resize)
152
- tl.initNewItems(); // pick up manually appended <li> elements
153
- tl.addItems(items); // create and append <li> from an array of item objects (or JSON string)
154
- tl.destroy(); // remove listeners and reset DOM classes
151
+ tl.refresh(); // re-layout all items (called automatically on resize)
152
+ tl.initNewItems(); // pick up manually appended <li> elements
153
+ tl.addItems(items); // create and append <li> from an array of item objects (or JSON string)
154
+ tl.insertItem(item, index); // insert a single item at a specific index (or random if omitted)
155
+ tl.destroy(); // remove listeners and reset DOM classes
155
156
  ```
156
157
 
158
+ ### insertItem
159
+
160
+ ```js
161
+ // Insert at a specific 0-based index:
162
+ tl.insertItem({ title: 'Breaking news', meta: 'Now', text: '...' }, 2);
163
+
164
+ // Insert at a random position (omit the index):
165
+ tl.insertItem({ title: 'Surprise!', meta: 'Now', text: '...' });
166
+
167
+ // Insert as a full-width item:
168
+ tl.insertItem({ title: 'Featured', meta: 'Now', text: '...', fullWidth: true }, 0);
169
+ ```
170
+
171
+ Badge numbers are automatically re-sequenced after insertion. Returns the inserted `<li>` element.
172
+
157
173
  ### addItems — item schema
158
174
 
159
175
  ```js
@@ -364,6 +380,9 @@ No framework option needed. Wrap the `<ul>` inside a Bootstrap `.container`:
364
380
 
365
381
  ## Changelog
366
382
 
383
+ ### v2.8.1
384
+ - New method `insertItem(item, index)` — inserts a single item at a specific 0-based index, or at a random position when index is omitted. Badge numbers are re-sequenced automatically.
385
+
367
386
  ### v2.8.0
368
387
  - New: full-width items — add `mo-fullwidth` class to any `<li>` to make it span both columns (two-column mode only). Badge and arrow are hidden automatically; card margin collapses to equal sides via `--mo-card-margin-fullwidth`
369
388
  - New option `randomFullWidth` (number 0–1 or boolean) — randomly promotes items to full-width during init (`true` = 33% probability)
@@ -1,6 +1,6 @@
1
- "use strict";var E=Object.defineProperty;var _=(n,e,t)=>e in n?E(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var p=(n,e,t)=>_(n,typeof e!="symbol"?e+"":e,t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});/*!
2
- * moTimeline v2.8.0
1
+ "use strict";var E=Object.defineProperty;var _=(a,e,t)=>e in a?E(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var p=(a,e,t)=>_(a,typeof e!="symbol"?e+"":e,t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});/*!
2
+ * moTimeline v2.8.1
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
6
- */const l=new WeakMap,v={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px",cardMargin:"0.5rem 1.25rem 0.5rem 0.5rem",cardMarginInverted:"0.5rem 0.5rem 0.5rem 1.25rem",cardMarginFullWidth:"0.5rem",randomFullWidth:0},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 n=window.innerWidth;return n<600?"xs":n<992?"sm":n<1200?"md":"lg"}function C(n,e=100){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>n(...s),e)}}function m(n){return n?{o:n.offsetTop,h:n.offsetHeight,gppu:n.offsetTop+n.offsetHeight}:{o:0,h:0,gppu:0}}function y(n,e){const t=[];let s=n.previousElementSibling;for(;s;)(!e||s.matches(e))&&t.push(s),s=s.previousElementSibling;return t}const c=class c{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(l.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});l.set(e,t),c.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),e.style.setProperty("--mo-card-margin",t.cardMargin),e.style.setProperty("--mo-card-margin-inverted",t.cardMarginInverted),e.style.setProperty("--mo-card-margin-fullwidth",t.cardMarginFullWidth),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){c.instances.forEach(e=>{const t=e.element,s=l.get(t);s&&(s.col=s.columnCount[w()],e._setDivider(),Array.from(t.children).forEach(o=>{e._setPostPosition(o)}))})}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),l.delete(this.element),c.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.style.removeProperty("--mo-card-margin"),this.element.style.removeProperty("--mo-card-margin-inverted"),this.element.style.removeProperty("--mo-card-margin-fullwidth"),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","mo-fullwidth","js-mo-fullwidth"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}_getData(){return l.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,o=Array.from(e.children),r=o.slice(s);if(r.length!==0){if(r.forEach((i,a)=>{i.id||(i.id="moT"+crypto.randomUUID()+"_"+(a+s)),i.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const i=t.randomFullWidth===!0?.33:t.randomFullWidth;r.forEach(a=>{!a.classList.contains("mo-fullwidth")&&Math.random()<i&&a.classList.add("mo-fullwidth","js-mo-fullwidth")})}r.forEach((i,a)=>{t.showBadge&&this._createBadge(i,a+s+1),t.showArrow&&this._createArrow(i)}),t.lastItemIdx=o.length,l.set(e,t),this.refresh(),r.forEach(i=>{i.querySelectorAll("img").forEach(a=>{a.complete||a.addEventListener("load",this._resizeHandler,{once:!0})})})}}_setPostPosition(e){if(e.classList.contains("mo-fullwidth")){e.classList.remove("mo-inverted","js-mo-inverted","mo-offset");return}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,o=y(e,".js-mo-inverted")[0]||null,r=y(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,i=m(r),a=m(o),u=m(e);let h=0,f=0;if(s>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 s=this._getData(),o=document.createElement("span");if(o.className="mo-badge js-mo-badge",s.showCounterStyle==="none")o.style.opacity="0";else if(s.showCounterStyle==="image"){const r=document.createElement("img");r.className="mo-badge-icon",r.alt="",r.src=e.dataset.moIcon||b,o.appendChild(r)}else o.textContent=t;e.prepend(o)}_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 r=document.createElement("div");r.className="mo-card-image";const i=document.createElement("img");if(i.className="mo-banner",i.src=e.banner,i.alt="",r.appendChild(i),e.avatar){const a=document.createElement("img");a.className="mo-avatar",a.src=e.avatar,a.alt="",r.appendChild(a)}s.appendChild(r)}const o=document.createElement("div");if(o.className="mo-card-body",e.title){const r=document.createElement("h3");r.textContent=e.title,o.appendChild(r)}if(e.meta){const r=document.createElement("p");r.className="mo-meta",r.textContent=e.meta,o.appendChild(r)}if(e.text){const r=document.createElement("p");r.textContent=e.text,o.appendChild(r)}return s.appendChild(o),t.appendChild(s),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}};p(c,"instances",new Set);let d=c;exports.MoTimeline=d;exports.default=d;
6
+ */const c=new WeakMap,w={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px",cardMargin:"0.5rem 1.25rem 0.5rem 0.5rem",cardMarginInverted:"0.5rem 0.5rem 0.5rem 1.25rem",cardMarginFullWidth:"0.5rem",randomFullWidth:0},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 v(){const a=window.innerWidth;return a<600?"xs":a<992?"sm":a<1200?"md":"lg"}function I(a,e=100){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>a(...s),e)}}function f(a){return a?{o:a.offsetTop,h:a.offsetHeight,gppu:a.offsetTop+a.offsetHeight}:{o:0,h:0,gppu:0}}function y(a,e){const t=[];let s=a.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({},w,t),this.settings.columnCount=Object.assign({},w.columnCount,t.columnCount),this._resizeHandler=I(()=>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),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),e.style.setProperty("--mo-card-margin",t.cardMargin),e.style.setProperty("--mo-card-margin-inverted",t.cardMarginInverted),e.style.setProperty("--mo-card-margin-fullwidth",t.cardMarginFullWidth),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=c.get(t);s&&(s.col=s.columnCount[v()],e._setDivider(),Array.from(t.children).forEach(o=>{e._setPostPosition(o)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}insertItem(e,t){const s=this.element,o=this._getData();if(!o)return;const r=this._createItemElement(e),n=Array.from(s.children).filter(l=>l.classList.contains("js-mo-item")),i=t==null?Math.floor(Math.random()*(n.length+1)):Math.max(0,Math.min(t,n.length));if(i>=n.length?s.appendChild(r):s.insertBefore(r,n[i]),r.id||(r.id="moT"+crypto.randomUUID()+"_"+i),r.classList.add("mo-item","js-mo-item"),e.fullWidth)r.classList.add("mo-fullwidth","js-mo-fullwidth");else if(o.randomFullWidth){const l=o.randomFullWidth===!0?.33:o.randomFullWidth;Math.random()<l&&r.classList.add("mo-fullwidth","js-mo-fullwidth")}return o.showBadge&&this._createBadge(r,i+1),o.showArrow&&this._createArrow(r),o.showBadge&&o.showCounterStyle==="counter"&&Array.from(s.querySelectorAll(".js-mo-item")).forEach((l,m)=>{const h=l.querySelector(".js-mo-badge");h&&(h.textContent=m+1)}),o.lastItemIdx=Array.from(s.children).length,c.set(s,o),this.refresh(),r.querySelectorAll("img").forEach(l=>{l.complete||l.addEventListener("load",this._resizeHandler,{once:!0})}),r}destroy(){window.removeEventListener("resize",this._resizeHandler),c.delete(this.element),d.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.style.removeProperty("--mo-card-margin"),this.element.style.removeProperty("--mo-card-margin-inverted"),this.element.style.removeProperty("--mo-card-margin-fullwidth"),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","mo-fullwidth","js-mo-fullwidth"),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[v()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const s=t.lastItemIdx,o=Array.from(e.children),r=o.slice(s);if(r.length!==0){if(r.forEach((n,i)=>{n.id||(n.id="moT"+crypto.randomUUID()+"_"+(i+s)),n.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const n=t.randomFullWidth===!0?.33:t.randomFullWidth;r.forEach(i=>{!i.classList.contains("mo-fullwidth")&&Math.random()<n&&i.classList.add("mo-fullwidth","js-mo-fullwidth")})}r.forEach((n,i)=>{t.showBadge&&this._createBadge(n,i+s+1),t.showArrow&&this._createArrow(n)}),t.lastItemIdx=o.length,c.set(e,t),this.refresh(),r.forEach(n=>{n.querySelectorAll("img").forEach(i=>{i.complete||i.addEventListener("load",this._resizeHandler,{once:!0})})})}}_setPostPosition(e){if(e.classList.contains("mo-fullwidth")){e.classList.remove("mo-inverted","js-mo-inverted","mo-offset");return}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,o=y(e,".js-mo-inverted")[0]||null,r=y(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,n=f(r),i=f(o),l=f(e);let m=0,h=0;if(s>1){n.gppu>l.o+1&&(m=1),i.gppu>n.gppu&&(m=0);const g=e.previousElementSibling;g&&Math.abs(l.o-f(g).o)<40&&(h=1)}return{lr:m,badge_offset:h}}_createBadge(e,t){const s=this._getData(),o=document.createElement("span");if(o.className="mo-badge js-mo-badge",s.showCounterStyle==="none")o.style.opacity="0";else if(s.showCounterStyle==="image"){const r=document.createElement("img");r.className="mo-badge-icon",r.alt="",r.src=e.dataset.moIcon||b,o.appendChild(r)}else o.textContent=t;e.prepend(o)}_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 r=document.createElement("div");r.className="mo-card-image";const n=document.createElement("img");if(n.className="mo-banner",n.src=e.banner,n.alt="",r.appendChild(n),e.avatar){const i=document.createElement("img");i.className="mo-avatar",i.src=e.avatar,i.alt="",r.appendChild(i)}s.appendChild(r)}const o=document.createElement("div");if(o.className="mo-card-body",e.title){const r=document.createElement("h3");r.textContent=e.title,o.appendChild(r)}if(e.meta){const r=document.createElement("p");r.className="mo-meta",r.textContent=e.meta,o.appendChild(r)}if(e.text){const r=document.createElement("p");r.textContent=e.text,o.appendChild(r)}return s.appendChild(o),t.appendChild(s),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}};p(d,"instances",new Set);let u=d;exports.MoTimeline=u;exports.default=u;
@@ -1,3 +1,3 @@
1
1
  /*!
2
- * moTimeline v2.8.0 — CSS
2
+ * moTimeline v2.8.1 — 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-timeline.mo-twocol>.mo-item.mo-fullwidth{clear:both;float:none;width:100%}.mo-theme>.mo-item.mo-fullwidth .mo-card{margin:var(--mo-card-margin-fullwidth, .5rem)}.mo-timeline.mo-twocol>.mo-item.mo-fullwidth .mo-badge,.mo-timeline.mo-twocol>.mo-item.mo-fullwidth .mo-arrow{display:none}.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:var(--mo-card-margin, .5rem 1.25rem .5rem .5rem);position:relative}.mo-theme>.mo-item.mo-inverted .mo-card{margin:var(--mo-card-margin-inverted, .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 E = Object.defineProperty;
2
- var _ = (n, e, t) => e in n ? E(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t;
3
- var g = (n, e, t) => _(n, typeof e != "symbol" ? e + "" : e, t);
2
+ var _ = (a, e, t) => e in a ? E(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t;
3
+ var g = (a, e, t) => _(a, typeof e != "symbol" ? e + "" : e, t);
4
4
  /*!
5
- * moTimeline v2.8.0
5
+ * moTimeline v2.8.1
6
6
  * Responsive two-column timeline layout library
7
7
  * https://github.com/MattOpen/moTimeline
8
8
  * MIT License
9
9
  */
10
- const l = /* @__PURE__ */ new WeakMap(), p = {
10
+ const c = /* @__PURE__ */ new WeakMap(), p = {
11
11
  columnCount: { xs: 1, sm: 2, md: 2, lg: 2 },
12
12
  showBadge: !1,
13
13
  showArrow: !1,
@@ -21,49 +21,49 @@ const l = /* @__PURE__ */ new WeakMap(), p = {
21
21
  cardMarginFullWidth: "0.5rem",
22
22
  randomFullWidth: 0
23
23
  // 0 = off; 0–1 = probability per item; true = 0.33
24
- }, 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>";
25
- function v() {
26
- const n = window.innerWidth;
27
- return n < 600 ? "xs" : n < 992 ? "sm" : n < 1200 ? "md" : "lg";
24
+ }, I = "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>";
25
+ function w() {
26
+ const a = window.innerWidth;
27
+ return a < 600 ? "xs" : a < 992 ? "sm" : a < 1200 ? "md" : "lg";
28
28
  }
29
- function C(n, e = 100) {
29
+ function b(a, e = 100) {
30
30
  let t;
31
31
  return (...s) => {
32
- clearTimeout(t), t = setTimeout(() => n(...s), e);
32
+ clearTimeout(t), t = setTimeout(() => a(...s), e);
33
33
  };
34
34
  }
35
- function m(n) {
36
- return n ? {
37
- o: n.offsetTop,
38
- h: n.offsetHeight,
39
- gppu: n.offsetTop + n.offsetHeight
35
+ function f(a) {
36
+ return a ? {
37
+ o: a.offsetTop,
38
+ h: a.offsetHeight,
39
+ gppu: a.offsetTop + a.offsetHeight
40
40
  } : { o: 0, h: 0, gppu: 0 };
41
41
  }
42
- function w(n, e) {
42
+ function v(a, e) {
43
43
  const t = [];
44
- let s = n.previousElementSibling;
44
+ let s = a.previousElementSibling;
45
45
  for (; s; )
46
46
  (!e || s.matches(e)) && t.push(s), s = s.previousElementSibling;
47
47
  return t;
48
48
  }
49
- const c = class c {
49
+ const d = class d {
50
50
  constructor(e, t = {}) {
51
51
  if (typeof e == "string" && (e = document.querySelector(e)), !e) throw new Error("moTimeline: element not found");
52
- this.element = e, this.settings = Object.assign({}, p, t), this.settings.columnCount = Object.assign({}, p.columnCount, t.columnCount), this._resizeHandler = C(() => this.refresh(), 100), this._initialized = !1, this.init();
52
+ 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();
53
53
  }
54
54
  init() {
55
55
  const e = this.element;
56
- if (l.has(e)) {
56
+ if (c.has(e)) {
57
57
  this.refresh();
58
58
  return;
59
59
  }
60
60
  const t = Object.assign({}, this.settings, { lastItemIdx: 0 });
61
- l.set(e, t), c.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), e.style.setProperty("--mo-card-margin", t.cardMargin), e.style.setProperty("--mo-card-margin-inverted", t.cardMarginInverted), e.style.setProperty("--mo-card-margin-fullwidth", t.cardMarginFullWidth), this._initialized = !0, window.addEventListener("resize", this._resizeHandler), Array.from(e.children).length > 0 && this._initItems();
61
+ c.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), e.style.setProperty("--mo-card-margin", t.cardMargin), e.style.setProperty("--mo-card-margin-inverted", t.cardMarginInverted), e.style.setProperty("--mo-card-margin-fullwidth", t.cardMarginFullWidth), this._initialized = !0, window.addEventListener("resize", this._resizeHandler), Array.from(e.children).length > 0 && this._initItems();
62
62
  }
63
63
  refresh() {
64
- c.instances.forEach((e) => {
65
- const t = e.element, s = l.get(t);
66
- s && (s.col = s.columnCount[v()], e._setDivider(), Array.from(t.children).forEach((o) => {
64
+ d.instances.forEach((e) => {
65
+ const t = e.element, s = c.get(t);
66
+ s && (s.col = s.columnCount[w()], e._setDivider(), Array.from(t.children).forEach((o) => {
67
67
  e._setPostPosition(o);
68
68
  }));
69
69
  });
@@ -83,37 +83,61 @@ const c = class c {
83
83
  addItems(e) {
84
84
  typeof e == "string" && (e = JSON.parse(e)), e.forEach((t) => this.element.appendChild(this._createItemElement(t))), this._initItems();
85
85
  }
86
+ /**
87
+ * Insert a single item at a specific position or at a random position.
88
+ *
89
+ * @param {Object} item — same shape as addItems(): { title, meta, text, banner, avatar, icon }
90
+ * @param {number} [index] — 0-based insertion index. Omit (or pass undefined) for a random position.
91
+ * @returns {HTMLElement} the inserted <li> element
92
+ */
93
+ insertItem(e, t) {
94
+ const s = this.element, o = this._getData();
95
+ if (!o) return;
96
+ const r = this._createItemElement(e), n = Array.from(s.children).filter((l) => l.classList.contains("js-mo-item")), i = t == null ? Math.floor(Math.random() * (n.length + 1)) : Math.max(0, Math.min(t, n.length));
97
+ if (i >= n.length ? s.appendChild(r) : s.insertBefore(r, n[i]), r.id || (r.id = "moT" + crypto.randomUUID() + "_" + i), r.classList.add("mo-item", "js-mo-item"), e.fullWidth)
98
+ r.classList.add("mo-fullwidth", "js-mo-fullwidth");
99
+ else if (o.randomFullWidth) {
100
+ const l = o.randomFullWidth === !0 ? 0.33 : o.randomFullWidth;
101
+ Math.random() < l && r.classList.add("mo-fullwidth", "js-mo-fullwidth");
102
+ }
103
+ return o.showBadge && this._createBadge(r, i + 1), o.showArrow && this._createArrow(r), o.showBadge && o.showCounterStyle === "counter" && Array.from(s.querySelectorAll(".js-mo-item")).forEach((l, m) => {
104
+ const h = l.querySelector(".js-mo-badge");
105
+ h && (h.textContent = m + 1);
106
+ }), o.lastItemIdx = Array.from(s.children).length, c.set(s, o), this.refresh(), r.querySelectorAll("img").forEach((l) => {
107
+ l.complete || l.addEventListener("load", this._resizeHandler, { once: !0 });
108
+ }), r;
109
+ }
86
110
  destroy() {
87
- window.removeEventListener("resize", this._resizeHandler), l.delete(this.element), c.instances.delete(this), this.element.style.removeProperty("--mo-card-border-radius"), this.element.style.removeProperty("--mo-avatar-size"), this.element.style.removeProperty("--mo-card-margin"), this.element.style.removeProperty("--mo-card-margin-inverted"), this.element.style.removeProperty("--mo-card-margin-fullwidth"), this.element.classList.remove("mo-timeline", "mo-theme", "mo-twocol"), Array.from(this.element.children).forEach((e) => {
111
+ window.removeEventListener("resize", this._resizeHandler), c.delete(this.element), d.instances.delete(this), this.element.style.removeProperty("--mo-card-border-radius"), this.element.style.removeProperty("--mo-avatar-size"), this.element.style.removeProperty("--mo-card-margin"), this.element.style.removeProperty("--mo-card-margin-inverted"), this.element.style.removeProperty("--mo-card-margin-fullwidth"), this.element.classList.remove("mo-timeline", "mo-theme", "mo-twocol"), Array.from(this.element.children).forEach((e) => {
88
112
  e.classList.remove("mo-item", "js-mo-item", "mo-inverted", "js-mo-inverted", "mo-offset", "mo-fullwidth", "js-mo-fullwidth"), e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach((t) => t.remove());
89
113
  });
90
114
  }
91
115
  // ─── Private ────────────────────────────────────────────────────────────────
92
116
  _getData() {
93
- return l.get(this.element);
117
+ return c.get(this.element);
94
118
  }
95
119
  _setDivider() {
96
120
  const e = this._getData();
97
- e && (e.col = e.columnCount[v()], this.element.classList.toggle("mo-twocol", e.col > 1));
121
+ e && (e.col = e.columnCount[w()], this.element.classList.toggle("mo-twocol", e.col > 1));
98
122
  }
99
123
  _initItems() {
100
124
  const e = this.element, t = this._getData();
101
125
  if (!t) return;
102
126
  const s = t.lastItemIdx, o = Array.from(e.children), r = o.slice(s);
103
127
  if (r.length !== 0) {
104
- if (r.forEach((i, a) => {
105
- i.id || (i.id = "moT" + crypto.randomUUID() + "_" + (a + s)), i.classList.add("mo-item", "js-mo-item");
128
+ if (r.forEach((n, i) => {
129
+ n.id || (n.id = "moT" + crypto.randomUUID() + "_" + (i + s)), n.classList.add("mo-item", "js-mo-item");
106
130
  }), this._setDivider(), t.randomFullWidth) {
107
- const i = t.randomFullWidth === !0 ? 0.33 : t.randomFullWidth;
108
- r.forEach((a) => {
109
- !a.classList.contains("mo-fullwidth") && Math.random() < i && a.classList.add("mo-fullwidth", "js-mo-fullwidth");
131
+ const n = t.randomFullWidth === !0 ? 0.33 : t.randomFullWidth;
132
+ r.forEach((i) => {
133
+ !i.classList.contains("mo-fullwidth") && Math.random() < n && i.classList.add("mo-fullwidth", "js-mo-fullwidth");
110
134
  });
111
135
  }
112
- r.forEach((i, a) => {
113
- t.showBadge && this._createBadge(i, a + s + 1), t.showArrow && this._createArrow(i);
114
- }), t.lastItemIdx = o.length, l.set(e, t), this.refresh(), r.forEach((i) => {
115
- i.querySelectorAll("img").forEach((a) => {
116
- a.complete || a.addEventListener("load", this._resizeHandler, { once: !0 });
136
+ r.forEach((n, i) => {
137
+ t.showBadge && this._createBadge(n, i + s + 1), t.showArrow && this._createArrow(n);
138
+ }), t.lastItemIdx = o.length, c.set(e, t), this.refresh(), r.forEach((n) => {
139
+ n.querySelectorAll("img").forEach((i) => {
140
+ i.complete || i.addEventListener("load", this._resizeHandler, { once: !0 });
117
141
  });
118
142
  });
119
143
  }
@@ -130,14 +154,14 @@ const c = class c {
130
154
  if (!e) return null;
131
155
  const t = this._getData();
132
156
  if (!t) return null;
133
- const s = t.col, o = w(e, ".js-mo-inverted")[0] || null, r = w(e, ".js-mo-item:not(.js-mo-inverted)")[0] || null, i = m(r), a = m(o), h = m(e);
134
- let d = 0, f = 0;
157
+ const s = t.col, o = v(e, ".js-mo-inverted")[0] || null, r = v(e, ".js-mo-item:not(.js-mo-inverted)")[0] || null, n = f(r), i = f(o), l = f(e);
158
+ let m = 0, h = 0;
135
159
  if (s > 1) {
136
- i.gppu > h.o + 1 && (d = 1), a.gppu > i.gppu && (d = 0);
160
+ n.gppu > l.o + 1 && (m = 1), i.gppu > n.gppu && (m = 0);
137
161
  const u = e.previousElementSibling;
138
- u && Math.abs(h.o - m(u).o) < 40 && (f = 1);
162
+ u && Math.abs(l.o - f(u).o) < 40 && (h = 1);
139
163
  }
140
- return { lr: d, badge_offset: f };
164
+ return { lr: m, badge_offset: h };
141
165
  }
142
166
  _createBadge(e, t) {
143
167
  const s = this._getData(), o = document.createElement("span");
@@ -145,7 +169,7 @@ const c = class c {
145
169
  o.style.opacity = "0";
146
170
  else if (s.showCounterStyle === "image") {
147
171
  const r = document.createElement("img");
148
- r.className = "mo-badge-icon", r.alt = "", r.src = e.dataset.moIcon || b, o.appendChild(r);
172
+ r.className = "mo-badge-icon", r.alt = "", r.src = e.dataset.moIcon || I, o.appendChild(r);
149
173
  } else
150
174
  o.textContent = t;
151
175
  e.prepend(o);
@@ -157,10 +181,10 @@ const c = class c {
157
181
  if (s.className = "mo-card", e.banner) {
158
182
  const r = document.createElement("div");
159
183
  r.className = "mo-card-image";
160
- const i = document.createElement("img");
161
- if (i.className = "mo-banner", i.src = e.banner, i.alt = "", r.appendChild(i), e.avatar) {
162
- const a = document.createElement("img");
163
- a.className = "mo-avatar", a.src = e.avatar, a.alt = "", r.appendChild(a);
184
+ const n = document.createElement("img");
185
+ if (n.className = "mo-banner", n.src = e.banner, n.alt = "", r.appendChild(n), e.avatar) {
186
+ const i = document.createElement("img");
187
+ i.className = "mo-avatar", i.src = e.avatar, i.alt = "", r.appendChild(i);
164
188
  }
165
189
  s.appendChild(r);
166
190
  }
@@ -184,8 +208,8 @@ const c = class c {
184
208
  t.className = "mo-arrow js-mo-arrow", e.prepend(t);
185
209
  }
186
210
  };
187
- g(c, "instances", /* @__PURE__ */ new Set());
188
- let y = c;
211
+ g(d, "instances", /* @__PURE__ */ new Set());
212
+ let y = d;
189
213
  export {
190
214
  y as MoTimeline,
191
215
  y as default
@@ -1,6 +1,6 @@
1
- (function(l,n){typeof exports=="object"&&typeof module<"u"?n(exports):typeof define=="function"&&define.amd?define(["exports"],n):(l=typeof globalThis<"u"?globalThis:l||self,n(l.MoTimeline={}))})(this,function(l){"use strict";var C=Object.defineProperty;var I=(l,n,d)=>n in l?C(l,n,{enumerable:!0,configurable:!0,writable:!0,value:d}):l[n]=d;var E=(l,n,d)=>I(l,typeof n!="symbol"?n+"":n,d);/*!
2
- * moTimeline v2.8.0
1
+ (function(l,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(l=typeof globalThis<"u"?globalThis:l||self,a(l.MoTimeline={}))})(this,function(l){"use strict";var I=Object.defineProperty;var C=(l,a,m)=>a in l?I(l,a,{enumerable:!0,configurable:!0,writable:!0,value:m}):l[a]=m;var E=(l,a,m)=>C(l,typeof a!="symbol"?a+"":a,m);/*!
2
+ * moTimeline v2.8.1
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
6
- */const n=new WeakMap,d={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px",cardMargin:"0.5rem 1.25rem 0.5rem 0.5rem",cardMarginInverted:"0.5rem 0.5rem 0.5rem 1.25rem",cardMarginFullWidth:"0.5rem",randomFullWidth:0},_="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 g(){const c=window.innerWidth;return c<600?"xs":c<992?"sm":c<1200?"md":"lg"}function b(c,e=100){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>c(...r),e)}}function h(c){return c?{o:c.offsetTop,h:c.offsetHeight,gppu:c.offsetTop+c.offsetHeight}:{o:0,h:0,gppu:0}}function p(c,e){const t=[];let r=c.previousElementSibling;for(;r;)(!e||r.matches(e))&&t.push(r),r=r.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(n.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});n.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),e.style.setProperty("--mo-card-margin",t.cardMargin),e.style.setProperty("--mo-card-margin-inverted",t.cardMarginInverted),e.style.setProperty("--mo-card-margin-fullwidth",t.cardMarginFullWidth),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,r=n.get(t);r&&(r.col=r.columnCount[g()],e._setDivider(),Array.from(t.children).forEach(o=>{e._setPostPosition(o)}))})}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),n.delete(this.element),m.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.style.removeProperty("--mo-card-margin"),this.element.style.removeProperty("--mo-card-margin-inverted"),this.element.style.removeProperty("--mo-card-margin-fullwidth"),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","mo-fullwidth","js-mo-fullwidth"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}_getData(){return n.get(this.element)}_setDivider(){const e=this._getData();e&&(e.col=e.columnCount[g()],this.element.classList.toggle("mo-twocol",e.col>1))}_initItems(){const e=this.element,t=this._getData();if(!t)return;const r=t.lastItemIdx,o=Array.from(e.children),s=o.slice(r);if(s.length!==0){if(s.forEach((i,a)=>{i.id||(i.id="moT"+crypto.randomUUID()+"_"+(a+r)),i.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const i=t.randomFullWidth===!0?.33:t.randomFullWidth;s.forEach(a=>{!a.classList.contains("mo-fullwidth")&&Math.random()<i&&a.classList.add("mo-fullwidth","js-mo-fullwidth")})}s.forEach((i,a)=>{t.showBadge&&this._createBadge(i,a+r+1),t.showArrow&&this._createArrow(i)}),t.lastItemIdx=o.length,n.set(e,t),this.refresh(),s.forEach(i=>{i.querySelectorAll("img").forEach(a=>{a.complete||a.addEventListener("load",this._resizeHandler,{once:!0})})})}}_setPostPosition(e){if(e.classList.contains("mo-fullwidth")){e.classList.remove("mo-inverted","js-mo-inverted","mo-offset");return}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 r=t.col,o=p(e,".js-mo-inverted")[0]||null,s=p(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,i=h(s),a=h(o),v=h(e);let u=0,w=0;if(r>1){i.gppu>v.o+1&&(u=1),a.gppu>i.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 r=this._getData(),o=document.createElement("span");if(o.className="mo-badge js-mo-badge",r.showCounterStyle==="none")o.style.opacity="0";else if(r.showCounterStyle==="image"){const s=document.createElement("img");s.className="mo-badge-icon",s.alt="",s.src=e.dataset.moIcon||_,o.appendChild(s)}else o.textContent=t;e.prepend(o)}_createItemElement(e){const t=document.createElement("li");e.icon&&(t.dataset.moIcon=e.icon);const r=document.createElement("div");if(r.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)}r.appendChild(s)}const o=document.createElement("div");if(o.className="mo-card-body",e.title){const s=document.createElement("h3");s.textContent=e.title,o.appendChild(s)}if(e.meta){const s=document.createElement("p");s.className="mo-meta",s.textContent=e.meta,o.appendChild(s)}if(e.text){const s=document.createElement("p");s.textContent=e.text,o.appendChild(s)}return r.appendChild(o),t.appendChild(r),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}};E(m,"instances",new Set);let f=m;l.MoTimeline=f,l.default=f,Object.defineProperties(l,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
6
+ */const a=new WeakMap,m={columnCount:{xs:1,sm:2,md:2,lg:2},showBadge:!1,showArrow:!1,theme:!1,showCounterStyle:"counter",cardBorderRadius:"8px",avatarSize:"50px",cardMargin:"0.5rem 1.25rem 0.5rem 0.5rem",cardMarginInverted:"0.5rem 0.5rem 0.5rem 1.25rem",cardMarginFullWidth:"0.5rem",randomFullWidth:0},_="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 d=window.innerWidth;return d<600?"xs":d<992?"sm":d<1200?"md":"lg"}function b(d,e=100){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>d(...s),e)}}function g(d){return d?{o:d.offsetTop,h:d.offsetHeight,gppu:d.offsetTop+d.offsetHeight}:{o:0,h:0,gppu:0}}function v(d,e){const t=[];let s=d.previousElementSibling;for(;s;)(!e||s.matches(e))&&t.push(s),s=s.previousElementSibling;return t}const h=class h{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(a.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});a.set(e,t),h.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),e.style.setProperty("--mo-card-margin",t.cardMargin),e.style.setProperty("--mo-card-margin-inverted",t.cardMarginInverted),e.style.setProperty("--mo-card-margin-fullwidth",t.cardMarginFullWidth),this._initialized=!0,window.addEventListener("resize",this._resizeHandler),Array.from(e.children).length>0&&this._initItems()}refresh(){h.instances.forEach(e=>{const t=e.element,s=a.get(t);s&&(s.col=s.columnCount[w()],e._setDivider(),Array.from(t.children).forEach(o=>{e._setPostPosition(o)}))})}initNewItems(){this._initItems()}addItems(e){typeof e=="string"&&(e=JSON.parse(e)),e.forEach(t=>this.element.appendChild(this._createItemElement(t))),this._initItems()}insertItem(e,t){const s=this.element,o=this._getData();if(!o)return;const r=this._createItemElement(e),n=Array.from(s.children).filter(c=>c.classList.contains("js-mo-item")),i=t==null?Math.floor(Math.random()*(n.length+1)):Math.max(0,Math.min(t,n.length));if(i>=n.length?s.appendChild(r):s.insertBefore(r,n[i]),r.id||(r.id="moT"+crypto.randomUUID()+"_"+i),r.classList.add("mo-item","js-mo-item"),e.fullWidth)r.classList.add("mo-fullwidth","js-mo-fullwidth");else if(o.randomFullWidth){const c=o.randomFullWidth===!0?.33:o.randomFullWidth;Math.random()<c&&r.classList.add("mo-fullwidth","js-mo-fullwidth")}return o.showBadge&&this._createBadge(r,i+1),o.showArrow&&this._createArrow(r),o.showBadge&&o.showCounterStyle==="counter"&&Array.from(s.querySelectorAll(".js-mo-item")).forEach((c,f)=>{const u=c.querySelector(".js-mo-badge");u&&(u.textContent=f+1)}),o.lastItemIdx=Array.from(s.children).length,a.set(s,o),this.refresh(),r.querySelectorAll("img").forEach(c=>{c.complete||c.addEventListener("load",this._resizeHandler,{once:!0})}),r}destroy(){window.removeEventListener("resize",this._resizeHandler),a.delete(this.element),h.instances.delete(this),this.element.style.removeProperty("--mo-card-border-radius"),this.element.style.removeProperty("--mo-avatar-size"),this.element.style.removeProperty("--mo-card-margin"),this.element.style.removeProperty("--mo-card-margin-inverted"),this.element.style.removeProperty("--mo-card-margin-fullwidth"),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","mo-fullwidth","js-mo-fullwidth"),e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach(t=>t.remove())})}_getData(){return a.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,o=Array.from(e.children),r=o.slice(s);if(r.length!==0){if(r.forEach((n,i)=>{n.id||(n.id="moT"+crypto.randomUUID()+"_"+(i+s)),n.classList.add("mo-item","js-mo-item")}),this._setDivider(),t.randomFullWidth){const n=t.randomFullWidth===!0?.33:t.randomFullWidth;r.forEach(i=>{!i.classList.contains("mo-fullwidth")&&Math.random()<n&&i.classList.add("mo-fullwidth","js-mo-fullwidth")})}r.forEach((n,i)=>{t.showBadge&&this._createBadge(n,i+s+1),t.showArrow&&this._createArrow(n)}),t.lastItemIdx=o.length,a.set(e,t),this.refresh(),r.forEach(n=>{n.querySelectorAll("img").forEach(i=>{i.complete||i.addEventListener("load",this._resizeHandler,{once:!0})})})}}_setPostPosition(e){if(e.classList.contains("mo-fullwidth")){e.classList.remove("mo-inverted","js-mo-inverted","mo-offset");return}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,o=v(e,".js-mo-inverted")[0]||null,r=v(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,n=g(r),i=g(o),c=g(e);let f=0,u=0;if(s>1){n.gppu>c.o+1&&(f=1),i.gppu>n.gppu&&(f=0);const y=e.previousElementSibling;y&&Math.abs(c.o-g(y).o)<40&&(u=1)}return{lr:f,badge_offset:u}}_createBadge(e,t){const s=this._getData(),o=document.createElement("span");if(o.className="mo-badge js-mo-badge",s.showCounterStyle==="none")o.style.opacity="0";else if(s.showCounterStyle==="image"){const r=document.createElement("img");r.className="mo-badge-icon",r.alt="",r.src=e.dataset.moIcon||_,o.appendChild(r)}else o.textContent=t;e.prepend(o)}_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 r=document.createElement("div");r.className="mo-card-image";const n=document.createElement("img");if(n.className="mo-banner",n.src=e.banner,n.alt="",r.appendChild(n),e.avatar){const i=document.createElement("img");i.className="mo-avatar",i.src=e.avatar,i.alt="",r.appendChild(i)}s.appendChild(r)}const o=document.createElement("div");if(o.className="mo-card-body",e.title){const r=document.createElement("h3");r.textContent=e.title,o.appendChild(r)}if(e.meta){const r=document.createElement("p");r.className="mo-meta",r.textContent=e.meta,o.appendChild(r)}if(e.text){const r=document.createElement("p");r.textContent=e.text,o.appendChild(r)}return s.appendChild(o),t.appendChild(s),t}_createArrow(e){const t=document.createElement("span");t.className="mo-arrow js-mo-arrow",e.prepend(t)}};E(h,"instances",new Set);let p=h;l.MoTimeline=p,l.default=p,Object.defineProperties(l,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "motimeline",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
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.8.0 — CSS
2
+ * moTimeline v2.8.1 — 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.8.0
2
+ * moTimeline v2.8.1
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
@@ -142,6 +142,67 @@ export class MoTimeline {
142
142
  this._initItems();
143
143
  }
144
144
 
145
+ /**
146
+ * Insert a single item at a specific position or at a random position.
147
+ *
148
+ * @param {Object} item — same shape as addItems(): { title, meta, text, banner, avatar, icon }
149
+ * @param {number} [index] — 0-based insertion index. Omit (or pass undefined) for a random position.
150
+ * @returns {HTMLElement} the inserted <li> element
151
+ */
152
+ insertItem(item, index) {
153
+ const el = this.element;
154
+ const data = this._getData();
155
+ if (!data) return;
156
+
157
+ const newEl = this._createItemElement(item);
158
+
159
+ // All currently initialised items (to determine valid range)
160
+ const items = Array.from(el.children).filter((c) => c.classList.contains('js-mo-item'));
161
+ const pos = (index === undefined || index === null)
162
+ ? Math.floor(Math.random() * (items.length + 1))
163
+ : Math.max(0, Math.min(index, items.length));
164
+
165
+ // Insert into DOM (append when pos is at end)
166
+ if (pos >= items.length) {
167
+ el.appendChild(newEl);
168
+ } else {
169
+ el.insertBefore(newEl, items[pos]);
170
+ }
171
+
172
+ // Initialise the new element
173
+ if (!newEl.id) newEl.id = 'moT' + crypto.randomUUID() + '_' + pos;
174
+ newEl.classList.add('mo-item', 'js-mo-item');
175
+
176
+ // Explicit fullWidth on item data takes priority, then fall back to randomFullWidth
177
+ if (item.fullWidth) {
178
+ newEl.classList.add('mo-fullwidth', 'js-mo-fullwidth');
179
+ } else if (data.randomFullWidth) {
180
+ const prob = data.randomFullWidth === true ? 0.33 : data.randomFullWidth;
181
+ if (Math.random() < prob) newEl.classList.add('mo-fullwidth', 'js-mo-fullwidth');
182
+ }
183
+ if (data.showBadge) this._createBadge(newEl, pos + 1);
184
+ if (data.showArrow) this._createArrow(newEl);
185
+
186
+ // Re-number all counter badges so sequence stays correct after insertion
187
+ if (data.showBadge && data.showCounterStyle === 'counter') {
188
+ Array.from(el.querySelectorAll('.js-mo-item')).forEach((it, i) => {
189
+ const badge = it.querySelector('.js-mo-badge');
190
+ if (badge) badge.textContent = i + 1;
191
+ });
192
+ }
193
+
194
+ data.lastItemIdx = Array.from(el.children).length;
195
+ instanceData.set(el, data);
196
+
197
+ this.refresh();
198
+
199
+ newEl.querySelectorAll('img').forEach((img) => {
200
+ if (!img.complete) img.addEventListener('load', this._resizeHandler, { once: true });
201
+ });
202
+
203
+ return newEl;
204
+ }
205
+
145
206
  destroy() {
146
207
  window.removeEventListener('resize', this._resizeHandler);
147
208
  instanceData.delete(this.element);