@frontfactory/readmore 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,6 +14,18 @@ button.
14
14
  npm install @frontfactory/readmore
15
15
  ```
16
16
 
17
+ ## Playground
18
+
19
+ Try it live on StackBlitz — no installation required:
20
+
21
+ [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/~/github.com/front-factory/readmore)
22
+
23
+ Or run it locally:
24
+
25
+ ```shell
26
+ npm run dev
27
+ ```
28
+
17
29
  ## Usage
18
30
 
19
31
  ```ts
@@ -0,0 +1,19 @@
1
+ export interface ReadMoreOptions {
2
+ lines?: number;
3
+ moreText?: string;
4
+ lessText?: string;
5
+ buttonClass?: string;
6
+ expandedClass?: string;
7
+ }
8
+ type ResolvedOptions = Required<ReadMoreOptions>;
9
+ export declare class ReadMore {
10
+ #private;
11
+ readonly el: HTMLElement;
12
+ readonly options: ResolvedOptions;
13
+ constructor(element: HTMLElement, options?: ReadMoreOptions);
14
+ static init(target: string | NodeListOf<HTMLElement> | HTMLElement[], options?: ReadMoreOptions): ReadMore[];
15
+ get expanded(): boolean;
16
+ toggle(): void;
17
+ destroy(): void;
18
+ }
19
+ export {};
@@ -0,0 +1,57 @@
1
+ //#region src/readmore.ts
2
+ var e = "readmore-clamp", t = {
3
+ lines: 3,
4
+ moreText: "Read more",
5
+ lessText: "Read less",
6
+ buttonClass: "readmore-btn",
7
+ expandedClass: "is-expanded"
8
+ }, n = class n {
9
+ el;
10
+ options;
11
+ #e = null;
12
+ #t = !1;
13
+ #n = () => this.#o();
14
+ #r = () => this.toggle();
15
+ constructor(e, n = {}) {
16
+ if (!(e instanceof HTMLElement)) throw TypeError("ReadMore: an HTMLElement is required.");
17
+ this.el = e, this.options = {
18
+ ...t,
19
+ ...n
20
+ }, this.#i();
21
+ }
22
+ static init(e, t) {
23
+ let r = typeof e == "string" ? document.querySelectorAll(e) : e;
24
+ return Array.from(r).map((e) => new n(e, t));
25
+ }
26
+ get expanded() {
27
+ return this.#t;
28
+ }
29
+ toggle() {
30
+ this.#t = !this.#t, this.el.classList.toggle(e, !this.#t), this.el.classList.toggle(this.options.expandedClass, this.#t), this.#e && (this.#e.textContent = this.#t ? this.options.lessText : this.options.moreText), this.#t || this.#o();
31
+ }
32
+ destroy() {
33
+ window.removeEventListener("resize", this.#n), this.#c(), this.el.classList.remove(e, this.options.expandedClass), this.el.style.removeProperty("--readmore-lines");
34
+ }
35
+ #i() {
36
+ this.el.style.setProperty("--readmore-lines", String(this.options.lines)), this.el.classList.add(e), window.addEventListener("resize", this.#n), this.#o();
37
+ }
38
+ #a() {
39
+ return this.el.scrollHeight - this.el.clientHeight > 1;
40
+ }
41
+ #o() {
42
+ if (this.#t) return;
43
+ let e = this.#a();
44
+ e && !this.#e ? this.#s() : !e && this.#e && this.#c();
45
+ }
46
+ #s() {
47
+ let e = document.createElement("button");
48
+ 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;
49
+ }
50
+ #c() {
51
+ this.#e &&= (this.#e.removeEventListener("click", this.#r), this.#e.remove(), null);
52
+ }
53
+ };
54
+ //#endregion
55
+ export { n as ReadMore };
56
+
57
+ //# sourceMappingURL=readmore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readmore.js","names":["#refresh","#init","#expanded","#button","#onResize","#unmountButton","#isOverflowing","#mountButton","#onClick"],"sources":["../src/readmore.ts"],"sourcesContent":["export interface ReadMoreOptions {\n lines?: number;\n moreText?: string;\n lessText?: string;\n buttonClass?: string;\n expandedClass?: string;\n}\n\ntype ResolvedOptions = Required<ReadMoreOptions>;\n\nconst CLAMP_CLASS = 'readmore-clamp';\n\nconst DEFAULTS: ResolvedOptions = {\n lines: 3,\n moreText: 'Read more',\n lessText: 'Read less',\n buttonClass: 'readmore-btn',\n expandedClass: 'is-expanded'\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 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 if (!this.#expanded) this.#refresh();\n }\n\n destroy(): void {\n window.removeEventListener('resize', this.#onResize);\n this.#unmountButton();\n this.el.classList.remove(CLAMP_CLASS, this.options.expandedClass);\n this.el.style.removeProperty('--readmore-lines');\n }\n\n #init(): void {\n this.el.style.setProperty('--readmore-lines', String(this.options.lines));\n this.el.classList.add(CLAMP_CLASS);\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 #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":";AAUA,IAAM,IAAc,kBAEd,IAA4B;CAC9B,OAAO;CACP,UAAU;CACV,UAAU;CACV,aAAa;CACb,eAAe;CAClB,EAEY,IAAb,MAAa,EAAS;CAClB;CACA;CAEA,KAAoC;CACpC,KAAY;CACZ,WAAwB,KAAKA,IAAU;CACvC,WAAuB,KAAK,QAAQ;CAEpC,YAAY,GAAsB,IAA2B,EAAE,EAAE;EAC7D,IAAI,EAAE,aAAmB,cACrB,MAAU,UAAU,wCAAwC;EAIhE,AAFA,KAAK,KAAK,GACV,KAAK,UAAU;GAAE,GAAG;GAAU,GAAG;GAAS,EAC1C,KAAKC,IAAO;;CAGhB,OAAO,KACH,GACA,GACU;EACV,IAAM,IACF,OAAO,KAAW,WACZ,SAAS,iBAA8B,EAAO,GAC9C;EACV,OAAO,MAAM,KAAK,EAAM,CAAC,KAAK,MAAM,IAAI,EAAS,GAAG,EAAQ,CAAC;;CAGjE,IAAI,WAAoB;EACpB,OAAO,KAAKC;;CAGhB,SAAe;EASX,AARA,KAAKA,KAAY,CAAC,KAAKA,IACvB,KAAK,GAAG,UAAU,OAAO,GAAa,CAAC,KAAKA,GAAU,EACtD,KAAK,GAAG,UAAU,OAAO,KAAK,QAAQ,eAAe,KAAKA,GAAU,EAChE,KAAKC,OACL,KAAKA,GAAQ,cAAc,KAAKD,KAC1B,KAAK,QAAQ,WACb,KAAK,QAAQ,WAElB,KAAKA,MAAW,KAAKF,IAAU;;CAGxC,UAAgB;EAIZ,AAHA,OAAO,oBAAoB,UAAU,KAAKI,GAAU,EACpD,KAAKC,IAAgB,EACrB,KAAK,GAAG,UAAU,OAAO,GAAa,KAAK,QAAQ,cAAc,EACjE,KAAK,GAAG,MAAM,eAAe,mBAAmB;;CAGpD,KAAc;EAIV,AAHA,KAAK,GAAG,MAAM,YAAY,oBAAoB,OAAO,KAAK,QAAQ,MAAM,CAAC,EACzE,KAAK,GAAG,UAAU,IAAI,EAAY,EAClC,OAAO,iBAAiB,UAAU,KAAKD,GAAU,EACjD,KAAKJ,IAAU;;CAGnB,KAA0B;EACtB,OAAO,KAAK,GAAG,eAAe,KAAK,GAAG,eAAe;;CAGzD,KAAiB;EACb,IAAI,KAAKE,IAAW;EACpB,IAAM,IAAc,KAAKI,IAAgB;EACzC,AAAI,KAAe,CAAC,KAAKH,KACrB,KAAKI,IAAc,GACZ,CAAC,KAAe,KAAKJ,MAC5B,KAAKE,IAAgB;;CAI7B,KAAqB;EACjB,IAAM,IAAM,SAAS,cAAc,SAAS;EAM5C,AALA,EAAI,OAAO,UACX,EAAI,YAAY,KAAK,QAAQ,aAC7B,EAAI,cAAc,KAAK,QAAQ,UAC/B,EAAI,iBAAiB,SAAS,KAAKG,GAAS,EAC5C,KAAK,GAAG,sBAAsB,YAAY,EAAI,EAC9C,KAAKL,KAAU;;CAGnB,KAAuB;EACd,AAGL,KAAKA,QAFL,KAAKA,GAAQ,oBAAoB,SAAS,KAAKK,GAAS,EACxD,KAAKL,GAAQ,QAAQ,EACN"}
@@ -0,0 +1,7 @@
1
+ .readmore-clamp {
2
+ display: -webkit-box;
3
+ -webkit-box-orient: vertical;
4
+ overflow: hidden;
5
+ -webkit-line-clamp: var(--readmore-lines, 3);
6
+ line-clamp: var(--readmore-lines, 3);
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontfactory/readmore",
3
- "version": "1.0.0",
3
+ "version": "1.0.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": {
@@ -8,7 +8,7 @@
8
8
  "build": "vite build",
9
9
  "preview": "vite preview",
10
10
  "typecheck": "tsc --noEmit",
11
- "prepare": "husky",
11
+ "prepare": "husky && npm run build",
12
12
  "release": "semantic-release"
13
13
  },
14
14
  "engines": {