@frontfactory/readmore 1.0.0 → 1.0.2
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 +12 -0
- package/dist/readmore.css +7 -0
- package/dist/readmore.d.ts +19 -0
- package/dist/readmore.js +57 -0
- package/dist/readmore.js.map +1 -0
- package/package.json +2 -2
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
|
+
[](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 {};
|
package/dist/readmore.js
ADDED
|
@@ -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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontfactory/readmore",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
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": {
|