motimeline 2.8.0 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -5
- package/dist/moTimeline.cjs +3 -3
- package/dist/moTimeline.css +1 -1
- package/dist/moTimeline.js +73 -49
- package/dist/moTimeline.umd.js +3 -3
- package/package.json +1 -1
- package/src/moTimeline.css +1 -1
- package/src/moTimeline.js +62 -1
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
|
|
|
@@ -148,12 +148,28 @@ import 'motimeline/dist/moTimeline.css';
|
|
|
148
148
|
```js
|
|
149
149
|
const tl = new MoTimeline(elementOrSelector, options);
|
|
150
150
|
|
|
151
|
-
tl.refresh();
|
|
152
|
-
tl.initNewItems();
|
|
153
|
-
tl.addItems(items);
|
|
154
|
-
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
|
|
155
156
|
```
|
|
156
157
|
|
|
158
|
+
### insertItem
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
// Insert at a specific 0-based index:
|
|
162
|
+
tl.insertItem({ title: 'Breaking news', meta: 'Now', text: '...' }, 2);
|
|
163
|
+
|
|
164
|
+
// Insert at a random position (omit the index):
|
|
165
|
+
tl.insertItem({ title: 'Surprise!', meta: 'Now', text: '...' });
|
|
166
|
+
|
|
167
|
+
// Insert as a full-width item:
|
|
168
|
+
tl.insertItem({ title: 'Featured', meta: 'Now', text: '...', fullWidth: true }, 0);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Badge numbers are automatically re-sequenced after insertion. Returns the inserted `<li>` element.
|
|
172
|
+
|
|
157
173
|
### addItems — item schema
|
|
158
174
|
|
|
159
175
|
```js
|
|
@@ -364,6 +380,9 @@ No framework option needed. Wrap the `<ul>` inside a Bootstrap `.container`:
|
|
|
364
380
|
|
|
365
381
|
## Changelog
|
|
366
382
|
|
|
383
|
+
### v2.8.1
|
|
384
|
+
- New method `insertItem(item, index)` — inserts a single item at a specific 0-based index, or at a random position when index is omitted. Badge numbers are re-sequenced automatically.
|
|
385
|
+
|
|
367
386
|
### v2.8.0
|
|
368
387
|
- New: full-width items — add `mo-fullwidth` class to any `<li>` to make it span both columns (two-column mode only). Badge and arrow are hidden automatically; card margin collapses to equal sides via `--mo-card-margin-fullwidth`
|
|
369
388
|
- New option `randomFullWidth` (number 0–1 or boolean) — randomly promotes items to full-width during init (`true` = 33% probability)
|
package/dist/moTimeline.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"use strict";var E=Object.defineProperty;var _=(
|
|
2
|
-
* moTimeline v2.8.
|
|
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
|
|
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.8.
|
|
2
|
+
* moTimeline v2.8.1 — CSS
|
|
3
3
|
*/:root{--mo-line-color: #dde1e7;--mo-badge-bg: #4f46e5;--mo-badge-color: #fff;--mo-badge-size: 26px;--mo-badge-font-size: 12px;--mo-arrow-color: #dde1e7}.mo-timeline{display:block;list-style:none;margin:0;padding:0;position:relative;width:100%}.mo-timeline:after{content:"";display:table;clear:both}.mo-timeline.mo-twocol:before{background-color:var(--mo-line-color);bottom:0;content:"";left:50%;margin-left:-1.5px;position:absolute;top:0;width:3px;z-index:0}.mo-timeline>.mo-item{box-sizing:border-box;display:block;float:left;position:relative;width:50%}.mo-timeline:not(.mo-twocol)>.mo-item{float:none;width:100%}.mo-timeline>.mo-item.mo-inverted{float:right}.mo-timeline.mo-twocol>.mo-item.mo-fullwidth{clear:both;float:none;width:100%}.mo-theme>.mo-item.mo-fullwidth .mo-card{margin:var(--mo-card-margin-fullwidth, .5rem)}.mo-timeline.mo-twocol>.mo-item.mo-fullwidth .mo-badge,.mo-timeline.mo-twocol>.mo-item.mo-fullwidth .mo-arrow{display:none}.mo-badge{align-items:center;background:var(--mo-badge-bg);border-radius:50%;color:var(--mo-badge-color);display:flex;font-size:var(--mo-badge-font-size);font-weight:700;height:var(--mo-badge-size);justify-content:center;min-width:var(--mo-badge-size);overflow:hidden;position:absolute;top:18px;z-index:2}.mo-badge .mo-badge-icon{border-radius:50%;height:100%;object-fit:cover;width:100%}.mo-timeline.mo-twocol>.mo-item:not(.mo-inverted) .mo-badge{left:auto;right:calc(var(--mo-badge-size) / -2)}.mo-timeline.mo-twocol>.mo-item.mo-inverted .mo-badge{left:calc(var(--mo-badge-size) / -2);right:auto}.mo-timeline.mo-twocol>.mo-item.mo-offset .mo-badge{top:calc(18px + var(--mo-badge-size) + 10px)}.mo-timeline.mo-twocol>.mo-item.mo-offset .mo-arrow{top:calc(26px + var(--mo-badge-size) + 10px)}.mo-timeline:not(.mo-twocol)>.mo-item .mo-badge{display:none}.mo-arrow{border:8px solid transparent;display:block;height:0;position:absolute;top:26px;width:0;z-index:1}.mo-timeline.mo-twocol>.mo-item:not(.mo-inverted) .mo-arrow{border-left:8px solid var(--mo-arrow-color);border-right:none;left:auto;right:0}.mo-timeline.mo-twocol>.mo-item.mo-inverted .mo-arrow{border-right:8px solid var(--mo-arrow-color);border-left:none;left:0;right:auto}.mo-timeline:not(.mo-twocol)>.mo-item .mo-arrow{display:none}.mo-theme{--mo-arrow-color: #fff}.mo-theme>.mo-item .mo-card{background:#fff;border-radius:var(--mo-card-border-radius, 8px);box-shadow:0 2px 14px #0000001a;margin:var(--mo-card-margin, .5rem 1.25rem .5rem .5rem);position:relative}.mo-theme>.mo-item.mo-inverted .mo-card{margin:var(--mo-card-margin-inverted, .5rem .5rem .5rem 1.25rem)}.mo-theme>.mo-item .mo-banner{border-radius:var(--mo-card-border-radius, 8px) var(--mo-card-border-radius, 8px) 0 0;display:block;max-height:240px;object-fit:cover;width:100%}.mo-theme>.mo-item .mo-card-image{overflow:visible;position:relative}.mo-theme>.mo-item .mo-avatar{border:3px solid #fff;border-radius:50%;bottom:calc(var(--mo-avatar-size, 50px) * -.44);box-shadow:0 2px 8px #0000002e;height:var(--mo-avatar-size, 50px);object-fit:cover;position:absolute;right:14px;width:var(--mo-avatar-size, 50px);z-index:1}.mo-theme>.mo-item .mo-card-body{padding:1.75rem 1rem 1rem}.mo-theme>.mo-item .mo-card-body h3{font-size:1rem;font-weight:700;margin:0 0 .3rem}.mo-theme>.mo-item .mo-card-body .mo-meta{color:#9ca3af;font-size:.75rem;margin-bottom:.5rem}.mo-theme>.mo-item .mo-card-body p{color:#6b7280;font-size:.875rem;line-height:1.55;margin:0}.mo-theme.mo-twocol>.mo-item:not(.mo-inverted) .mo-arrow{border-left-color:var(--mo-arrow-color);right:12px}.mo-theme.mo-twocol>.mo-item.mo-inverted .mo-arrow{border-right-color:var(--mo-arrow-color);left:12px}.mo-theme .mo-badge{background:#fff;border:2px solid var(--mo-line-color);box-shadow:0 2px 6px #0000001a;color:#374151}
|
package/dist/moTimeline.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
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.8.
|
|
5
|
+
* moTimeline v2.8.1
|
|
6
6
|
* Responsive two-column timeline layout library
|
|
7
7
|
* https://github.com/MattOpen/moTimeline
|
|
8
8
|
* MIT License
|
|
9
9
|
*/
|
|
10
|
-
const
|
|
10
|
+
const c = /* @__PURE__ */ new WeakMap(), p = {
|
|
11
11
|
columnCount: { xs: 1, sm: 2, md: 2, lg: 2 },
|
|
12
12
|
showBadge: !1,
|
|
13
13
|
showArrow: !1,
|
|
@@ -21,49 +21,49 @@ const l = /* @__PURE__ */ new WeakMap(), p = {
|
|
|
21
21
|
cardMarginFullWidth: "0.5rem",
|
|
22
22
|
randomFullWidth: 0
|
|
23
23
|
// 0 = off; 0–1 = probability per item; true = 0.33
|
|
24
|
-
},
|
|
25
|
-
function
|
|
26
|
-
const
|
|
27
|
-
return
|
|
24
|
+
}, I = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='11' fill='%234f46e5'/><circle cx='12' cy='12' r='4.5' fill='white'/></svg>";
|
|
25
|
+
function w() {
|
|
26
|
+
const a = window.innerWidth;
|
|
27
|
+
return a < 600 ? "xs" : a < 992 ? "sm" : a < 1200 ? "md" : "lg";
|
|
28
28
|
}
|
|
29
|
-
function
|
|
29
|
+
function b(a, e = 100) {
|
|
30
30
|
let t;
|
|
31
31
|
return (...s) => {
|
|
32
|
-
clearTimeout(t), t = setTimeout(() =>
|
|
32
|
+
clearTimeout(t), t = setTimeout(() => a(...s), e);
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
function
|
|
36
|
-
return
|
|
37
|
-
o:
|
|
38
|
-
h:
|
|
39
|
-
gppu:
|
|
35
|
+
function f(a) {
|
|
36
|
+
return a ? {
|
|
37
|
+
o: a.offsetTop,
|
|
38
|
+
h: a.offsetHeight,
|
|
39
|
+
gppu: a.offsetTop + a.offsetHeight
|
|
40
40
|
} : { o: 0, h: 0, gppu: 0 };
|
|
41
41
|
}
|
|
42
|
-
function
|
|
42
|
+
function v(a, e) {
|
|
43
43
|
const t = [];
|
|
44
|
-
let s =
|
|
44
|
+
let s = a.previousElementSibling;
|
|
45
45
|
for (; s; )
|
|
46
46
|
(!e || s.matches(e)) && t.push(s), s = s.previousElementSibling;
|
|
47
47
|
return t;
|
|
48
48
|
}
|
|
49
|
-
const
|
|
49
|
+
const d = class d {
|
|
50
50
|
constructor(e, t = {}) {
|
|
51
51
|
if (typeof e == "string" && (e = document.querySelector(e)), !e) throw new Error("moTimeline: element not found");
|
|
52
|
-
this.element = e, this.settings = Object.assign({}, p, t), this.settings.columnCount = Object.assign({}, p.columnCount, t.columnCount), this._resizeHandler =
|
|
52
|
+
this.element = e, this.settings = Object.assign({}, p, t), this.settings.columnCount = Object.assign({}, p.columnCount, t.columnCount), this._resizeHandler = b(() => this.refresh(), 100), this._initialized = !1, this.init();
|
|
53
53
|
}
|
|
54
54
|
init() {
|
|
55
55
|
const e = this.element;
|
|
56
|
-
if (
|
|
56
|
+
if (c.has(e)) {
|
|
57
57
|
this.refresh();
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
const t = Object.assign({}, this.settings, { lastItemIdx: 0 });
|
|
61
|
-
|
|
61
|
+
c.set(e, t), d.instances.add(this), e.classList.add("mo-timeline"), t.theme && e.classList.add("mo-theme"), e.style.setProperty("--mo-card-border-radius", t.cardBorderRadius), e.style.setProperty("--mo-avatar-size", t.avatarSize), e.style.setProperty("--mo-card-margin", t.cardMargin), e.style.setProperty("--mo-card-margin-inverted", t.cardMarginInverted), e.style.setProperty("--mo-card-margin-fullwidth", t.cardMarginFullWidth), this._initialized = !0, window.addEventListener("resize", this._resizeHandler), Array.from(e.children).length > 0 && this._initItems();
|
|
62
62
|
}
|
|
63
63
|
refresh() {
|
|
64
|
-
|
|
65
|
-
const t = e.element, s =
|
|
66
|
-
s && (s.col = s.columnCount[
|
|
64
|
+
d.instances.forEach((e) => {
|
|
65
|
+
const t = e.element, s = c.get(t);
|
|
66
|
+
s && (s.col = s.columnCount[w()], e._setDivider(), Array.from(t.children).forEach((o) => {
|
|
67
67
|
e._setPostPosition(o);
|
|
68
68
|
}));
|
|
69
69
|
});
|
|
@@ -83,37 +83,61 @@ const c = class c {
|
|
|
83
83
|
addItems(e) {
|
|
84
84
|
typeof e == "string" && (e = JSON.parse(e)), e.forEach((t) => this.element.appendChild(this._createItemElement(t))), this._initItems();
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Insert a single item at a specific position or at a random position.
|
|
88
|
+
*
|
|
89
|
+
* @param {Object} item — same shape as addItems(): { title, meta, text, banner, avatar, icon }
|
|
90
|
+
* @param {number} [index] — 0-based insertion index. Omit (or pass undefined) for a random position.
|
|
91
|
+
* @returns {HTMLElement} the inserted <li> element
|
|
92
|
+
*/
|
|
93
|
+
insertItem(e, t) {
|
|
94
|
+
const s = this.element, o = this._getData();
|
|
95
|
+
if (!o) return;
|
|
96
|
+
const r = this._createItemElement(e), n = Array.from(s.children).filter((l) => l.classList.contains("js-mo-item")), i = t == null ? Math.floor(Math.random() * (n.length + 1)) : Math.max(0, Math.min(t, n.length));
|
|
97
|
+
if (i >= n.length ? s.appendChild(r) : s.insertBefore(r, n[i]), r.id || (r.id = "moT" + crypto.randomUUID() + "_" + i), r.classList.add("mo-item", "js-mo-item"), e.fullWidth)
|
|
98
|
+
r.classList.add("mo-fullwidth", "js-mo-fullwidth");
|
|
99
|
+
else if (o.randomFullWidth) {
|
|
100
|
+
const l = o.randomFullWidth === !0 ? 0.33 : o.randomFullWidth;
|
|
101
|
+
Math.random() < l && r.classList.add("mo-fullwidth", "js-mo-fullwidth");
|
|
102
|
+
}
|
|
103
|
+
return o.showBadge && this._createBadge(r, i + 1), o.showArrow && this._createArrow(r), o.showBadge && o.showCounterStyle === "counter" && Array.from(s.querySelectorAll(".js-mo-item")).forEach((l, m) => {
|
|
104
|
+
const h = l.querySelector(".js-mo-badge");
|
|
105
|
+
h && (h.textContent = m + 1);
|
|
106
|
+
}), o.lastItemIdx = Array.from(s.children).length, c.set(s, o), this.refresh(), r.querySelectorAll("img").forEach((l) => {
|
|
107
|
+
l.complete || l.addEventListener("load", this._resizeHandler, { once: !0 });
|
|
108
|
+
}), r;
|
|
109
|
+
}
|
|
86
110
|
destroy() {
|
|
87
|
-
window.removeEventListener("resize", this._resizeHandler),
|
|
111
|
+
window.removeEventListener("resize", this._resizeHandler), c.delete(this.element), d.instances.delete(this), this.element.style.removeProperty("--mo-card-border-radius"), this.element.style.removeProperty("--mo-avatar-size"), this.element.style.removeProperty("--mo-card-margin"), this.element.style.removeProperty("--mo-card-margin-inverted"), this.element.style.removeProperty("--mo-card-margin-fullwidth"), this.element.classList.remove("mo-timeline", "mo-theme", "mo-twocol"), Array.from(this.element.children).forEach((e) => {
|
|
88
112
|
e.classList.remove("mo-item", "js-mo-item", "mo-inverted", "js-mo-inverted", "mo-offset", "mo-fullwidth", "js-mo-fullwidth"), e.querySelectorAll(".js-mo-badge, .js-mo-arrow").forEach((t) => t.remove());
|
|
89
113
|
});
|
|
90
114
|
}
|
|
91
115
|
// ─── Private ────────────────────────────────────────────────────────────────
|
|
92
116
|
_getData() {
|
|
93
|
-
return
|
|
117
|
+
return c.get(this.element);
|
|
94
118
|
}
|
|
95
119
|
_setDivider() {
|
|
96
120
|
const e = this._getData();
|
|
97
|
-
e && (e.col = e.columnCount[
|
|
121
|
+
e && (e.col = e.columnCount[w()], this.element.classList.toggle("mo-twocol", e.col > 1));
|
|
98
122
|
}
|
|
99
123
|
_initItems() {
|
|
100
124
|
const e = this.element, t = this._getData();
|
|
101
125
|
if (!t) return;
|
|
102
126
|
const s = t.lastItemIdx, o = Array.from(e.children), r = o.slice(s);
|
|
103
127
|
if (r.length !== 0) {
|
|
104
|
-
if (r.forEach((
|
|
105
|
-
|
|
128
|
+
if (r.forEach((n, i) => {
|
|
129
|
+
n.id || (n.id = "moT" + crypto.randomUUID() + "_" + (i + s)), n.classList.add("mo-item", "js-mo-item");
|
|
106
130
|
}), this._setDivider(), t.randomFullWidth) {
|
|
107
|
-
const
|
|
108
|
-
r.forEach((
|
|
109
|
-
!
|
|
131
|
+
const n = t.randomFullWidth === !0 ? 0.33 : t.randomFullWidth;
|
|
132
|
+
r.forEach((i) => {
|
|
133
|
+
!i.classList.contains("mo-fullwidth") && Math.random() < n && i.classList.add("mo-fullwidth", "js-mo-fullwidth");
|
|
110
134
|
});
|
|
111
135
|
}
|
|
112
|
-
r.forEach((
|
|
113
|
-
t.showBadge && this._createBadge(
|
|
114
|
-
}), t.lastItemIdx = o.length,
|
|
115
|
-
|
|
116
|
-
|
|
136
|
+
r.forEach((n, i) => {
|
|
137
|
+
t.showBadge && this._createBadge(n, i + s + 1), t.showArrow && this._createArrow(n);
|
|
138
|
+
}), t.lastItemIdx = o.length, c.set(e, t), this.refresh(), r.forEach((n) => {
|
|
139
|
+
n.querySelectorAll("img").forEach((i) => {
|
|
140
|
+
i.complete || i.addEventListener("load", this._resizeHandler, { once: !0 });
|
|
117
141
|
});
|
|
118
142
|
});
|
|
119
143
|
}
|
|
@@ -130,14 +154,14 @@ const c = class c {
|
|
|
130
154
|
if (!e) return null;
|
|
131
155
|
const t = this._getData();
|
|
132
156
|
if (!t) return null;
|
|
133
|
-
const s = t.col, o =
|
|
134
|
-
let
|
|
157
|
+
const s = t.col, o = v(e, ".js-mo-inverted")[0] || null, r = v(e, ".js-mo-item:not(.js-mo-inverted)")[0] || null, n = f(r), i = f(o), l = f(e);
|
|
158
|
+
let m = 0, h = 0;
|
|
135
159
|
if (s > 1) {
|
|
136
|
-
|
|
160
|
+
n.gppu > l.o + 1 && (m = 1), i.gppu > n.gppu && (m = 0);
|
|
137
161
|
const u = e.previousElementSibling;
|
|
138
|
-
u && Math.abs(
|
|
162
|
+
u && Math.abs(l.o - f(u).o) < 40 && (h = 1);
|
|
139
163
|
}
|
|
140
|
-
return { lr:
|
|
164
|
+
return { lr: m, badge_offset: h };
|
|
141
165
|
}
|
|
142
166
|
_createBadge(e, t) {
|
|
143
167
|
const s = this._getData(), o = document.createElement("span");
|
|
@@ -145,7 +169,7 @@ const c = class c {
|
|
|
145
169
|
o.style.opacity = "0";
|
|
146
170
|
else if (s.showCounterStyle === "image") {
|
|
147
171
|
const r = document.createElement("img");
|
|
148
|
-
r.className = "mo-badge-icon", r.alt = "", r.src = e.dataset.moIcon ||
|
|
172
|
+
r.className = "mo-badge-icon", r.alt = "", r.src = e.dataset.moIcon || I, o.appendChild(r);
|
|
149
173
|
} else
|
|
150
174
|
o.textContent = t;
|
|
151
175
|
e.prepend(o);
|
|
@@ -157,10 +181,10 @@ const c = class c {
|
|
|
157
181
|
if (s.className = "mo-card", e.banner) {
|
|
158
182
|
const r = document.createElement("div");
|
|
159
183
|
r.className = "mo-card-image";
|
|
160
|
-
const
|
|
161
|
-
if (
|
|
162
|
-
const
|
|
163
|
-
|
|
184
|
+
const n = document.createElement("img");
|
|
185
|
+
if (n.className = "mo-banner", n.src = e.banner, n.alt = "", r.appendChild(n), e.avatar) {
|
|
186
|
+
const i = document.createElement("img");
|
|
187
|
+
i.className = "mo-avatar", i.src = e.avatar, i.alt = "", r.appendChild(i);
|
|
164
188
|
}
|
|
165
189
|
s.appendChild(r);
|
|
166
190
|
}
|
|
@@ -184,8 +208,8 @@ const c = class c {
|
|
|
184
208
|
t.className = "mo-arrow js-mo-arrow", e.prepend(t);
|
|
185
209
|
}
|
|
186
210
|
};
|
|
187
|
-
g(
|
|
188
|
-
let y =
|
|
211
|
+
g(d, "instances", /* @__PURE__ */ new Set());
|
|
212
|
+
let y = d;
|
|
189
213
|
export {
|
|
190
214
|
y as MoTimeline,
|
|
191
215
|
y as default
|
package/dist/moTimeline.umd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
(function(l,
|
|
2
|
-
* moTimeline v2.8.
|
|
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
package/src/moTimeline.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* moTimeline v2.8.
|
|
2
|
+
* moTimeline v2.8.1
|
|
3
3
|
* Responsive two-column timeline layout library
|
|
4
4
|
* https://github.com/MattOpen/moTimeline
|
|
5
5
|
* MIT License
|
|
@@ -142,6 +142,67 @@ export class MoTimeline {
|
|
|
142
142
|
this._initItems();
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Insert a single item at a specific position or at a random position.
|
|
147
|
+
*
|
|
148
|
+
* @param {Object} item — same shape as addItems(): { title, meta, text, banner, avatar, icon }
|
|
149
|
+
* @param {number} [index] — 0-based insertion index. Omit (or pass undefined) for a random position.
|
|
150
|
+
* @returns {HTMLElement} the inserted <li> element
|
|
151
|
+
*/
|
|
152
|
+
insertItem(item, index) {
|
|
153
|
+
const el = this.element;
|
|
154
|
+
const data = this._getData();
|
|
155
|
+
if (!data) return;
|
|
156
|
+
|
|
157
|
+
const newEl = this._createItemElement(item);
|
|
158
|
+
|
|
159
|
+
// All currently initialised items (to determine valid range)
|
|
160
|
+
const items = Array.from(el.children).filter((c) => c.classList.contains('js-mo-item'));
|
|
161
|
+
const pos = (index === undefined || index === null)
|
|
162
|
+
? Math.floor(Math.random() * (items.length + 1))
|
|
163
|
+
: Math.max(0, Math.min(index, items.length));
|
|
164
|
+
|
|
165
|
+
// Insert into DOM (append when pos is at end)
|
|
166
|
+
if (pos >= items.length) {
|
|
167
|
+
el.appendChild(newEl);
|
|
168
|
+
} else {
|
|
169
|
+
el.insertBefore(newEl, items[pos]);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Initialise the new element
|
|
173
|
+
if (!newEl.id) newEl.id = 'moT' + crypto.randomUUID() + '_' + pos;
|
|
174
|
+
newEl.classList.add('mo-item', 'js-mo-item');
|
|
175
|
+
|
|
176
|
+
// Explicit fullWidth on item data takes priority, then fall back to randomFullWidth
|
|
177
|
+
if (item.fullWidth) {
|
|
178
|
+
newEl.classList.add('mo-fullwidth', 'js-mo-fullwidth');
|
|
179
|
+
} else if (data.randomFullWidth) {
|
|
180
|
+
const prob = data.randomFullWidth === true ? 0.33 : data.randomFullWidth;
|
|
181
|
+
if (Math.random() < prob) newEl.classList.add('mo-fullwidth', 'js-mo-fullwidth');
|
|
182
|
+
}
|
|
183
|
+
if (data.showBadge) this._createBadge(newEl, pos + 1);
|
|
184
|
+
if (data.showArrow) this._createArrow(newEl);
|
|
185
|
+
|
|
186
|
+
// Re-number all counter badges so sequence stays correct after insertion
|
|
187
|
+
if (data.showBadge && data.showCounterStyle === 'counter') {
|
|
188
|
+
Array.from(el.querySelectorAll('.js-mo-item')).forEach((it, i) => {
|
|
189
|
+
const badge = it.querySelector('.js-mo-badge');
|
|
190
|
+
if (badge) badge.textContent = i + 1;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
data.lastItemIdx = Array.from(el.children).length;
|
|
195
|
+
instanceData.set(el, data);
|
|
196
|
+
|
|
197
|
+
this.refresh();
|
|
198
|
+
|
|
199
|
+
newEl.querySelectorAll('img').forEach((img) => {
|
|
200
|
+
if (!img.complete) img.addEventListener('load', this._resizeHandler, { once: true });
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return newEl;
|
|
204
|
+
}
|
|
205
|
+
|
|
145
206
|
destroy() {
|
|
146
207
|
window.removeEventListener('resize', this._resizeHandler);
|
|
147
208
|
instanceData.delete(this.element);
|