@diabolic/pointy 1.0.1 → 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 CHANGED
@@ -137,8 +137,10 @@ pointy.reset(); // Reset to initial position
137
137
  ### Content
138
138
 
139
139
  ```javascript
140
- pointy.setContent('New message');
141
- pointy.setContent(['Message 1', 'Message 2']);
140
+ pointy.setMessages('New message'); // Replace all messages
141
+ pointy.setMessages(['Message 1', 'Message 2']); // Set multiple messages
142
+
143
+ pointy.setMessage('Updated text'); // Update current message only
142
144
 
143
145
  pointy.nextMessage();
144
146
  pointy.prevMessage();
@@ -171,6 +173,44 @@ pointy.pauseMessageCycle();
171
173
  pointy.resumeMessageCycle();
172
174
  ```
173
175
 
176
+ ### Setters
177
+
178
+ All setters emit corresponding `*Change` events:
179
+
180
+ ```javascript
181
+ // Animation
182
+ pointy.setEasing('bounce');
183
+ pointy.setAnimationDuration(800);
184
+ pointy.setIntroFadeDuration(500);
185
+ pointy.setBubbleFadeDuration(300);
186
+ pointy.setMessageTransitionDuration(400);
187
+ pointy.setFloatingAnimation(true);
188
+
189
+ // Position
190
+ pointy.setOffset(30, 20);
191
+ pointy.setInitialPosition('top-left');
192
+ pointy.setInitialPositionOffset(50);
193
+
194
+ // Tracking
195
+ pointy.setTracking(true);
196
+ pointy.setTrackingFps(30);
197
+
198
+ // Messages
199
+ pointy.setMessageInterval(2000);
200
+
201
+ // Autoplay
202
+ pointy.setAutoplayInterval(3000);
203
+ pointy.setAutoplayWaitForMessages(true);
204
+
205
+ // Completion
206
+ pointy.setResetOnComplete(true);
207
+ pointy.setHideOnComplete(true);
208
+ pointy.setHideOnCompleteDelay(500);
209
+
210
+ // Styling
211
+ pointy.setPointerSvg('<svg>...</svg>');
212
+ ```
213
+
174
214
  ## Easing Presets
175
215
 
176
216
  ```javascript
@@ -259,8 +299,8 @@ pointy.on('all', (data) => {
259
299
  #### Content
260
300
  | Event | Data |
261
301
  |-------|------|
262
- | `contentSet` | `{ messages, total, animated, cyclePaused }` |
263
- | `messagesSet` | `{ messages, total, cyclePaused }` |
302
+ | `messagesSet` | `{ messages, total, animated, cyclePaused }` |
303
+ | `messageUpdate` | `{ index, message, oldMessage, total, animated }` |
264
304
  | `messageChange` | `{ fromIndex, toIndex, message, total, isAuto? }` |
265
305
 
266
306
  #### Message Cycle
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Pointy - A lightweight tooltip library with animated pointer
3
- * @version 1.0.1
3
+ * @version 1.0.2
4
4
  * @license MIT
5
5
  */
6
6
  /**
@@ -78,9 +78,9 @@
78
78
  * - introAnimationEnd: Initial fade-in animation completed
79
79
  *
80
80
  * Content:
81
- * - contentSet: Content updated via setContent()
82
- * - messagesSet: New messages array set for step
83
- * - messageChange: Message changed (manual or auto)
81
+ * - messagesSet: Messages array replaced via setMessages()
82
+ * - messageUpdate: Single message updated via setMessage()
83
+ * - messageChange: Message changed (navigation or auto-cycle)
84
84
  *
85
85
  * Message Cycle:
86
86
  * - messageCycleStart: Auto message cycling started
@@ -131,7 +131,7 @@
131
131
  * Core: show(), hide(), destroy()
132
132
  * Navigation: next(), prev(), goToStep(index), reset(), restart()
133
133
  * Custom Target: pointTo(target, content?, direction?)
134
- * Content: setContent(content), nextMessage(), prevMessage(), goToMessage(index)
134
+ * Content: setMessages(content), setMessage(msg), nextMessage(), prevMessage(), goToMessage(index)
135
135
  * Message Cycle: startMessageCycle(interval?), stopMessageCycle(), pauseMessageCycle(), resumeMessageCycle()
136
136
  * Autoplay: startAutoplay(), stopAutoplay(), pauseAutoplay(), resumeAutoplay()
137
137
  * Animation: animateToInitialPosition()
@@ -718,9 +718,17 @@ class Pointy {
718
718
 
719
719
  this._startTracking();
720
720
 
721
- // Show bubble immediately with fade
721
+ // Show bubble immediately with fade (only if content is not empty)
722
+ const hasContent = this.currentMessages.length > 0 &&
723
+ this.currentMessages.some(m => m !== '' && m !== null && m !== undefined);
724
+
722
725
  this.bubble.style.transition = `opacity ${this.bubbleFadeDuration}ms ease`;
723
- this.bubble.style.opacity = '1';
726
+ if (hasContent) {
727
+ this.bubble.style.opacity = '1';
728
+ } else {
729
+ this.bubble.style.opacity = '0';
730
+ this.bubble.style.pointerEvents = 'none';
731
+ }
724
732
 
725
733
  // Re-enable transitions after bubble fade completes
726
734
  setTimeout(() => {
@@ -749,9 +757,18 @@ class Pointy {
749
757
  this._startTracking();
750
758
 
751
759
  // Show bubble with fade after arriving at first target
760
+ // Show bubble with fade after arriving at first target (only if content is not empty)
752
761
  setTimeout(() => {
762
+ const hasContent = this.currentMessages.length > 0 &&
763
+ this.currentMessages.some(m => m !== '' && m !== null && m !== undefined);
764
+
753
765
  this.bubble.style.transition = '';
754
- this.bubble.style.opacity = '1';
766
+ if (hasContent) {
767
+ this.bubble.style.opacity = '1';
768
+ } else {
769
+ this.bubble.style.opacity = '0';
770
+ this.bubble.style.pointerEvents = 'none';
771
+ }
755
772
 
756
773
  // Start message cycle if multi-message
757
774
  if (this.messageInterval && this.currentMessages.length > 1 && !this._messageIntervalId) {
@@ -1003,6 +1020,24 @@ class Pointy {
1003
1020
  }
1004
1021
 
1005
1022
  updateContent(newContent, animate = true) {
1023
+ // Check if content is empty
1024
+ const isEmpty = newContent === '' || newContent === null || newContent === undefined ||
1025
+ (Array.isArray(newContent) && newContent.length === 0) ||
1026
+ (Array.isArray(newContent) && newContent.every(m => m === '' || m === null || m === undefined));
1027
+
1028
+ if (isEmpty) {
1029
+ // Hide bubble when content is empty
1030
+ this.bubble.style.opacity = '0';
1031
+ this.bubble.style.pointerEvents = 'none';
1032
+ return;
1033
+ }
1034
+
1035
+ // Show bubble if it was hidden
1036
+ if (this.bubble.style.opacity === '0' && this.isVisible) {
1037
+ this.bubble.style.opacity = '1';
1038
+ this.bubble.style.pointerEvents = '';
1039
+ }
1040
+
1006
1041
  // Skip if content is the same (only for string content)
1007
1042
  if (typeof newContent === 'string' && this.bubbleText.innerHTML === newContent) {
1008
1043
  return;
@@ -1024,7 +1059,7 @@ class Pointy {
1024
1059
  * @param {boolean} fromStepChange - Whether this is from a step change (internal)
1025
1060
  * @private
1026
1061
  */
1027
- _setMessages(content, fromStepChange = false) {
1062
+ _applyMessages(content, fromStepChange = false) {
1028
1063
  // Check if cycle was running before
1029
1064
  const wasRunning = this._messageIntervalId !== null;
1030
1065
 
@@ -1038,8 +1073,8 @@ class Pointy {
1038
1073
  // Show first message
1039
1074
  this.updateContent(this.currentMessages[0]);
1040
1075
 
1041
- // Only auto-start cycle on step changes, not on manual setContent
1042
- // For manual setContent, user must call resumeMessageCycle()
1076
+ // Only auto-start cycle on step changes, not on manual setMessages
1077
+ // For manual setMessages, user must call resumeMessageCycle()
1043
1078
  if (fromStepChange && this.messageInterval && this.currentMessages.length > 1) {
1044
1079
  this._startMessageCycle();
1045
1080
  } else if (wasRunning && this.currentMessages.length > 1) {
@@ -1249,16 +1284,36 @@ class Pointy {
1249
1284
  }
1250
1285
 
1251
1286
  /**
1252
- * Set content programmatically (replaces current messages)
1287
+ * Set/update the current message (at current index)
1288
+ * @param {string} message - New message content
1289
+ * @param {boolean} animate - Whether to animate the change (default: true)
1290
+ */
1291
+ setMessage(message, animate = true) {
1292
+ const oldMessage = this.currentMessages[this.currentMessageIndex];
1293
+ this.currentMessages[this.currentMessageIndex] = message;
1294
+
1295
+ this.updateContent(message, animate);
1296
+
1297
+ this._emit('messageUpdate', {
1298
+ index: this.currentMessageIndex,
1299
+ message: message,
1300
+ oldMessage: oldMessage,
1301
+ total: this.currentMessages.length,
1302
+ animated: animate
1303
+ });
1304
+ }
1305
+
1306
+ /**
1307
+ * Set messages programmatically (replaces current messages)
1253
1308
  * @param {string|string[]} content - Single message or array of messages
1254
1309
  * @param {boolean} animate - Whether to animate the change (default: true)
1255
1310
  */
1256
- setContent(content, animate = true) {
1311
+ setMessages(content, animate = true) {
1257
1312
  // Check if cycle was running before
1258
1313
  const wasRunning = this._messageIntervalId !== null;
1259
1314
 
1260
1315
  if (animate) {
1261
- this._setMessages(content, false); // false = not from step change
1316
+ this._applyMessages(content, false); // false = not from step change
1262
1317
  } else {
1263
1318
  // Stop any existing auto-cycle
1264
1319
  this._stopMessageCycle();
@@ -1277,7 +1332,7 @@ class Pointy {
1277
1332
  }
1278
1333
  }
1279
1334
 
1280
- this._emit('contentSet', {
1335
+ this._emit('messagesSet', {
1281
1336
  messages: this.currentMessages,
1282
1337
  total: this.currentMessages.length,
1283
1338
  animated: animate,
@@ -1636,6 +1691,10 @@ class Pointy {
1636
1691
  // Set direction: step.direction can be 'up', 'down', or undefined (auto)
1637
1692
  this.manualDirection = step.direction || null;
1638
1693
 
1694
+ // Reset velocity tracking for new target
1695
+ this._targetYHistory = [];
1696
+ this.lastTargetY = null;
1697
+
1639
1698
  // Pause floating animation during movement
1640
1699
  this.container.classList.add(this.classNames.moving);
1641
1700
  if (this.moveTimeout) clearTimeout(this.moveTimeout);
@@ -1663,7 +1722,7 @@ class Pointy {
1663
1722
 
1664
1723
  this._emit('move', { index: index, step: step });
1665
1724
 
1666
- this._setMessages(step.content, true); // true = from step change, auto-start cycle
1725
+ this._applyMessages(step.content, true); // true = from step change, auto-start cycle
1667
1726
  this.targetElement = Pointy.getTargetElement(step.target);
1668
1727
  this.updatePosition();
1669
1728
 
@@ -1956,6 +2015,10 @@ class Pointy {
1956
2015
  // Set manual direction (null means auto)
1957
2016
  this.manualDirection = direction || null;
1958
2017
 
2018
+ // Reset velocity tracking for new target
2019
+ this._targetYHistory = [];
2020
+ this.lastTargetY = null;
2021
+
1959
2022
  // Pause floating animation during movement
1960
2023
  this.container.classList.add(this.classNames.moving);
1961
2024
  if (this.moveTimeout) clearTimeout(this.moveTimeout);
@@ -1972,7 +2035,7 @@ class Pointy {
1972
2035
  this.targetElement = toTarget;
1973
2036
 
1974
2037
  if (content !== undefined) {
1975
- this._setMessages(content, false); // false = not from step change, don't auto-start cycle
2038
+ this._applyMessages(content, false); // false = not from step change, don't auto-start cycle
1976
2039
  }
1977
2040
 
1978
2041
  this.updatePosition();
@@ -2038,7 +2101,7 @@ class Pointy {
2038
2101
  * Content:
2039
2102
  * - messagesSet: When messages array is set for a step
2040
2103
  * - messageChange: When current message changes (next/prev message) - includes isAuto flag
2041
- * - contentSet: When setContent() is called
2104
+ * - messagesSet: When setMessages() is called
2042
2105
  * - messageCycleStart: When auto message cycling starts
2043
2106
  * - messageCycleStop: When auto message cycling stops
2044
2107
  * - messageCyclePause: When message cycling is paused
package/dist/pointy.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Pointy - A lightweight tooltip library with animated pointer
3
- * @version 1.0.1
3
+ * @version 1.0.2
4
4
  * @license MIT
5
5
  */
6
6
  (function (global, factory) {
@@ -84,9 +84,9 @@
84
84
  * - introAnimationEnd: Initial fade-in animation completed
85
85
  *
86
86
  * Content:
87
- * - contentSet: Content updated via setContent()
88
- * - messagesSet: New messages array set for step
89
- * - messageChange: Message changed (manual or auto)
87
+ * - messagesSet: Messages array replaced via setMessages()
88
+ * - messageUpdate: Single message updated via setMessage()
89
+ * - messageChange: Message changed (navigation or auto-cycle)
90
90
  *
91
91
  * Message Cycle:
92
92
  * - messageCycleStart: Auto message cycling started
@@ -137,7 +137,7 @@
137
137
  * Core: show(), hide(), destroy()
138
138
  * Navigation: next(), prev(), goToStep(index), reset(), restart()
139
139
  * Custom Target: pointTo(target, content?, direction?)
140
- * Content: setContent(content), nextMessage(), prevMessage(), goToMessage(index)
140
+ * Content: setMessages(content), setMessage(msg), nextMessage(), prevMessage(), goToMessage(index)
141
141
  * Message Cycle: startMessageCycle(interval?), stopMessageCycle(), pauseMessageCycle(), resumeMessageCycle()
142
142
  * Autoplay: startAutoplay(), stopAutoplay(), pauseAutoplay(), resumeAutoplay()
143
143
  * Animation: animateToInitialPosition()
@@ -724,9 +724,17 @@
724
724
 
725
725
  this._startTracking();
726
726
 
727
- // Show bubble immediately with fade
727
+ // Show bubble immediately with fade (only if content is not empty)
728
+ const hasContent = this.currentMessages.length > 0 &&
729
+ this.currentMessages.some(m => m !== '' && m !== null && m !== undefined);
730
+
728
731
  this.bubble.style.transition = `opacity ${this.bubbleFadeDuration}ms ease`;
729
- this.bubble.style.opacity = '1';
732
+ if (hasContent) {
733
+ this.bubble.style.opacity = '1';
734
+ } else {
735
+ this.bubble.style.opacity = '0';
736
+ this.bubble.style.pointerEvents = 'none';
737
+ }
730
738
 
731
739
  // Re-enable transitions after bubble fade completes
732
740
  setTimeout(() => {
@@ -755,9 +763,18 @@
755
763
  this._startTracking();
756
764
 
757
765
  // Show bubble with fade after arriving at first target
766
+ // Show bubble with fade after arriving at first target (only if content is not empty)
758
767
  setTimeout(() => {
768
+ const hasContent = this.currentMessages.length > 0 &&
769
+ this.currentMessages.some(m => m !== '' && m !== null && m !== undefined);
770
+
759
771
  this.bubble.style.transition = '';
760
- this.bubble.style.opacity = '1';
772
+ if (hasContent) {
773
+ this.bubble.style.opacity = '1';
774
+ } else {
775
+ this.bubble.style.opacity = '0';
776
+ this.bubble.style.pointerEvents = 'none';
777
+ }
761
778
 
762
779
  // Start message cycle if multi-message
763
780
  if (this.messageInterval && this.currentMessages.length > 1 && !this._messageIntervalId) {
@@ -1009,6 +1026,24 @@
1009
1026
  }
1010
1027
 
1011
1028
  updateContent(newContent, animate = true) {
1029
+ // Check if content is empty
1030
+ const isEmpty = newContent === '' || newContent === null || newContent === undefined ||
1031
+ (Array.isArray(newContent) && newContent.length === 0) ||
1032
+ (Array.isArray(newContent) && newContent.every(m => m === '' || m === null || m === undefined));
1033
+
1034
+ if (isEmpty) {
1035
+ // Hide bubble when content is empty
1036
+ this.bubble.style.opacity = '0';
1037
+ this.bubble.style.pointerEvents = 'none';
1038
+ return;
1039
+ }
1040
+
1041
+ // Show bubble if it was hidden
1042
+ if (this.bubble.style.opacity === '0' && this.isVisible) {
1043
+ this.bubble.style.opacity = '1';
1044
+ this.bubble.style.pointerEvents = '';
1045
+ }
1046
+
1012
1047
  // Skip if content is the same (only for string content)
1013
1048
  if (typeof newContent === 'string' && this.bubbleText.innerHTML === newContent) {
1014
1049
  return;
@@ -1030,7 +1065,7 @@
1030
1065
  * @param {boolean} fromStepChange - Whether this is from a step change (internal)
1031
1066
  * @private
1032
1067
  */
1033
- _setMessages(content, fromStepChange = false) {
1068
+ _applyMessages(content, fromStepChange = false) {
1034
1069
  // Check if cycle was running before
1035
1070
  const wasRunning = this._messageIntervalId !== null;
1036
1071
 
@@ -1044,8 +1079,8 @@
1044
1079
  // Show first message
1045
1080
  this.updateContent(this.currentMessages[0]);
1046
1081
 
1047
- // Only auto-start cycle on step changes, not on manual setContent
1048
- // For manual setContent, user must call resumeMessageCycle()
1082
+ // Only auto-start cycle on step changes, not on manual setMessages
1083
+ // For manual setMessages, user must call resumeMessageCycle()
1049
1084
  if (fromStepChange && this.messageInterval && this.currentMessages.length > 1) {
1050
1085
  this._startMessageCycle();
1051
1086
  } else if (wasRunning && this.currentMessages.length > 1) {
@@ -1255,16 +1290,36 @@
1255
1290
  }
1256
1291
 
1257
1292
  /**
1258
- * Set content programmatically (replaces current messages)
1293
+ * Set/update the current message (at current index)
1294
+ * @param {string} message - New message content
1295
+ * @param {boolean} animate - Whether to animate the change (default: true)
1296
+ */
1297
+ setMessage(message, animate = true) {
1298
+ const oldMessage = this.currentMessages[this.currentMessageIndex];
1299
+ this.currentMessages[this.currentMessageIndex] = message;
1300
+
1301
+ this.updateContent(message, animate);
1302
+
1303
+ this._emit('messageUpdate', {
1304
+ index: this.currentMessageIndex,
1305
+ message: message,
1306
+ oldMessage: oldMessage,
1307
+ total: this.currentMessages.length,
1308
+ animated: animate
1309
+ });
1310
+ }
1311
+
1312
+ /**
1313
+ * Set messages programmatically (replaces current messages)
1259
1314
  * @param {string|string[]} content - Single message or array of messages
1260
1315
  * @param {boolean} animate - Whether to animate the change (default: true)
1261
1316
  */
1262
- setContent(content, animate = true) {
1317
+ setMessages(content, animate = true) {
1263
1318
  // Check if cycle was running before
1264
1319
  const wasRunning = this._messageIntervalId !== null;
1265
1320
 
1266
1321
  if (animate) {
1267
- this._setMessages(content, false); // false = not from step change
1322
+ this._applyMessages(content, false); // false = not from step change
1268
1323
  } else {
1269
1324
  // Stop any existing auto-cycle
1270
1325
  this._stopMessageCycle();
@@ -1283,7 +1338,7 @@
1283
1338
  }
1284
1339
  }
1285
1340
 
1286
- this._emit('contentSet', {
1341
+ this._emit('messagesSet', {
1287
1342
  messages: this.currentMessages,
1288
1343
  total: this.currentMessages.length,
1289
1344
  animated: animate,
@@ -1642,6 +1697,10 @@
1642
1697
  // Set direction: step.direction can be 'up', 'down', or undefined (auto)
1643
1698
  this.manualDirection = step.direction || null;
1644
1699
 
1700
+ // Reset velocity tracking for new target
1701
+ this._targetYHistory = [];
1702
+ this.lastTargetY = null;
1703
+
1645
1704
  // Pause floating animation during movement
1646
1705
  this.container.classList.add(this.classNames.moving);
1647
1706
  if (this.moveTimeout) clearTimeout(this.moveTimeout);
@@ -1669,7 +1728,7 @@
1669
1728
 
1670
1729
  this._emit('move', { index: index, step: step });
1671
1730
 
1672
- this._setMessages(step.content, true); // true = from step change, auto-start cycle
1731
+ this._applyMessages(step.content, true); // true = from step change, auto-start cycle
1673
1732
  this.targetElement = Pointy.getTargetElement(step.target);
1674
1733
  this.updatePosition();
1675
1734
 
@@ -1962,6 +2021,10 @@
1962
2021
  // Set manual direction (null means auto)
1963
2022
  this.manualDirection = direction || null;
1964
2023
 
2024
+ // Reset velocity tracking for new target
2025
+ this._targetYHistory = [];
2026
+ this.lastTargetY = null;
2027
+
1965
2028
  // Pause floating animation during movement
1966
2029
  this.container.classList.add(this.classNames.moving);
1967
2030
  if (this.moveTimeout) clearTimeout(this.moveTimeout);
@@ -1978,7 +2041,7 @@
1978
2041
  this.targetElement = toTarget;
1979
2042
 
1980
2043
  if (content !== undefined) {
1981
- this._setMessages(content, false); // false = not from step change, don't auto-start cycle
2044
+ this._applyMessages(content, false); // false = not from step change, don't auto-start cycle
1982
2045
  }
1983
2046
 
1984
2047
  this.updatePosition();
@@ -2044,7 +2107,7 @@
2044
2107
  * Content:
2045
2108
  * - messagesSet: When messages array is set for a step
2046
2109
  * - messageChange: When current message changes (next/prev message) - includes isAuto flag
2047
- * - contentSet: When setContent() is called
2110
+ * - messagesSet: When setMessages() is called
2048
2111
  * - messageCycleStart: When auto message cycling starts
2049
2112
  * - messageCycleStop: When auto message cycling stops
2050
2113
  * - messageCyclePause: When message cycling is paused