@frontfactory/readmore 1.3.0 → 1.3.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 +50 -18
- package/dist/readmore.d.ts +103 -2
- package/package.json +24 -10
package/README.md
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
Lightweight, framework-agnostic plugin to clamp text to N lines (or a fixed pixel height) with an ellipsis and a
|
|
8
8
|
`Read more` / `Read less` toggle button.
|
|
9
9
|
|
|
10
|
-
- Zero dependencies, ~1 KB
|
|
10
|
+
- Zero dependencies, ~1.3 KB gzipped
|
|
11
11
|
- TypeScript types included
|
|
12
12
|
- CSS-driven clamping via `-webkit-line-clamp` or `max-height`
|
|
13
13
|
- The toggle button is only created when the text actually overflows
|
|
14
|
+
- Accessible by default: `aria-expanded` and `aria-controls` on the toggle button
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -18,6 +19,9 @@ Lightweight, framework-agnostic plugin to clamp text to N lines (or a fixed pixe
|
|
|
18
19
|
npm install @frontfactory/readmore
|
|
19
20
|
```
|
|
20
21
|
|
|
22
|
+
Requires Node.js `>=20.19` to build from source. The published package runs in any modern browser supporting
|
|
23
|
+
`ResizeObserver`.
|
|
24
|
+
|
|
21
25
|
## Playground
|
|
22
26
|
|
|
23
27
|
Try it live on StackBlitz — no installation required:
|
|
@@ -47,6 +51,14 @@ ReadMore.init('.excerpt', {
|
|
|
47
51
|
});
|
|
48
52
|
```
|
|
49
53
|
|
|
54
|
+
`ReadMore.init()` also accepts a `NodeList` or an array of elements:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
ReadMore.init(document.querySelectorAll<HTMLElement>('.excerpt'), {
|
|
58
|
+
lines: 3
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
50
62
|
Or instantiate against a single element:
|
|
51
63
|
|
|
52
64
|
```ts
|
|
@@ -60,44 +72,64 @@ if (el) new ReadMore(el, {
|
|
|
60
72
|
|
|
61
73
|
## Options
|
|
62
74
|
|
|
63
|
-
| Option | Type
|
|
64
|
-
|
|
65
|
-
| `lines` | `number`
|
|
66
|
-
| `height` | `number`
|
|
67
|
-
| `moreText` | `string`
|
|
68
|
-
| `lessText` | `string`
|
|
69
|
-
| `buttonClass` | `string`
|
|
70
|
-
| `expandedClass` | `string`
|
|
71
|
-
| `openingClass` | `string`
|
|
72
|
-
| `closingClass` | `string`
|
|
73
|
-
| `onToggle` | `(expanded: boolean) => void` | —
|
|
75
|
+
| Option | Type | Default | Description |
|
|
76
|
+
|-----------------|-------------------------------|------------------|---------------------------------------------------------------------|
|
|
77
|
+
| `lines` | `number` | `3` | Number of lines to clamp to (min `1`). Ignored if `height` is set. |
|
|
78
|
+
| `height` | `number` | — | Max height in pixels (min `1`). Takes precedence over `lines`. |
|
|
79
|
+
| `moreText` | `string` | `'Read more'` | Label of the button when the text is collapsed. |
|
|
80
|
+
| `lessText` | `string` | `'Read less'` | Label of the button when the text is expanded. |
|
|
81
|
+
| `buttonClass` | `string` | `'readmore-btn'` | Class applied to the toggle button. |
|
|
82
|
+
| `expandedClass` | `string` | `'is-expanded'` | Class applied to the target element while expanded. |
|
|
83
|
+
| `openingClass` | `string` | `'is-opening'` | Transient class applied while expanding, removed after transition. |
|
|
84
|
+
| `closingClass` | `string` | `'is-closing'` | Transient class applied while collapsing, removed after transition. |
|
|
85
|
+
| `onToggle` | `(expanded: boolean) => void` | — | Callback fired after each toggle with the current `expanded` state. |
|
|
74
86
|
|
|
75
87
|
## API
|
|
76
88
|
|
|
77
89
|
```ts
|
|
78
90
|
const instance = new ReadMore(element, options);
|
|
79
91
|
|
|
92
|
+
instance.el; // the target element
|
|
93
|
+
instance.options; // resolved options
|
|
80
94
|
instance.toggle(); // expand / collapse programmatically
|
|
81
95
|
instance.expanded; // boolean getter
|
|
82
|
-
instance.destroy(); // remove button, classes and
|
|
96
|
+
instance.destroy(); // remove button, classes, listeners and generated id
|
|
83
97
|
|
|
84
|
-
ReadMore.init(
|
|
98
|
+
ReadMore.init(target, options); // target: selector | NodeList | HTMLElement[] — returns ReadMore[]
|
|
99
|
+
ReadMore.getInstance(element); // returns the ReadMore bound to an element, or undefined
|
|
85
100
|
```
|
|
86
101
|
|
|
102
|
+
### Errors
|
|
103
|
+
|
|
104
|
+
The constructor throws instead of failing silently:
|
|
105
|
+
|
|
106
|
+
| Error | When |
|
|
107
|
+
|--------------|-------------------------------------------------------------------------------|
|
|
108
|
+
| `TypeError` | `element` is not an `HTMLElement`. |
|
|
109
|
+
| `Error` | The element is already initialized — call `destroy()` before re-initializing. |
|
|
110
|
+
| `RangeError` | `lines` is lower than `1`, or `height` is lower than or equal to `0`. |
|
|
111
|
+
|
|
112
|
+
## Accessibility
|
|
113
|
+
|
|
114
|
+
When the button is mounted, it gets `aria-expanded` (kept in sync on every toggle) and `aria-controls`
|
|
115
|
+
pointing to the target element. If the element has no `id`, a unique one (`readmore-1`, `readmore-2`, …)
|
|
116
|
+
is generated and removed again on `destroy()`; an existing `id` is always preserved.
|
|
117
|
+
|
|
87
118
|
## How it works
|
|
88
119
|
|
|
89
120
|
The plugin sets a CSS custom property on the target element and toggles `.readmore-clamp`. In **lines mode** (default),
|
|
90
121
|
it sets `--readmore-lines` and the bundled stylesheet applies `-webkit-line-clamp`, producing an ellipsis at the end of
|
|
91
122
|
the last visible line. In **height mode** (`height` option), it sets `--readmore-height` and applies `max-height` via
|
|
92
123
|
the `.readmore-clamp--height` modifier class instead. The toggle button is mounted right after the element only when the
|
|
93
|
-
content actually overflows
|
|
124
|
+
content actually overflows; a `ResizeObserver` watches the element and unmounts the button again if the text ends up
|
|
125
|
+
fitting (or mounts it later if it starts overflowing).
|
|
94
126
|
|
|
95
127
|
### Transient classes for animations
|
|
96
128
|
|
|
97
129
|
In addition to `expandedClass` (which stays applied for the entire expanded state), the plugin applies short-lived
|
|
98
|
-
`openingClass` and `closingClass` markers on the target element at the moment of expand and collapse respectively.
|
|
99
|
-
|
|
100
|
-
|
|
130
|
+
`openingClass` and `closingClass` markers on the target element at the moment of expand and collapse respectively. Each
|
|
131
|
+
is added when the toggle starts and removed automatically once the CSS transition on the element ends — or on the next
|
|
132
|
+
tick if no transition is declared. Use them as hooks to drive enter/leave animations:
|
|
101
133
|
|
|
102
134
|
```css
|
|
103
135
|
.is-opening {
|
package/dist/readmore.d.ts
CHANGED
|
@@ -1,27 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options accepted by {@link ReadMore}. Every field is optional.
|
|
3
|
+
*/
|
|
1
4
|
export interface ReadMoreOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Number of lines to clamp to. Ignored when {@link ReadMoreOptions.height} is set.
|
|
7
|
+
*
|
|
8
|
+
* @defaultValue 3
|
|
9
|
+
* @throws RangeError - if lower than 1.
|
|
10
|
+
*/
|
|
2
11
|
lines?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Max height in pixels. Takes precedence over {@link ReadMoreOptions.lines}
|
|
14
|
+
* and clamps with `max-height` instead of `-webkit-line-clamp`.
|
|
15
|
+
*
|
|
16
|
+
* @throws RangeError - if lower than or equal to 0.
|
|
17
|
+
*/
|
|
3
18
|
height?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Label of the toggle button while the text is collapsed.
|
|
21
|
+
*
|
|
22
|
+
* @defaultValue 'Read more'
|
|
23
|
+
*/
|
|
4
24
|
moreText?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Label of the toggle button while the text is expanded.
|
|
27
|
+
*
|
|
28
|
+
* @defaultValue 'Read less'
|
|
29
|
+
*/
|
|
5
30
|
lessText?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Class applied to the toggle button.
|
|
33
|
+
*
|
|
34
|
+
* @defaultValue 'readmore-btn'
|
|
35
|
+
*/
|
|
6
36
|
buttonClass?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Class applied to the target element for the whole expanded state.
|
|
39
|
+
*
|
|
40
|
+
* @defaultValue 'is-expanded'
|
|
41
|
+
*/
|
|
7
42
|
expandedClass?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Transient class applied when expanding, removed once the CSS transition
|
|
45
|
+
* on the element ends — or on the next tick if no transition is declared.
|
|
46
|
+
*
|
|
47
|
+
* @defaultValue 'is-opening'
|
|
48
|
+
*/
|
|
8
49
|
openingClass?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Transient class applied when collapsing, removed once the CSS transition
|
|
52
|
+
* on the element ends — or on the next tick if no transition is declared.
|
|
53
|
+
*
|
|
54
|
+
* @defaultValue 'is-closing'
|
|
55
|
+
*/
|
|
9
56
|
closingClass?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Called after each toggle with the resulting state.
|
|
59
|
+
*
|
|
60
|
+
* @param expanded - `true` once expanded, `false` once collapsed.
|
|
61
|
+
*/
|
|
10
62
|
onToggle?: (expanded: boolean) => void;
|
|
11
63
|
}
|
|
12
|
-
|
|
64
|
+
/**
|
|
65
|
+
* {@link ReadMoreOptions} merged with the defaults, as exposed on
|
|
66
|
+
* {@link ReadMore.options}.
|
|
67
|
+
*/
|
|
68
|
+
export type ResolvedOptions = Required<Omit<ReadMoreOptions, 'height' | 'onToggle'>> & {
|
|
13
69
|
height?: number;
|
|
14
70
|
onToggle?: (expanded: boolean) => void;
|
|
15
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Clamps an element to a number of lines (or a fixed height) and mounts a
|
|
74
|
+
* `Read more` / `Read less` button right after it, but only while the content
|
|
75
|
+
* actually overflows.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { ReadMore } from '@frontfactory/readmore';
|
|
80
|
+
* import '@frontfactory/readmore/style.css';
|
|
81
|
+
*
|
|
82
|
+
* ReadMore.init('.excerpt', { lines: 3 });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
16
85
|
export declare class ReadMore {
|
|
17
86
|
#private;
|
|
87
|
+
/** The clamped element. */
|
|
18
88
|
readonly el: HTMLElement;
|
|
89
|
+
/** The options this instance was built with, merged with the defaults. */
|
|
19
90
|
readonly options: ResolvedOptions;
|
|
91
|
+
/**
|
|
92
|
+
* @param element - The element to clamp.
|
|
93
|
+
* @param options - See {@link ReadMoreOptions}.
|
|
94
|
+
* @throws TypeError - if `element` is not an `HTMLElement`.
|
|
95
|
+
* @throws Error - if `element` is already initialized; call
|
|
96
|
+
* {@link ReadMore.destroy} first.
|
|
97
|
+
* @throws RangeError - if `lines` is lower than 1, or `height` lower than
|
|
98
|
+
* or equal to 0.
|
|
99
|
+
*/
|
|
20
100
|
constructor(element: HTMLElement, options?: ReadMoreOptions);
|
|
101
|
+
/**
|
|
102
|
+
* Instantiates {@link ReadMore} on every matching element.
|
|
103
|
+
*
|
|
104
|
+
* @param target - A CSS selector, a `NodeList` or an array of elements.
|
|
105
|
+
* @param options - See {@link ReadMoreOptions}.
|
|
106
|
+
* @returns One instance per element, in document order.
|
|
107
|
+
*/
|
|
21
108
|
static init(target: string | NodeListOf<HTMLElement> | HTMLElement[], options?: ReadMoreOptions): ReadMore[];
|
|
109
|
+
/** Whether the text is currently expanded. */
|
|
22
110
|
get expanded(): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Expands or collapses the text, updating the button label, the
|
|
113
|
+
* `aria-expanded` attribute and the state classes, then calls
|
|
114
|
+
* {@link ReadMoreOptions.onToggle}.
|
|
115
|
+
*/
|
|
23
116
|
toggle(): void;
|
|
117
|
+
/**
|
|
118
|
+
* @param element - An element possibly initialized by this plugin.
|
|
119
|
+
* @returns The instance bound to it, or `undefined`.
|
|
120
|
+
*/
|
|
24
121
|
static getInstance(element: HTMLElement): ReadMore | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* Restores the element: removes the button, the classes, the CSS custom
|
|
124
|
+
* properties, the listeners and the generated `id`. The element can then be
|
|
125
|
+
* initialized again.
|
|
126
|
+
*/
|
|
25
127
|
destroy(): void;
|
|
26
128
|
}
|
|
27
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontfactory/readmore",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "Zero-dependency plugin to truncate text to N lines or a fixed height, with an ellipsis and an accessible read more / read less toggle.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite",
|
|
@@ -12,18 +12,20 @@
|
|
|
12
12
|
"test": "vitest run",
|
|
13
13
|
"test:watch": "vitest",
|
|
14
14
|
"test:coverage": "vitest run --coverage",
|
|
15
|
-
"prepare": "husky
|
|
15
|
+
"prepare": "husky",
|
|
16
|
+
"prepack": "npm run build",
|
|
16
17
|
"release": "semantic-release"
|
|
17
18
|
},
|
|
18
19
|
"engines": {
|
|
19
|
-
"node": ">=20
|
|
20
|
+
"node": ">=20"
|
|
20
21
|
},
|
|
21
22
|
"main": "./dist/readmore.js",
|
|
22
23
|
"types": "./dist/readmore.d.ts",
|
|
23
24
|
"exports": {
|
|
24
25
|
".": {
|
|
25
26
|
"types": "./dist/readmore.d.ts",
|
|
26
|
-
"import": "./dist/readmore.js"
|
|
27
|
+
"import": "./dist/readmore.js",
|
|
28
|
+
"default": "./dist/readmore.js"
|
|
27
29
|
},
|
|
28
30
|
"./style.css": "./dist/readmore.css",
|
|
29
31
|
"./package.json": "./package.json"
|
|
@@ -49,16 +51,22 @@
|
|
|
49
51
|
"vite-plugin-static-copy": "^4.1.0",
|
|
50
52
|
"vitest": "^4.1.5"
|
|
51
53
|
},
|
|
52
|
-
"sideEffects": false,
|
|
53
|
-
"files": [
|
|
54
|
-
"dist"
|
|
55
|
-
],
|
|
56
54
|
"keywords": [
|
|
57
55
|
"readmore",
|
|
56
|
+
"read-more",
|
|
57
|
+
"show-more",
|
|
58
|
+
"truncate",
|
|
59
|
+
"text-truncate",
|
|
60
|
+
"line-clamp",
|
|
61
|
+
"clamp",
|
|
62
|
+
"ellipsis",
|
|
58
63
|
"expand",
|
|
59
64
|
"collapse",
|
|
60
|
-
"
|
|
65
|
+
"typescript",
|
|
66
|
+
"vanilla-js",
|
|
67
|
+
"a11y"
|
|
61
68
|
],
|
|
69
|
+
"author": "Front Factory",
|
|
62
70
|
"license": "MIT",
|
|
63
71
|
"homepage": "https://github.com/front-factory/readmore",
|
|
64
72
|
"bugs": {
|
|
@@ -68,6 +76,12 @@
|
|
|
68
76
|
"type": "git",
|
|
69
77
|
"url": "git+https://github.com/front-factory/readmore.git"
|
|
70
78
|
},
|
|
79
|
+
"sideEffects": [
|
|
80
|
+
"**/*.css"
|
|
81
|
+
],
|
|
82
|
+
"files": [
|
|
83
|
+
"dist"
|
|
84
|
+
],
|
|
71
85
|
"publishConfig": {
|
|
72
86
|
"access": "public",
|
|
73
87
|
"provenance": true
|