motimeline 2.7.5 → 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
 
@@ -107,6 +107,8 @@ import 'motimeline/dist/moTimeline.css';
107
107
  | `avatarSize` | string | `'50px'` | Width and height of the circular avatar image. Sets `--mo-avatar-size` on the container. Any valid CSS length is accepted (e.g. `'40px'`, `'4rem'`). |
108
108
  | `cardMargin` | string | `'0.5rem 1.25rem 0.5rem 0.5rem'` | Margin of left-column themed cards. The larger right value creates space toward the center line. Sets `--mo-card-margin` on the container. |
109
109
  | `cardMarginInverted` | string | `'0.5rem 0.5rem 0.5rem 1.25rem'` | Margin of right-column (inverted) themed cards. The larger left value creates space toward the center line. Sets `--mo-card-margin-inverted` on the container. |
110
+ | `cardMarginFullWidth` | string | `'0.5rem'` | Margin of full-width themed cards. Sets `--mo-card-margin-fullwidth` on the container. |
111
+ | `randomFullWidth` | number \| boolean | `0` | `0`/`false` = off. A number `0–1` sets the probability that each item is randomly promoted to full-width during init. `true` = 33% chance. Items can also be set manually by adding the `mo-fullwidth` class to the `<li>`. |
110
112
 
111
113
  ---
112
114
 
@@ -146,12 +148,28 @@ import 'motimeline/dist/moTimeline.css';
146
148
  ```js
147
149
  const tl = new MoTimeline(elementOrSelector, options);
148
150
 
149
- tl.refresh(); // re-layout all items (called automatically on resize)
150
- tl.initNewItems(); // pick up manually appended <li> elements
151
- tl.addItems(items); // create and append <li> from an array of item objects (or JSON string)
152
- 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
153
156
  ```
154
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
+
155
173
  ### addItems — item schema
156
174
 
157
175
  ```js
@@ -330,8 +348,9 @@ async function fetchPage(page) {
330
348
  --mo-arrow-color: #dde1e7;
331
349
  --mo-card-border-radius: 8px;
332
350
  --mo-avatar-size: 50px;
333
- --mo-card-margin: 0.5rem 1.25rem 0.5rem 0.5rem;
334
- --mo-card-margin-inverted: 0.5rem 0.5rem 0.5rem 1.25rem;
351
+ --mo-card-margin: 0.5rem 1.25rem 0.5rem 0.5rem;
352
+ --mo-card-margin-inverted: 0.5rem 0.5rem 0.5rem 1.25rem;
353
+ --mo-card-margin-fullwidth: 0.5rem;
335
354
  }
336
355
  ```
337
356
 
@@ -361,6 +380,14 @@ No framework option needed. Wrap the `<ul>` inside a Bootstrap `.container`:
361
380
 
362
381
  ## Changelog
363
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
+
386
+ ### v2.8.0
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`
388
+ - New option `randomFullWidth` (number 0–1 or boolean) — randomly promotes items to full-width during init (`true` = 33% probability)
389
+ - New option `cardMarginFullWidth` (string, default `'0.5rem'`) — controls the themed card margin for full-width items
390
+
364
391
  ### v2.7.5
365
392
  - New options `cardMargin` (default `'0.5rem 1.25rem 0.5rem 0.5rem'`) and `cardMarginInverted` (default `'0.5rem 0.5rem 0.5rem 1.25rem'`) — control themed card margins via `--mo-card-margin` and `--mo-card-margin-inverted`
366
393
 
@@ -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.7.5
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 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",cardMargin:"0.5rem 1.25rem 0.5rem 0.5rem",cardMarginInverted:"0.5rem 0.5rem 0.5rem 1.25rem"},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 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),e.style.setProperty("--mo-card-margin",t.cardMargin),e.style.setProperty("--mo-card-margin-inverted",t.cardMarginInverted),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(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),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.style.removeProperty("--mo-card-margin"),this.element.style.removeProperty("--mo-card-margin-inverted"),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,o=Array.from(e.children),r=o.slice(s);r.length!==0&&(r.forEach((a,i)=>{a.id||(a.id="moT"+crypto.randomUUID()+"_"+(i+s)),a.classList.add("mo-item","js-mo-item")}),this._setDivider(),r.forEach((a,i)=>{t.showBadge&&this._createBadge(a,i+s+1),t.showArrow&&this._createArrow(a)}),t.lastItemIdx=o.length,c.set(e,t),this.refresh(),r.forEach(a=>{a.querySelectorAll("img").forEach(i=>{i.complete||i.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 s=t.col,o=y(e,".js-mo-inverted")[0]||null,r=y(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,a=m(r),i=m(o),u=m(e);let h=0,f=0;if(s>1){a.gppu>u.o+1&&(h=1),i.gppu>a.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 a=document.createElement("img");if(a.className="mo-banner",a.src=e.banner,a.alt="",r.appendChild(a),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(l,"instances",new Set);let d=l;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.7.5 — CSS
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: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}
2
+ * moTimeline v2.8.1 — CSS
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,8 +1,8 @@
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.7.5
5
+ * moTimeline v2.8.1
6
6
  * Responsive two-column timeline layout library
7
7
  * https://github.com/MattOpen/moTimeline
8
8
  * MIT License
@@ -17,33 +17,36 @@ const c = /* @__PURE__ */ new WeakMap(), p = {
17
17
  cardBorderRadius: "8px",
18
18
  avatarSize: "50px",
19
19
  cardMargin: "0.5rem 1.25rem 0.5rem 0.5rem",
20
- cardMarginInverted: "0.5rem 0.5rem 0.5rem 1.25rem"
21
- }, C = "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>";
22
- function v() {
23
- const n = window.innerWidth;
24
- return n < 600 ? "xs" : n < 992 ? "sm" : n < 1200 ? "md" : "lg";
20
+ cardMarginInverted: "0.5rem 0.5rem 0.5rem 1.25rem",
21
+ cardMarginFullWidth: "0.5rem",
22
+ randomFullWidth: 0
23
+ // 0 = off; 0–1 = probability per item; true = 0.33
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";
25
28
  }
26
- function b(n, e = 100) {
29
+ function b(a, e = 100) {
27
30
  let t;
28
- return (...r) => {
29
- clearTimeout(t), t = setTimeout(() => n(...r), e);
31
+ return (...s) => {
32
+ clearTimeout(t), t = setTimeout(() => a(...s), e);
30
33
  };
31
34
  }
32
- function m(n) {
33
- return n ? {
34
- o: n.offsetTop,
35
- h: n.offsetHeight,
36
- 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
37
40
  } : { o: 0, h: 0, gppu: 0 };
38
41
  }
39
- function w(n, e) {
42
+ function v(a, e) {
40
43
  const t = [];
41
- let r = n.previousElementSibling;
42
- for (; r; )
43
- (!e || r.matches(e)) && t.push(r), r = r.previousElementSibling;
44
+ let s = a.previousElementSibling;
45
+ for (; s; )
46
+ (!e || s.matches(e)) && t.push(s), s = s.previousElementSibling;
44
47
  return t;
45
48
  }
46
- const l = class l {
49
+ const d = class d {
47
50
  constructor(e, t = {}) {
48
51
  if (typeof e == "string" && (e = document.querySelector(e)), !e) throw new Error("moTimeline: element not found");
49
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();
@@ -55,12 +58,12 @@ const l = class l {
55
58
  return;
56
59
  }
57
60
  const t = Object.assign({}, this.settings, { lastItemIdx: 0 });
58
- 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), e.style.setProperty("--mo-card-margin", t.cardMargin), e.style.setProperty("--mo-card-margin-inverted", t.cardMarginInverted), 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();
59
62
  }
60
63
  refresh() {
61
- l.instances.forEach((e) => {
62
- const t = e.element, r = c.get(t);
63
- r && (r.col = r.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) => {
64
67
  e._setPostPosition(o);
65
68
  }));
66
69
  });
@@ -80,9 +83,33 @@ const l = class l {
80
83
  addItems(e) {
81
84
  typeof e == "string" && (e = JSON.parse(e)), e.forEach((t) => this.element.appendChild(this._createItemElement(t))), this._initItems();
82
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
+ }
83
110
  destroy() {
84
- 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.style.removeProperty("--mo-card-margin"), this.element.style.removeProperty("--mo-card-margin-inverted"), this.element.classList.remove("mo-timeline", "mo-theme", "mo-twocol"), Array.from(this.element.children).forEach((e) => {
85
- 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());
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) => {
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());
86
113
  });
87
114
  }
88
115
  // ─── Private ────────────────────────────────────────────────────────────────
@@ -91,23 +118,35 @@ const l = class l {
91
118
  }
92
119
  _setDivider() {
93
120
  const e = this._getData();
94
- 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));
95
122
  }
96
123
  _initItems() {
97
124
  const e = this.element, t = this._getData();
98
125
  if (!t) return;
99
- const r = t.lastItemIdx, o = Array.from(e.children), s = o.slice(r);
100
- s.length !== 0 && (s.forEach((a, i) => {
101
- a.id || (a.id = "moT" + crypto.randomUUID() + "_" + (i + r)), a.classList.add("mo-item", "js-mo-item");
102
- }), this._setDivider(), s.forEach((a, i) => {
103
- t.showBadge && this._createBadge(a, i + r + 1), t.showArrow && this._createArrow(a);
104
- }), t.lastItemIdx = o.length, c.set(e, t), this.refresh(), s.forEach((a) => {
105
- a.querySelectorAll("img").forEach((i) => {
106
- i.complete || i.addEventListener("load", this._resizeHandler, { once: !0 });
126
+ const s = t.lastItemIdx, o = Array.from(e.children), r = o.slice(s);
127
+ if (r.length !== 0) {
128
+ if (r.forEach((n, i) => {
129
+ n.id || (n.id = "moT" + crypto.randomUUID() + "_" + (i + s)), n.classList.add("mo-item", "js-mo-item");
130
+ }), this._setDivider(), t.randomFullWidth) {
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");
134
+ });
135
+ }
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 });
141
+ });
107
142
  });
108
- }));
143
+ }
109
144
  }
110
145
  _setPostPosition(e) {
146
+ if (e.classList.contains("mo-fullwidth")) {
147
+ e.classList.remove("mo-inverted", "js-mo-inverted", "mo-offset");
148
+ return;
149
+ }
111
150
  const t = this._getLeftOrRight(e);
112
151
  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));
113
152
  }
@@ -115,22 +154,22 @@ const l = class l {
115
154
  if (!e) return null;
116
155
  const t = this._getData();
117
156
  if (!t) return null;
118
- const r = t.col, o = w(e, ".js-mo-inverted")[0] || null, s = w(e, ".js-mo-item:not(.js-mo-inverted)")[0] || null, a = m(s), i = m(o), h = m(e);
119
- let d = 0, f = 0;
120
- if (r > 1) {
121
- a.gppu > h.o + 1 && (d = 1), i.gppu > a.gppu && (d = 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;
159
+ if (s > 1) {
160
+ n.gppu > l.o + 1 && (m = 1), i.gppu > n.gppu && (m = 0);
122
161
  const u = e.previousElementSibling;
123
- u && Math.abs(h.o - m(u).o) < 40 && (f = 1);
162
+ u && Math.abs(l.o - f(u).o) < 40 && (h = 1);
124
163
  }
125
- return { lr: d, badge_offset: f };
164
+ return { lr: m, badge_offset: h };
126
165
  }
127
166
  _createBadge(e, t) {
128
- const r = this._getData(), o = document.createElement("span");
129
- if (o.className = "mo-badge js-mo-badge", r.showCounterStyle === "none")
167
+ const s = this._getData(), o = document.createElement("span");
168
+ if (o.className = "mo-badge js-mo-badge", s.showCounterStyle === "none")
130
169
  o.style.opacity = "0";
131
- else if (r.showCounterStyle === "image") {
132
- const s = document.createElement("img");
133
- s.className = "mo-badge-icon", s.alt = "", s.src = e.dataset.moIcon || C, o.appendChild(s);
170
+ else if (s.showCounterStyle === "image") {
171
+ const r = document.createElement("img");
172
+ r.className = "mo-badge-icon", r.alt = "", r.src = e.dataset.moIcon || I, o.appendChild(r);
134
173
  } else
135
174
  o.textContent = t;
136
175
  e.prepend(o);
@@ -138,39 +177,39 @@ const l = class l {
138
177
  _createItemElement(e) {
139
178
  const t = document.createElement("li");
140
179
  e.icon && (t.dataset.moIcon = e.icon);
141
- const r = document.createElement("div");
142
- if (r.className = "mo-card", e.banner) {
143
- const s = document.createElement("div");
144
- s.className = "mo-card-image";
145
- const a = document.createElement("img");
146
- if (a.className = "mo-banner", a.src = e.banner, a.alt = "", s.appendChild(a), e.avatar) {
180
+ const s = document.createElement("div");
181
+ if (s.className = "mo-card", e.banner) {
182
+ const r = document.createElement("div");
183
+ r.className = "mo-card-image";
184
+ const n = document.createElement("img");
185
+ if (n.className = "mo-banner", n.src = e.banner, n.alt = "", r.appendChild(n), e.avatar) {
147
186
  const i = document.createElement("img");
148
- i.className = "mo-avatar", i.src = e.avatar, i.alt = "", s.appendChild(i);
187
+ i.className = "mo-avatar", i.src = e.avatar, i.alt = "", r.appendChild(i);
149
188
  }
150
- r.appendChild(s);
189
+ s.appendChild(r);
151
190
  }
152
191
  const o = document.createElement("div");
153
192
  if (o.className = "mo-card-body", e.title) {
154
- const s = document.createElement("h3");
155
- s.textContent = e.title, o.appendChild(s);
193
+ const r = document.createElement("h3");
194
+ r.textContent = e.title, o.appendChild(r);
156
195
  }
157
196
  if (e.meta) {
158
- const s = document.createElement("p");
159
- s.className = "mo-meta", s.textContent = e.meta, o.appendChild(s);
197
+ const r = document.createElement("p");
198
+ r.className = "mo-meta", r.textContent = e.meta, o.appendChild(r);
160
199
  }
161
200
  if (e.text) {
162
- const s = document.createElement("p");
163
- s.textContent = e.text, o.appendChild(s);
201
+ const r = document.createElement("p");
202
+ r.textContent = e.text, o.appendChild(r);
164
203
  }
165
- return r.appendChild(o), t.appendChild(r), t;
204
+ return s.appendChild(o), t.appendChild(s), t;
166
205
  }
167
206
  _createArrow(e) {
168
207
  const t = document.createElement("span");
169
208
  t.className = "mo-arrow js-mo-arrow", e.prepend(t);
170
209
  }
171
210
  };
172
- g(l, "instances", /* @__PURE__ */ new Set());
173
- let y = l;
211
+ g(d, "instances", /* @__PURE__ */ new Set());
212
+ let y = d;
174
213
  export {
175
214
  y as MoTimeline,
176
215
  y as default
@@ -1,6 +1,6 @@
1
- (function(a,r){typeof exports=="object"&&typeof module<"u"?r(exports):typeof define=="function"&&define.amd?define(["exports"],r):(a=typeof globalThis<"u"?globalThis:a||self,r(a.MoTimeline={}))})(this,function(a){"use strict";var C=Object.defineProperty;var I=(a,r,m)=>r in a?C(a,r,{enumerable:!0,configurable:!0,writable:!0,value:m}):a[r]=m;var E=(a,r,m)=>I(a,typeof r!="symbol"?r+"":r,m);/*!
2
- * moTimeline v2.7.5
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 r=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"},_="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(...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 p(c,e){const t=[];let n=c.previousElementSibling;for(;n;)(!e||n.matches(e))&&t.push(n),n=n.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(r.has(e)){this.refresh();return}const t=Object.assign({},this.settings,{lastItemIdx:0});r.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),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,n=r.get(t);n&&(n.col=n.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),r.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.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 r.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 n=t.lastItemIdx,o=Array.from(e.children),s=o.slice(n);s.length!==0&&(s.forEach((i,l)=>{i.id||(i.id="moT"+crypto.randomUUID()+"_"+(l+n)),i.classList.add("mo-item","js-mo-item")}),this._setDivider(),s.forEach((i,l)=>{t.showBadge&&this._createBadge(i,l+n+1),t.showArrow&&this._createArrow(i)}),t.lastItemIdx=o.length,r.set(e,t),this.refresh(),s.forEach(i=>{i.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,o=p(e,".js-mo-inverted")[0]||null,s=p(e,".js-mo-item:not(.js-mo-inverted)")[0]||null,i=h(s),l=h(o),v=h(e);let u=0,y=0;if(n>1){i.gppu>v.o+1&&(u=1),l.gppu>i.gppu&&(u=0);const w=e.previousElementSibling;w&&Math.abs(v.o-h(w).o)<40&&(y=1)}return{lr:u,badge_offset:y}}_createBadge(e,t){const n=this._getData(),o=document.createElement("span");if(o.className="mo-badge js-mo-badge",n.showCounterStyle==="none")o.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||_,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 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 l=document.createElement("img");l.className="mo-avatar",l.src=e.avatar,l.alt="",s.appendChild(l)}n.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 n.appendChild(o),t.appendChild(n),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 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.7.5",
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.7.5 — CSS
2
+ * moTimeline v2.8.1 — CSS
3
3
  */
4
4
 
5
5
  /* ── CSS custom properties (easy override) ─────────────────── */
@@ -64,6 +64,24 @@
64
64
  float: right;
65
65
  }
66
66
 
67
+ /* Full-width item — spans both columns (two-column mode only) */
68
+ .mo-timeline.mo-twocol > .mo-item.mo-fullwidth {
69
+ clear: both;
70
+ float: none;
71
+ width: 100%;
72
+ }
73
+
74
+ /* Full-width card: equal margin on both sides (no center-line gap) */
75
+ .mo-theme > .mo-item.mo-fullwidth .mo-card {
76
+ margin: var(--mo-card-margin-fullwidth, 0.5rem);
77
+ }
78
+
79
+ /* Hide badge & arrow for full-width items — no center-line interaction */
80
+ .mo-timeline.mo-twocol > .mo-item.mo-fullwidth .mo-badge,
81
+ .mo-timeline.mo-twocol > .mo-item.mo-fullwidth .mo-arrow {
82
+ display: none;
83
+ }
84
+
67
85
  /* ── Badge — circle sitting ON the center line ──────────────── */
68
86
 
69
87
  .mo-badge {
package/src/moTimeline.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * moTimeline v2.7.5
2
+ * moTimeline v2.8.1
3
3
  * Responsive two-column timeline layout library
4
4
  * https://github.com/MattOpen/moTimeline
5
5
  * MIT License
@@ -20,6 +20,8 @@ const DEFAULTS = {
20
20
  avatarSize: '50px',
21
21
  cardMargin: '0.5rem 1.25rem 0.5rem 0.5rem',
22
22
  cardMarginInverted: '0.5rem 0.5rem 0.5rem 1.25rem',
23
+ cardMarginFullWidth: '0.5rem',
24
+ randomFullWidth: 0, // 0 = off; 0–1 = probability per item; true = 0.33
23
25
  };
24
26
 
25
27
  const DEFAULT_BADGE_ICON = "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>";
@@ -96,6 +98,7 @@ export class MoTimeline {
96
98
  el.style.setProperty('--mo-avatar-size', data.avatarSize);
97
99
  el.style.setProperty('--mo-card-margin', data.cardMargin);
98
100
  el.style.setProperty('--mo-card-margin-inverted', data.cardMarginInverted);
101
+ el.style.setProperty('--mo-card-margin-fullwidth', data.cardMarginFullWidth);
99
102
 
100
103
  this._initialized = true;
101
104
  window.addEventListener('resize', this._resizeHandler);
@@ -139,6 +142,67 @@ export class MoTimeline {
139
142
  this._initItems();
140
143
  }
141
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
+
142
206
  destroy() {
143
207
  window.removeEventListener('resize', this._resizeHandler);
144
208
  instanceData.delete(this.element);
@@ -147,9 +211,10 @@ export class MoTimeline {
147
211
  this.element.style.removeProperty('--mo-avatar-size');
148
212
  this.element.style.removeProperty('--mo-card-margin');
149
213
  this.element.style.removeProperty('--mo-card-margin-inverted');
214
+ this.element.style.removeProperty('--mo-card-margin-fullwidth');
150
215
  this.element.classList.remove('mo-timeline', 'mo-theme', 'mo-twocol');
151
216
  Array.from(this.element.children).forEach((child) => {
152
- child.classList.remove('mo-item', 'js-mo-item', 'mo-inverted', 'js-mo-inverted', 'mo-offset');
217
+ child.classList.remove('mo-item', 'js-mo-item', 'mo-inverted', 'js-mo-inverted', 'mo-offset', 'mo-fullwidth', 'js-mo-fullwidth');
153
218
  child.querySelectorAll('.js-mo-badge, .js-mo-arrow').forEach((b) => b.remove());
154
219
  });
155
220
  }
@@ -188,6 +253,16 @@ export class MoTimeline {
188
253
 
189
254
  this._setDivider();
190
255
 
256
+ // Random full-width assignment
257
+ if (data.randomFullWidth) {
258
+ const prob = data.randomFullWidth === true ? 0.33 : data.randomFullWidth;
259
+ newItems.forEach((item) => {
260
+ if (!item.classList.contains('mo-fullwidth') && Math.random() < prob) {
261
+ item.classList.add('mo-fullwidth', 'js-mo-fullwidth');
262
+ }
263
+ });
264
+ }
265
+
191
266
  // Badges / arrows
192
267
  newItems.forEach((item, i) => {
193
268
  if (data.showBadge) {
@@ -215,6 +290,12 @@ export class MoTimeline {
215
290
  }
216
291
 
217
292
  _setPostPosition(el) {
293
+ // Full-width items span both columns — skip column assignment
294
+ if (el.classList.contains('mo-fullwidth')) {
295
+ el.classList.remove('mo-inverted', 'js-mo-inverted', 'mo-offset');
296
+ return;
297
+ }
298
+
218
299
  const result = this._getLeftOrRight(el);
219
300
  if (!result) return;
220
301