@briannorman9/eli-utils 1.0.0

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.
Files changed (3) hide show
  1. package/README.md +81 -0
  2. package/index.js +262 -0
  3. package/package.json +30 -0
package/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # @briannorman9/eli-utils
2
+
3
+ Utility functions for ELI web experiments.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @briannorman9/eli-utils
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ In your ELI project code, you can import utils using the special `@eli/utils` import syntax (the server will automatically resolve it to the installed `eli-utils` package):
14
+
15
+ ```javascript
16
+ import utils from '@eli/utils';
17
+
18
+ // Wait for an element to appear
19
+ utils.waitForElement('#myElement').then(element => {
20
+ console.log('Element found:', element);
21
+ });
22
+
23
+ // Wait until a condition is met
24
+ utils.waitUntil(() => document.readyState === 'complete');
25
+
26
+ // DOM manipulation
27
+ utils.addClass('#myButton', 'active');
28
+ utils.removeClass('#myButton', 'inactive');
29
+
30
+ // Event handling
31
+ utils.on('#myButton', 'click', () => console.log('Clicked!'));
32
+
33
+ // Cookies
34
+ const userId = utils.getCookie('userId');
35
+ utils.setCookie('userId', '12345', 30);
36
+
37
+ // Query parameters
38
+ const campaign = utils.getQueryParam('campaign');
39
+
40
+ // Custom events
41
+ utils.triggerEvent('experimentLoaded', { variant: 'v1' });
42
+ ```
43
+
44
+ ## API Reference
45
+
46
+ ### Element Utilities
47
+
48
+ - `waitForElement(selector)` - Wait for element to appear (waits indefinitely)
49
+ - `waitUntil(condition, interval)` - Wait until condition is true (waits indefinitely, checks every `interval` ms, default: 100ms)
50
+ - `select(selector, context)` - Select single element
51
+ - `selectAll(selector, context)` - Select multiple elements
52
+ - `addClass(element, className)` - Add class to element
53
+ - `removeClass(element, className)` - Remove class from element
54
+ - `toggleClass(element, className)` - Toggle class on element
55
+ - `hasClass(element, className)` - Check if element has class
56
+
57
+ ### Event Utilities
58
+
59
+ - `on(element, event, handler)` - Add event listener
60
+ - `off(element, event, handler)` - Remove event listener
61
+ - `delegate(parent, selector, event, handler)` - Event delegation
62
+ - `triggerEvent(eventName, data, target)` - Trigger custom event
63
+
64
+ ### Cookie Utilities
65
+
66
+ - `getCookie(name)` - Get cookie value
67
+ - `setCookie(name, value, days, path)` - Set cookie
68
+
69
+ ### URL Utilities
70
+
71
+ - `getQueryParam(name, url)` - Get URL query parameter
72
+
73
+ ### Viewport Utilities
74
+
75
+ - `getViewport()` - Get viewport dimensions
76
+ - `isInViewport(element, threshold)` - Check if element is in viewport
77
+ - `scrollIntoView(element, options)` - Scroll element into view
78
+
79
+ ## License
80
+
81
+ MIT
package/index.js ADDED
@@ -0,0 +1,262 @@
1
+ // ELI Utils - Utility functions for web experiments
2
+ // This package can be installed via: npm install @briannorman9/eli-utils
3
+ // Then imported in variant code using: import utils from '@eli/utils';
4
+ // (The server automatically resolves @eli/utils to the @briannorman9/eli-utils package)
5
+
6
+ export default {
7
+ /**
8
+ * Wait for an element to appear in the DOM
9
+ * @param {string|Element} selector - CSS selector or element
10
+ * @returns {Promise<Element>} Promise that resolves with the element (waits indefinitely)
11
+ */
12
+ waitForElement: function(selector) {
13
+ return new Promise((resolve) => {
14
+ // If selector is already an element, return it immediately
15
+ if (selector instanceof Element) {
16
+ resolve(selector);
17
+ return;
18
+ }
19
+
20
+ // Check if element already exists
21
+ const element = document.querySelector(selector);
22
+ if (element) {
23
+ resolve(element);
24
+ return;
25
+ }
26
+
27
+ // Set up MutationObserver to watch for element
28
+ const observer = new MutationObserver((mutations, obs) => {
29
+ const element = document.querySelector(selector);
30
+ if (element) {
31
+ obs.disconnect();
32
+ resolve(element);
33
+ }
34
+ });
35
+
36
+ observer.observe(document.body, {
37
+ childList: true,
38
+ subtree: true
39
+ });
40
+ });
41
+ },
42
+
43
+ /**
44
+ * Wait until a condition function returns true
45
+ * @param {Function} condition - Function that returns a boolean
46
+ * @param {number} interval - Check interval in milliseconds (default: 100)
47
+ * @returns {Promise} Promise that resolves when condition is met (waits indefinitely)
48
+ */
49
+ waitUntil: function(condition, interval = 100) {
50
+ return new Promise((resolve, reject) => {
51
+ const checkCondition = () => {
52
+ try {
53
+ if (condition()) {
54
+ resolve();
55
+ } else {
56
+ setTimeout(checkCondition, interval);
57
+ }
58
+ } catch (error) {
59
+ reject(error);
60
+ }
61
+ };
62
+
63
+ checkCondition();
64
+ });
65
+ },
66
+
67
+ /**
68
+ * Get a cookie value by name
69
+ * @param {string} name - Cookie name
70
+ * @returns {string|null} Cookie value or null if not found
71
+ */
72
+ getCookie: function(name) {
73
+ const value = `; ${document.cookie}`;
74
+ const parts = value.split(`; ${name}=`);
75
+ if (parts.length === 2) {
76
+ return parts.pop().split(';').shift();
77
+ }
78
+ return null;
79
+ },
80
+
81
+ /**
82
+ * Set a cookie
83
+ * @param {string} name - Cookie name
84
+ * @param {string} value - Cookie value
85
+ * @param {number} days - Number of days until expiration (default: 365)
86
+ * @param {string} path - Cookie path (default: '/')
87
+ */
88
+ setCookie: function(name, value, days = 365, path = '/') {
89
+ const expires = new Date();
90
+ expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
91
+ document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=${path}`;
92
+ },
93
+
94
+ /**
95
+ * Get a URL query parameter value
96
+ * @param {string} name - Parameter name
97
+ * @param {string} url - Optional URL (defaults to current window location)
98
+ * @returns {string|null} Parameter value or null if not found
99
+ */
100
+ getQueryParam: function(name, url = window.location.href) {
101
+ const urlObj = new URL(url);
102
+ return urlObj.searchParams.get(name);
103
+ },
104
+
105
+ /**
106
+ * Trigger a custom event
107
+ * @param {string} eventName - Event name
108
+ * @param {Object} data - Event data
109
+ * @param {Element|string} target - Target element (defaults to document)
110
+ */
111
+ triggerEvent: function(eventName, data = {}, target = document) {
112
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
113
+ if (targetEl) {
114
+ const event = new CustomEvent(eventName, { detail: data });
115
+ targetEl.dispatchEvent(event);
116
+ }
117
+ },
118
+
119
+ /**
120
+ * Select a single element
121
+ * @param {string} selector - CSS selector
122
+ * @param {Element} context - Context element (defaults to document)
123
+ * @returns {Element|null} Element or null if not found
124
+ */
125
+ select: function(selector, context = document) {
126
+ return context.querySelector(selector);
127
+ },
128
+
129
+ /**
130
+ * Select multiple elements
131
+ * @param {string} selector - CSS selector
132
+ * @param {Element} context - Context element (defaults to document)
133
+ * @returns {NodeList} NodeList of elements
134
+ */
135
+ selectAll: function(selector, context = document) {
136
+ return context.querySelectorAll(selector);
137
+ },
138
+
139
+ /**
140
+ * Add a class to an element
141
+ * @param {Element|string} element - Element or selector
142
+ * @param {string} className - Class name to add
143
+ */
144
+ addClass: function(element, className) {
145
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
146
+ if (el) el.classList.add(className);
147
+ },
148
+
149
+ /**
150
+ * Remove a class from an element
151
+ * @param {Element|string} element - Element or selector
152
+ * @param {string} className - Class name to remove
153
+ */
154
+ removeClass: function(element, className) {
155
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
156
+ if (el) el.classList.remove(className);
157
+ },
158
+
159
+ /**
160
+ * Toggle a class on an element
161
+ * @param {Element|string} element - Element or selector
162
+ * @param {string} className - Class name to toggle
163
+ */
164
+ toggleClass: function(element, className) {
165
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
166
+ if (el) el.classList.toggle(className);
167
+ },
168
+
169
+ /**
170
+ * Check if an element has a class
171
+ * @param {Element|string} element - Element or selector
172
+ * @param {string} className - Class name to check
173
+ * @returns {boolean} True if element has the class
174
+ */
175
+ hasClass: function(element, className) {
176
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
177
+ return el ? el.classList.contains(className) : false;
178
+ },
179
+
180
+ /**
181
+ * Add an event listener
182
+ * @param {Element|string} element - Element or selector
183
+ * @param {string} event - Event name
184
+ * @param {Function} handler - Event handler
185
+ */
186
+ on: function(element, event, handler) {
187
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
188
+ if (el) el.addEventListener(event, handler);
189
+ },
190
+
191
+ /**
192
+ * Remove an event listener
193
+ * @param {Element|string} element - Element or selector
194
+ * @param {string} event - Event name
195
+ * @param {Function} handler - Event handler
196
+ */
197
+ off: function(element, event, handler) {
198
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
199
+ if (el) el.removeEventListener(event, handler);
200
+ },
201
+
202
+ /**
203
+ * Event delegation - attach event to parent, handle on children
204
+ * @param {Element|string} parent - Parent element or selector
205
+ * @param {string} selector - Child selector to match
206
+ * @param {string} event - Event name
207
+ * @param {Function} handler - Event handler
208
+ */
209
+ delegate: function(parent, selector, event, handler) {
210
+ const parentEl = typeof parent === 'string' ? document.querySelector(parent) : parent;
211
+ if (parentEl) {
212
+ parentEl.addEventListener(event, (e) => {
213
+ if (e.target.matches(selector) || e.target.closest(selector)) {
214
+ handler(e);
215
+ }
216
+ });
217
+ }
218
+ },
219
+
220
+ /**
221
+ * Get the viewport dimensions
222
+ * @returns {Object} Object with width and height
223
+ */
224
+ getViewport: function() {
225
+ return {
226
+ width: window.innerWidth || document.documentElement.clientWidth,
227
+ height: window.innerHeight || document.documentElement.clientHeight
228
+ };
229
+ },
230
+
231
+ /**
232
+ * Check if element is in viewport
233
+ * @param {Element|string} element - Element or selector
234
+ * @param {number} threshold - Visibility threshold (0-1, default: 0)
235
+ * @returns {boolean} True if element is in viewport
236
+ */
237
+ isInViewport: function(element, threshold = 0) {
238
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
239
+ if (!el) return false;
240
+
241
+ const rect = el.getBoundingClientRect();
242
+ const viewport = this.getViewport();
243
+
244
+ return (
245
+ rect.top >= -threshold * rect.height &&
246
+ rect.left >= -threshold * rect.width &&
247
+ rect.bottom <= viewport.height + threshold * rect.height &&
248
+ rect.right <= viewport.width + threshold * rect.width
249
+ );
250
+ },
251
+
252
+ /**
253
+ * Scroll element into view
254
+ * @param {Element|string} element - Element or selector
255
+ * @param {Object} options - ScrollIntoView options
256
+ */
257
+ scrollIntoView: function(element, options = { behavior: 'smooth', block: 'center' }) {
258
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
259
+ if (el) el.scrollIntoView(options);
260
+ }
261
+ };
262
+
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@briannorman9/eli-utils",
3
+ "version": "1.0.0",
4
+ "description": "Utility functions for ELI web experiments",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": "./index.js"
9
+ },
10
+ "keywords": [
11
+ "eli",
12
+ "utils",
13
+ "web-experiments",
14
+ "utilities"
15
+ ],
16
+ "author": "",
17
+ "license": "MIT",
18
+ "engines": {
19
+ "node": ">=12.0.0"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/briannorman/eli.git",
24
+ "directory": "packages/eli-utils"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ }
29
+ }
30
+