@frontfactory/readmore 1.1.0 → 1.1.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/dist/readmore.js CHANGED
@@ -12,14 +12,20 @@ var e = "readmore-clamp", t = "readmore-clamp--height", n = {
12
12
  options;
13
13
  #e = null;
14
14
  #t = !1;
15
- #n = () => this.#o();
16
- #r = () => this.toggle();
15
+ #n = null;
16
+ #r = null;
17
+ #i = () => {
18
+ this.#n !== null && clearTimeout(this.#n), this.#n = setTimeout(() => {
19
+ this.#n = null, this.#c();
20
+ }, 100);
21
+ };
22
+ #a = () => this.toggle();
17
23
  constructor(e, t = {}) {
18
24
  if (!(e instanceof HTMLElement)) throw TypeError("ReadMore: an HTMLElement is required.");
19
25
  this.el = e, this.options = {
20
26
  ...n,
21
27
  ...t
22
- }, this.#i();
28
+ }, this.#o();
23
29
  }
24
30
  static init(e, t) {
25
31
  let n = typeof e == "string" ? document.querySelectorAll(e) : e;
@@ -31,43 +37,41 @@ var e = "readmore-clamp", t = "readmore-clamp--height", n = {
31
37
  toggle() {
32
38
  this.#t = !this.#t, this.el.classList.toggle(e, !this.#t), this.options.height != null && this.el.classList.toggle(t, !this.#t), this.el.classList.toggle(this.options.expandedClass, this.#t), this.#e && (this.#e.textContent = this.#t ? this.options.lessText : this.options.moreText);
33
39
  let n = this.#t ? this.options.openingClass : this.options.closingClass, r = this.#t ? this.options.closingClass : this.options.openingClass;
34
- this.el.classList.remove(r), this.#s(n), this.#t || this.#o();
40
+ this.el.classList.remove(r), this.#l(n), this.#t || this.#c();
35
41
  }
36
42
  destroy() {
37
- window.removeEventListener("resize", this.#n), this.#l(), this.el.classList.remove(e, t, this.options.expandedClass, this.options.openingClass, this.options.closingClass), this.el.style.removeProperty("--readmore-lines"), this.el.style.removeProperty("--readmore-height");
43
+ window.removeEventListener("resize", this.#i), this.#n !== null && (clearTimeout(this.#n), this.#n = null), this.#r &&= (this.el.removeEventListener("transitionend", this.#r), null), this.#d(), this.el.classList.remove(e, t, this.options.expandedClass, this.options.openingClass, this.options.closingClass), this.el.style.removeProperty("--readmore-lines"), this.el.style.removeProperty("--readmore-height");
38
44
  }
39
- #i() {
40
- this.options.height == null ? (this.el.style.setProperty("--readmore-lines", String(this.options.lines)), this.el.classList.add(e)) : (this.el.style.setProperty("--readmore-height", `${this.options.height}px`), this.el.classList.add(e, t)), window.addEventListener("resize", this.#n), this.#o();
45
+ #o() {
46
+ this.options.height == null ? (this.el.style.setProperty("--readmore-lines", String(this.options.lines)), this.el.classList.add(e)) : (this.el.style.setProperty("--readmore-height", `${this.options.height}px`), this.el.classList.add(e, t)), window.addEventListener("resize", this.#i), this.#c();
41
47
  }
42
- #a() {
48
+ #s() {
43
49
  return this.el.scrollHeight - this.el.clientHeight > 1;
44
50
  }
45
- #o() {
51
+ #c() {
46
52
  if (this.#t) return;
47
- let e = this.#a();
48
- e && !this.#e ? this.#c() : !e && this.#e && this.#l();
53
+ let e = this.#s();
54
+ e && !this.#e ? this.#u() : !e && this.#e && this.#d();
49
55
  }
50
- #s(e) {
51
- this.el.classList.add(e);
56
+ #l(e) {
57
+ this.#r &&= (this.el.removeEventListener("transitionend", this.#r), null), this.el.classList.add(e);
52
58
  let t = () => this.el.classList.remove(e);
53
59
  if (!getComputedStyle(this.el).transitionDuration.split(",").some((e) => parseFloat(e) > 0)) {
54
60
  setTimeout(t, 0);
55
61
  return;
56
62
  }
57
63
  let n = (e) => {
58
- e.target === this.el && (this.el.removeEventListener("transitionend", n), t());
64
+ e.target === this.el && (this.el.removeEventListener("transitionend", n), this.#r = null, t());
59
65
  };
60
- this.el.addEventListener("transitionend", n);
66
+ this.#r = n, this.el.addEventListener("transitionend", n);
61
67
  }
62
- #c() {
68
+ #u() {
63
69
  let e = document.createElement("button");
64
- e.type = "button", e.className = this.options.buttonClass, e.textContent = this.options.moreText, e.addEventListener("click", this.#r), this.el.insertAdjacentElement("afterend", e), this.#e = e;
70
+ e.type = "button", e.className = this.options.buttonClass, e.textContent = this.options.moreText, e.addEventListener("click", this.#a), this.el.insertAdjacentElement("afterend", e), this.#e = e;
65
71
  }
66
- #l() {
67
- this.#e &&= (this.#e.removeEventListener("click", this.#r), this.#e.remove(), null);
72
+ #d() {
73
+ this.#e &&= (this.#e.removeEventListener("click", this.#a), this.#e.remove(), null);
68
74
  }
69
75
  };
70
76
  //#endregion
71
77
  export { r as ReadMore };
72
-
73
- //# sourceMappingURL=readmore.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontfactory/readmore",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Lightweight plugin to clamp text to N lines with ellipsis and a read more action.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -18,7 +18,6 @@
18
18
  "node": ">=20.19"
19
19
  },
20
20
  "main": "./dist/readmore.js",
21
- "module": "./dist/readmore.js",
22
21
  "types": "./dist/readmore.d.ts",
23
22
  "exports": {
24
23
  ".": {
@@ -38,6 +37,7 @@
38
37
  "conventional-changelog-conventionalcommits": "^9.3.1",
39
38
  "husky": "^9.1.7",
40
39
  "jsdom": "^29.1.1",
40
+ "lightningcss": "^1.32.0",
41
41
  "semantic-release": "^25.0.3",
42
42
  "typescript": "^5.4.0",
43
43
  "vite": "^8.0.11",
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- {"version":3,"file":"readmore.js","names":["#refresh","#init","#expanded","#button","#applyTransientClass","#onResize","#unmountButton","#isOverflowing","#mountButton","#onClick"],"sources":["../src/readmore.ts"],"sourcesContent":["export interface ReadMoreOptions {\n lines?: number;\n height?: number;\n moreText?: string;\n lessText?: string;\n buttonClass?: string;\n expandedClass?: string;\n openingClass?: string;\n closingClass?: string;\n}\n\ntype ResolvedOptions = Required<Omit<ReadMoreOptions, 'height'>> & { height?: number };\n\nconst CLAMP_CLASS = 'readmore-clamp';\nconst CLAMP_HEIGHT_CLASS = 'readmore-clamp--height';\n\nconst DEFAULTS: ResolvedOptions = {\n lines: 3,\n moreText: 'Read more',\n lessText: 'Read less',\n buttonClass: 'readmore-btn',\n expandedClass: 'is-expanded',\n openingClass: 'is-opening',\n closingClass: 'is-closing'\n};\n\nexport class ReadMore {\n readonly el: HTMLElement;\n readonly options: ResolvedOptions;\n\n #button: HTMLButtonElement | null = null;\n #expanded = false;\n #onResize = (): void => this.#refresh();\n #onClick = (): void => this.toggle();\n\n constructor(element: HTMLElement, options: ReadMoreOptions = {}) {\n if (!(element instanceof HTMLElement)) {\n throw new TypeError('ReadMore: an HTMLElement is required.');\n }\n this.el = element;\n this.options = { ...DEFAULTS, ...options };\n this.#init();\n }\n\n static init(\n target: string | NodeListOf<HTMLElement> | HTMLElement[],\n options?: ReadMoreOptions\n ): ReadMore[] {\n const nodes =\n typeof target === 'string'\n ? document.querySelectorAll<HTMLElement>(target)\n : target;\n return Array.from(nodes).map((n) => new ReadMore(n, options));\n }\n\n get expanded(): boolean {\n return this.#expanded;\n }\n\n toggle(): void {\n this.#expanded = !this.#expanded;\n this.el.classList.toggle(CLAMP_CLASS, !this.#expanded);\n if (this.options.height != null) {\n this.el.classList.toggle(CLAMP_HEIGHT_CLASS, !this.#expanded);\n }\n this.el.classList.toggle(this.options.expandedClass, this.#expanded);\n if (this.#button) {\n this.#button.textContent = this.#expanded\n ? this.options.lessText\n : this.options.moreText;\n }\n const stateClass = this.#expanded ? this.options.openingClass : this.options.closingClass;\n const otherClass = this.#expanded ? this.options.closingClass : this.options.openingClass;\n this.el.classList.remove(otherClass);\n this.#applyTransientClass(stateClass);\n if (!this.#expanded) this.#refresh();\n }\n\n destroy(): void {\n window.removeEventListener('resize', this.#onResize);\n this.#unmountButton();\n this.el.classList.remove(\n CLAMP_CLASS,\n CLAMP_HEIGHT_CLASS,\n this.options.expandedClass,\n this.options.openingClass,\n this.options.closingClass\n );\n this.el.style.removeProperty('--readmore-lines');\n this.el.style.removeProperty('--readmore-height');\n }\n\n #init(): void {\n if (this.options.height != null) {\n this.el.style.setProperty('--readmore-height', `${ this.options.height }px`);\n this.el.classList.add(CLAMP_CLASS, CLAMP_HEIGHT_CLASS);\n } else {\n this.el.style.setProperty('--readmore-lines', String(this.options.lines));\n this.el.classList.add(CLAMP_CLASS);\n }\n window.addEventListener('resize', this.#onResize);\n this.#refresh();\n }\n\n #isOverflowing(): boolean {\n return this.el.scrollHeight - this.el.clientHeight > 1;\n }\n\n #refresh(): void {\n if (this.#expanded) return;\n const overflowing = this.#isOverflowing();\n if (overflowing && !this.#button) {\n this.#mountButton();\n } else if (!overflowing && this.#button) {\n this.#unmountButton();\n }\n }\n\n #applyTransientClass(stateClass: string): void {\n this.el.classList.add(stateClass);\n const remove = (): void => this.el.classList.remove(stateClass);\n const duration = getComputedStyle(this.el).transitionDuration;\n const hasTransition = duration\n .split(',')\n .some((d) => parseFloat(d) > 0);\n if (!hasTransition) {\n setTimeout(remove, 0);\n return;\n }\n const onEnd = (e: TransitionEvent): void => {\n if (e.target !== this.el) return;\n this.el.removeEventListener('transitionend', onEnd);\n remove();\n };\n this.el.addEventListener('transitionend', onEnd);\n }\n\n #mountButton(): void {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = this.options.buttonClass;\n btn.textContent = this.options.moreText;\n btn.addEventListener('click', this.#onClick);\n this.el.insertAdjacentElement('afterend', btn);\n this.#button = btn;\n }\n\n #unmountButton(): void {\n if (!this.#button) return;\n this.#button.removeEventListener('click', this.#onClick);\n this.#button.remove();\n this.#button = null;\n }\n}\n"],"mappings":"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"}