tiny-essentials 1.20.2 โ†’ 1.20.3

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.
@@ -0,0 +1,198 @@
1
+ /**
2
+ * @typedef {Object} OnInputInfo
3
+ * @property {number} breakLines - Total number of `\n` line breaks in the textarea value.
4
+ * @property {number} height - Final calculated height (in pixels) applied to the textarea.
5
+ * @property {number} scrollHeight - Internal scrollHeight before limiting.
6
+ * @property {number} maxHeight - Maximum allowed height before scrolling is forced.
7
+ * @property {number} lineHeight - Height of one line of text, computed from CSS.
8
+ * @property {number} maxRows - Maximum number of visible rows allowed.
9
+ * @property {number} rows - Effective number of visual rows being used.
10
+ */
11
+ /**
12
+ * A lightweight utility class that automatically adjusts the height of a `<textarea>`
13
+ * element based on its content. It prevents scrollbars by expanding vertically as needed,
14
+ * up to a configurable maximum number of visible rows.
15
+ *
16
+ * Features:
17
+ * - Automatically resizes the textarea as the user types
18
+ * - Prevents vertical scrollbars until a maximum row limit is reached
19
+ * - Supports additional height padding
20
+ * - Provides real-time callbacks for input and resize events
21
+ * - Allows manual refresh and cleanup of behavior
22
+ *
23
+ * Ideal for chat inputs, note editors, or any form where dynamic space usage
24
+ * is preferred without relying on scrollbars too early.
25
+ *
26
+ * @class
27
+ * @beta
28
+ */
29
+ class TinyTextarea {
30
+ #lineHeight;
31
+ #maxRows;
32
+ #extraHeight;
33
+ #lastKnownHeight = 0;
34
+ #lastKnownRows = 0;
35
+ /** @type {HTMLTextAreaElement} */
36
+ #textarea;
37
+ /**
38
+ * @type {((info: OnInputInfo) => void) | null}
39
+ */
40
+ #onResize = null;
41
+ /**
42
+ * @type {((info: OnInputInfo) => void) | null}
43
+ */
44
+ #onInput = null;
45
+ /**
46
+ * Returns the computed line height in pixels.
47
+ * @returns {number}
48
+ */
49
+ get lineHeight() {
50
+ return this.#lineHeight;
51
+ }
52
+ /**
53
+ * Returns the maximum number of rows allowed.
54
+ * @returns {number}
55
+ */
56
+ get maxRows() {
57
+ return this.#maxRows - 1;
58
+ }
59
+ /**
60
+ * Returns the additional height added to the textarea.
61
+ * @returns {number}
62
+ */
63
+ get extraHeight() {
64
+ return this.#extraHeight;
65
+ }
66
+ /**
67
+ * Returns the most recently applied height.
68
+ * @returns {number}
69
+ */
70
+ get currentHeight() {
71
+ return this.#lastKnownHeight;
72
+ }
73
+ /**
74
+ * Returns the most recently calculated row count.
75
+ * @returns {number}
76
+ */
77
+ get currentRows() {
78
+ return this.#lastKnownRows;
79
+ }
80
+ /**
81
+ * Returns the original textarea element managed by this instance.
82
+ * @returns {HTMLTextAreaElement}
83
+ */
84
+ get textarea() {
85
+ return this.#textarea;
86
+ }
87
+ /**
88
+ * Creates a new TinyTextarea instance.
89
+ *
90
+ * @param {HTMLTextAreaElement} textarea - The `<textarea>` element to enhance.
91
+ * @param {Object} [options={}] - Optional configuration parameters.
92
+ * @param {number} [options.maxRows] - Maximum number of visible rows before scrolling.
93
+ * @param {number} [options.extraHeight] - Additional pixels to add to final height.
94
+ * @param {(info: OnInputInfo) => void} [options.onResize] - Callback when the number of rows changes.
95
+ * @param {(info: OnInputInfo) => void} [options.onInput] - Callback on every input event.
96
+ * @throws {Error} If `textarea` is not a valid `<textarea>` element.
97
+ * @throws {TypeError} If provided options are of invalid types.
98
+ */
99
+ constructor(textarea, options = {}) {
100
+ if (!(textarea instanceof HTMLTextAreaElement))
101
+ throw new Error('TinyTextarea: Provided element is not a <textarea>.');
102
+ if (typeof options !== 'object' || options === null)
103
+ throw new TypeError('TinyTextarea: Options must be an object if provided.');
104
+ if ('maxRows' in options && typeof options.maxRows !== 'number')
105
+ throw new TypeError('TinyTextarea: `maxRows` must be a number.');
106
+ if ('extraHeight' in options && typeof options.extraHeight !== 'number')
107
+ throw new TypeError('TinyTextarea: `extraHeight` must be a number.');
108
+ if ('onResize' in options && typeof options.onResize !== 'function')
109
+ throw new TypeError('TinyTextarea: `onResize` must be a function.');
110
+ if ('onInput' in options && typeof options.onInput !== 'function')
111
+ throw new TypeError('TinyTextarea: `onInput` must be a function.');
112
+ this.#textarea = textarea;
113
+ this.#maxRows = (options.maxRows ?? 5) + 1;
114
+ this.#extraHeight = options.extraHeight ?? 0;
115
+ this.#onResize = options.onResize ?? null;
116
+ this.#onInput = options.onInput ?? null;
117
+ this.#lineHeight = this.#getLineHeight();
118
+ textarea.style.overflowY = 'hidden';
119
+ textarea.style.resize = 'none';
120
+ this._handleInput = () => this.#resize();
121
+ textarea.addEventListener('input', this._handleInput);
122
+ this.#resize();
123
+ }
124
+ /**
125
+ * Automatically resize the textarea based on its content and notify listeners.
126
+ * Triggers `onResize` if the number of rows has changed.
127
+ * Always triggers `onInput`.
128
+ */
129
+ #resize() {
130
+ this.#textarea.style.height = 'auto';
131
+ const style = window.getComputedStyle(this.#textarea);
132
+ const paddingTop = parseFloat(style.paddingTop) || 0;
133
+ const paddingBottom = parseFloat(style.paddingBottom) || 0;
134
+ const breakLines = (this.#textarea.value.match(/\n/g) || []).length;
135
+ const scrollHeight = this.#textarea.scrollHeight;
136
+ const maxHeight = this.#lineHeight * this.#maxRows;
137
+ const newHeight = Math.ceil(Math.min(scrollHeight, maxHeight) - paddingTop - paddingBottom + this.#extraHeight);
138
+ // const rows = Math.round(newHeight / this.#lineHeight);
139
+ const maxRows = this.#maxRows - 1;
140
+ const rows = breakLines < maxRows ? breakLines + 1 : maxRows;
141
+ this.#textarea.style.height = `${newHeight}px`;
142
+ this.#textarea.style.overflowY = scrollHeight > maxHeight ? 'auto' : 'hidden';
143
+ this.#lastKnownHeight = newHeight;
144
+ const info = {
145
+ breakLines,
146
+ rows,
147
+ height: newHeight,
148
+ scrollHeight,
149
+ maxHeight,
150
+ lineHeight: this.#lineHeight,
151
+ maxRows,
152
+ };
153
+ if (rows !== this.#lastKnownRows) {
154
+ this.#lastKnownRows = rows;
155
+ if (typeof this.#onResize === 'function') {
156
+ this.#onResize({ ...info });
157
+ }
158
+ }
159
+ if (typeof this.#onInput === 'function') {
160
+ this.#onInput(info);
161
+ }
162
+ }
163
+ /**
164
+ * Computes the current line height from the textarea's computed styles.
165
+ * Falls back to `fontSize * 1.2` if lineHeight is not a number.
166
+ * @returns {number} - The computed line height in pixels.
167
+ */
168
+ #getLineHeight() {
169
+ const style = window.getComputedStyle(this.#textarea);
170
+ const line = parseFloat(style.lineHeight);
171
+ if (!Number.isNaN(line))
172
+ return line;
173
+ return parseFloat(style.fontSize) * 1.2;
174
+ }
175
+ /**
176
+ * Returns the latest height and row count of the textarea.
177
+ * @returns {{ height: number, rows: number }} - Last known resize state.
178
+ */
179
+ getData() {
180
+ return {
181
+ rows: this.#lastKnownRows,
182
+ height: this.#lastKnownHeight,
183
+ };
184
+ }
185
+ /**
186
+ * Manually trigger a resize check.
187
+ */
188
+ refresh() {
189
+ this.#resize();
190
+ }
191
+ /**
192
+ * Cleans up internal listeners and disables dynamic behavior.
193
+ */
194
+ destroy() {
195
+ this.#textarea.removeEventListener('input', this._handleInput);
196
+ }
197
+ }
198
+ export default TinyTextarea;
package/docs/v1/README.md CHANGED
@@ -46,6 +46,7 @@ This folder contains the core scripts we have worked on so far. Each file is a m
46
46
  - ๐Ÿ“ฆ **[TinyLocalStorage](./libs/TinyLocalStorage.md)** โ€” A tiny wrapper for `localStorage` with full support for objects, arrays, `Map`, `Set`, and typed value helpers like string, number, and boolean.
47
47
  - ๐Ÿ–ผ๏ธ **[TinyIframeEvents](./libs/TinyIframeEvents.md)** โ€” A structured `postMessage`-based event router for secure and reliable communication between a parent window and its embedded iframe. Supports directional filtering, origin enforcement, payload transport, and listener lifecycle.
48
48
  - ๐ŸชŸ **[TinyNewWinEvents](./libs/TinyNewWinEvents.md)** โ€” A smart, route-based `postMessage` system for structured communication between a main window and a popup (`window.open`). Includes queueing, origin enforcement, and lifecycle tracking.
49
+ - โœจ **[TinyTextarea](./libs/TinyTextarea.md)** โ€” A minimal auto-expanding `<textarea>` manager with configurable row limits, extra height padding, and real-time resize/input event hooks.
49
50
 
50
51
  ### 3. **`fileManager/`**
51
52
  * ๐Ÿ“ **[Main](./fileManager/main.md)** โ€” A Node.js file/directory utility module with support for JSON, backups, renaming, size analysis, and more.
@@ -442,19 +442,19 @@ Scrolls the target all the way to the top immediately.
442
442
 
443
443
  ## ๐Ÿงญ Scroll Position Status
444
444
 
445
- ### ๐Ÿ”š `isUserAtCustomBottom()`
445
+ ### ๐Ÿ”š `isAtCustomBottom()`
446
446
 
447
447
  Returns `true` if the user is within the custom boundary of the bottom.
448
448
 
449
- ### ๐Ÿ” `isUserAtCustomTop()`
449
+ ### ๐Ÿ” `isAtCustomTop()`
450
450
 
451
451
  Returns `true` if the user is within the custom boundary of the top.
452
452
 
453
- ### ๐Ÿงจ `isUserAtBottom()`
453
+ ### ๐Ÿงจ `isAtBottom()`
454
454
 
455
455
  Returns `true` if user is currently scrolled to the actual bottom.
456
456
 
457
- ### ๐Ÿงท `isUserAtTop()`
457
+ ### ๐Ÿงท `isAtTop()`
458
458
 
459
459
  Returns `true` if user is currently scrolled to the actual top.
460
460
 
@@ -0,0 +1,156 @@
1
+ # โœจ `TinyTextarea`
2
+
3
+ A lightweight JavaScript utility for automatically resizing `<textarea>` elements as users type โ€” without ugly scrollbars!
4
+
5
+ ---
6
+
7
+ ## ๐Ÿ“ฆ Features
8
+
9
+ * ๐Ÿ” **Auto-resizing**: Grows or shrinks with the content
10
+ * ๐Ÿšซ **No scrollbars** (until limit): Prevents `overflow-y` unless needed
11
+ * ๐Ÿ”ง **Configurable**: Set `maxRows`, `extraHeight`, and hooks
12
+ * ๐Ÿ“ก **Live events**: Hooks for `onInput` and `onResize`
13
+ * ๐Ÿงผ **Clean lifecycle**: Includes `.refresh()` and `.destroy()`
14
+
15
+ ---
16
+
17
+ ## ๐Ÿš€ Usage
18
+
19
+ ```js
20
+ import TinyTextarea from './TinyTextarea.js';
21
+
22
+ const textarea = document.querySelector('textarea');
23
+
24
+ const autoResize = new TinyTextarea(textarea, {
25
+ maxRows: 8,
26
+ extraHeight: 0,
27
+ onResize: (info) => console.log('Resized:', info),
28
+ onInput: (info) => console.log('Input event:', info),
29
+ });
30
+ ```
31
+
32
+ To stop behavior and clean up:
33
+
34
+ ```js
35
+ autoResize.destroy();
36
+ ```
37
+
38
+ ---
39
+
40
+ ## ๐Ÿ”ง Constructor
41
+
42
+ ```js
43
+ new TinyTextarea(textarea, options?);
44
+ ```
45
+
46
+ ### Parameters
47
+
48
+ | Name | Type | Description |
49
+ | --------------------- | ----------------------------- | ----------------------------------------------------- |
50
+ | `textarea` | `HTMLTextAreaElement` | The `<textarea>` element to be managed. |
51
+ | `options` | `Object` *(optional)* | Configuration options. |
52
+ | `options.maxRows` | `number` | Max number of visible rows before scrollbars appear. |
53
+ | `options.extraHeight` | `number` | Extra height (in px) to add on top of computed value. |
54
+ | `options.onResize` | `(info: OnInputInfo) => void` | Called when number of visible rows changes. |
55
+ | `options.onInput` | `(info: OnInputInfo) => void` | Called on every `input` event. |
56
+
57
+ ---
58
+
59
+ ## ๐Ÿ“š API
60
+
61
+ ### `.getData() โ†’ { height, rows }`
62
+
63
+ Returns the last known height and number of visible rows.
64
+
65
+ ```js
66
+ const { height, rows } = autoResize.getData();
67
+ ```
68
+
69
+ ---
70
+
71
+ ### `.refresh()`
72
+
73
+ Forces a manual recalculation and resize of the `<textarea>`.
74
+
75
+ ```js
76
+ autoResize.refresh();
77
+ ```
78
+
79
+ ---
80
+
81
+ ### `.destroy()`
82
+
83
+ Cleans up listeners and disables resizing behavior. Use this if you remove the element or want to stop automatic behavior.
84
+
85
+ ```js
86
+ autoResize.destroy();
87
+ ```
88
+
89
+ ---
90
+
91
+ ### ๐Ÿ” Getters
92
+
93
+ TinyTextarea exposes several readonly properties to let you inspect the current configuration and state of the managed `<textarea>`.
94
+
95
+ #### ๐Ÿ“ `lineHeight: number`
96
+
97
+ Returns the height in pixels of a single line of text in the textarea, computed from its current CSS styles.
98
+ Useful for calculating exact space usage or aligning UI elements.
99
+
100
+ ---
101
+
102
+ #### ๐Ÿ“ `maxRows: number`
103
+
104
+ Returns the maximum number of visible text rows allowed before the textarea begins to scroll.
105
+ This value reflects the limit set via `options.maxRows`.
106
+
107
+ ---
108
+
109
+ #### โž• `extraHeight: number`
110
+
111
+ Returns the number of extra pixels added to the calculated height of the textarea.
112
+ This is useful for custom padding or visual adjustments.
113
+
114
+ ---
115
+
116
+ #### ๐Ÿ“ `currentHeight: number`
117
+
118
+ Returns the most recently applied height (in pixels) that was set on the `<textarea>` after resizing.
119
+
120
+ ---
121
+
122
+ #### ๐Ÿ“Š `currentRows: number`
123
+
124
+ Returns the most recent number of visible text rows that the textarea is currently using.
125
+ This value is calculated dynamically and may change as the user types.
126
+
127
+ ---
128
+
129
+ #### ๐Ÿงฉ `textarea: HTMLTextAreaElement`
130
+
131
+ Returns the original `<textarea>` DOM element that was enhanced by this instance.
132
+ You can use this for direct styling, value changes, or other DOM-level operations.
133
+
134
+ ---
135
+
136
+ ## ๐Ÿ“ค Event Payload: `OnInputInfo`
137
+
138
+ Whenever `onResize` or `onInput` is triggered, the following object is passed:
139
+
140
+ ```ts
141
+ {
142
+ breakLines: number; // Total number of '\n' in the textarea
143
+ height: number; // Final computed height (px)
144
+ scrollHeight: number; // Natural scrollHeight of textarea
145
+ maxHeight: number; // Max height before scrollbars show
146
+ lineHeight: number; // Height of one line of text
147
+ maxRows: number; // Maximum number of visible rows allowed
148
+ rows: number; // Final calculated number of rows used
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ## ๐Ÿงช Beta Notice
155
+
156
+ > ๐Ÿงช This library is currently marked as `@beta`. The API is stable but may be adjusted slightly during updates.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-essentials",
3
- "version": "1.20.2",
3
+ "version": "1.20.3",
4
4
  "description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
5
5
  "scripts": {
6
6
  "test": "npm run test:mjs && npm run test:cjs && npm run test:js",