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 +34 -7
- package/dist/moTimeline.cjs +3 -3
- package/dist/moTimeline.css +2 -2
- package/dist/moTimeline.js +104 -65
- package/dist/moTimeline.umd.js +3 -3
- package/package.json +1 -1
- package/src/moTimeline.css +19 -1
- package/src/moTimeline.js +83 -2
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
|
|
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();
|
|
150
|
-
tl.initNewItems();
|
|
151
|
-
tl.addItems(items);
|
|
152
|
-
tl.
|
|
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:
|
|
334
|
-
--mo-card-margin-inverted:
|
|
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
|
|
package/dist/moTimeline.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"use strict";var E=Object.defineProperty;var _=(
|
|
2
|
-
* moTimeline v2.
|
|
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,
|
|
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;
|
package/dist/moTimeline.css
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* moTimeline v2.
|
|
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}
|
package/dist/moTimeline.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
var E = Object.defineProperty;
|
|
2
|
-
var _ = (
|
|
3
|
-
var g = (
|
|
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.
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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(
|
|
29
|
+
function b(a, e = 100) {
|
|
27
30
|
let t;
|
|
28
|
-
return (...
|
|
29
|
-
clearTimeout(t), t = setTimeout(() =>
|
|
31
|
+
return (...s) => {
|
|
32
|
+
clearTimeout(t), t = setTimeout(() => a(...s), e);
|
|
30
33
|
};
|
|
31
34
|
}
|
|
32
|
-
function
|
|
33
|
-
return
|
|
34
|
-
o:
|
|
35
|
-
h:
|
|
36
|
-
gppu:
|
|
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
|
|
42
|
+
function v(a, e) {
|
|
40
43
|
const t = [];
|
|
41
|
-
let
|
|
42
|
-
for (;
|
|
43
|
-
(!e ||
|
|
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
|
|
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),
|
|
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
|
-
|
|
62
|
-
const t = e.element,
|
|
63
|
-
|
|
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),
|
|
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[
|
|
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
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
119
|
-
let
|
|
120
|
-
if (
|
|
121
|
-
|
|
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(
|
|
162
|
+
u && Math.abs(l.o - f(u).o) < 40 && (h = 1);
|
|
124
163
|
}
|
|
125
|
-
return { lr:
|
|
164
|
+
return { lr: m, badge_offset: h };
|
|
126
165
|
}
|
|
127
166
|
_createBadge(e, t) {
|
|
128
|
-
const
|
|
129
|
-
if (o.className = "mo-badge js-mo-badge",
|
|
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 (
|
|
132
|
-
const
|
|
133
|
-
|
|
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
|
|
142
|
-
if (
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
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 = "",
|
|
187
|
+
i.className = "mo-avatar", i.src = e.avatar, i.alt = "", r.appendChild(i);
|
|
149
188
|
}
|
|
150
|
-
|
|
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
|
|
155
|
-
|
|
193
|
+
const r = document.createElement("h3");
|
|
194
|
+
r.textContent = e.title, o.appendChild(r);
|
|
156
195
|
}
|
|
157
196
|
if (e.meta) {
|
|
158
|
-
const
|
|
159
|
-
|
|
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
|
|
163
|
-
|
|
201
|
+
const r = document.createElement("p");
|
|
202
|
+
r.textContent = e.text, o.appendChild(r);
|
|
164
203
|
}
|
|
165
|
-
return
|
|
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(
|
|
173
|
-
let y =
|
|
211
|
+
g(d, "instances", /* @__PURE__ */ new Set());
|
|
212
|
+
let y = d;
|
|
174
213
|
export {
|
|
175
214
|
y as MoTimeline,
|
|
176
215
|
y as default
|
package/dist/moTimeline.umd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
(function(a
|
|
2
|
-
* moTimeline v2.
|
|
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
|
|
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
package/src/moTimeline.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* moTimeline v2.
|
|
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.
|
|
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
|
|