@diabolic/pointy 1.0.1 → 1.0.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.
- package/README.md +44 -4
- package/dist/pointy.esm.js +114 -19
- package/dist/pointy.js +114 -19
- package/dist/pointy.min.js +1 -1
- package/dist/pointy.min.js.map +1 -1
- package/package.json +1 -1
- package/src/pointy.js +113 -18
package/README.md
CHANGED
|
@@ -137,8 +137,10 @@ pointy.reset(); // Reset to initial position
|
|
|
137
137
|
### Content
|
|
138
138
|
|
|
139
139
|
```javascript
|
|
140
|
-
pointy.
|
|
141
|
-
pointy.
|
|
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
|
-
| `
|
|
263
|
-
| `
|
|
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
|
package/dist/pointy.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pointy - A lightweight tooltip library with animated pointer
|
|
3
|
-
* @version 1.0.
|
|
3
|
+
* @version 1.0.3
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* @options
|
|
25
25
|
* - steps {Array<{target, content, direction?, duration?}>} - Tour steps
|
|
26
26
|
* - target {string|HTMLElement} - Initial target element
|
|
27
|
+
* - content {string|string[]} - Initial content/messages (single-step use)
|
|
27
28
|
* - offsetX {number} - Horizontal offset from target (default: 20)
|
|
28
29
|
* - offsetY {number} - Vertical offset from target (default: 16)
|
|
29
30
|
* - trackingFps {number} - Position update FPS, 0 = unlimited (default: 60)
|
|
@@ -78,9 +79,9 @@
|
|
|
78
79
|
* - introAnimationEnd: Initial fade-in animation completed
|
|
79
80
|
*
|
|
80
81
|
* Content:
|
|
81
|
-
* -
|
|
82
|
-
* -
|
|
83
|
-
* - messageChange: Message changed (
|
|
82
|
+
* - messagesSet: Messages array replaced via setMessages()
|
|
83
|
+
* - messageUpdate: Single message updated via setMessage()
|
|
84
|
+
* - messageChange: Message changed (navigation or auto-cycle)
|
|
84
85
|
*
|
|
85
86
|
* Message Cycle:
|
|
86
87
|
* - messageCycleStart: Auto message cycling started
|
|
@@ -131,7 +132,7 @@
|
|
|
131
132
|
* Core: show(), hide(), destroy()
|
|
132
133
|
* Navigation: next(), prev(), goToStep(index), reset(), restart()
|
|
133
134
|
* Custom Target: pointTo(target, content?, direction?)
|
|
134
|
-
* Content:
|
|
135
|
+
* Content: setMessages(content), setMessage(msg), nextMessage(), prevMessage(), goToMessage(index)
|
|
135
136
|
* Message Cycle: startMessageCycle(interval?), stopMessageCycle(), pauseMessageCycle(), resumeMessageCycle()
|
|
136
137
|
* Autoplay: startAutoplay(), stopAutoplay(), pauseAutoplay(), resumeAutoplay()
|
|
137
138
|
* Animation: animateToInitialPosition()
|
|
@@ -506,6 +507,8 @@ class Pointy {
|
|
|
506
507
|
|
|
507
508
|
this.bubble = document.createElement('div');
|
|
508
509
|
this.bubble.className = this.classNames.bubble;
|
|
510
|
+
// Set initial bubble position for pointing up (default)
|
|
511
|
+
this.bubble.style.transform = 'translateY(28px)';
|
|
509
512
|
|
|
510
513
|
this.bubbleText = document.createElement('span');
|
|
511
514
|
this.bubbleText.className = this.classNames.bubbleText;
|
|
@@ -695,7 +698,7 @@ class Pointy {
|
|
|
695
698
|
} else {
|
|
696
699
|
// Default: pointing up
|
|
697
700
|
this.pointer.style.transform = 'rotate(0deg)';
|
|
698
|
-
this.bubble.style.transform = 'translateY(
|
|
701
|
+
this.bubble.style.transform = 'translateY(28px)';
|
|
699
702
|
}
|
|
700
703
|
|
|
701
704
|
this.container.style.display = 'flex';
|
|
@@ -718,9 +721,17 @@ class Pointy {
|
|
|
718
721
|
|
|
719
722
|
this._startTracking();
|
|
720
723
|
|
|
721
|
-
// Show bubble immediately with fade
|
|
724
|
+
// Show bubble immediately with fade (only if content is not empty)
|
|
725
|
+
const hasContent = this.currentMessages.length > 0 &&
|
|
726
|
+
this.currentMessages.some(m => m !== '' && m !== null && m !== undefined);
|
|
727
|
+
|
|
722
728
|
this.bubble.style.transition = `opacity ${this.bubbleFadeDuration}ms ease`;
|
|
723
|
-
|
|
729
|
+
if (hasContent) {
|
|
730
|
+
this.bubble.style.opacity = '1';
|
|
731
|
+
} else {
|
|
732
|
+
this.bubble.style.opacity = '0';
|
|
733
|
+
this.bubble.style.pointerEvents = 'none';
|
|
734
|
+
}
|
|
724
735
|
|
|
725
736
|
// Re-enable transitions after bubble fade completes
|
|
726
737
|
setTimeout(() => {
|
|
@@ -749,9 +760,18 @@ class Pointy {
|
|
|
749
760
|
this._startTracking();
|
|
750
761
|
|
|
751
762
|
// Show bubble with fade after arriving at first target
|
|
763
|
+
// Show bubble with fade after arriving at first target (only if content is not empty)
|
|
752
764
|
setTimeout(() => {
|
|
765
|
+
const hasContent = this.currentMessages.length > 0 &&
|
|
766
|
+
this.currentMessages.some(m => m !== '' && m !== null && m !== undefined);
|
|
767
|
+
|
|
753
768
|
this.bubble.style.transition = '';
|
|
754
|
-
|
|
769
|
+
if (hasContent) {
|
|
770
|
+
this.bubble.style.opacity = '1';
|
|
771
|
+
} else {
|
|
772
|
+
this.bubble.style.opacity = '0';
|
|
773
|
+
this.bubble.style.pointerEvents = 'none';
|
|
774
|
+
}
|
|
755
775
|
|
|
756
776
|
// Start message cycle if multi-message
|
|
757
777
|
if (this.messageInterval && this.currentMessages.length > 1 && !this._messageIntervalId) {
|
|
@@ -1003,6 +1023,48 @@ class Pointy {
|
|
|
1003
1023
|
}
|
|
1004
1024
|
|
|
1005
1025
|
updateContent(newContent, animate = true) {
|
|
1026
|
+
// Check if content is empty
|
|
1027
|
+
const isEmpty = newContent === '' || newContent === null || newContent === undefined ||
|
|
1028
|
+
(Array.isArray(newContent) && newContent.length === 0) ||
|
|
1029
|
+
(Array.isArray(newContent) && newContent.every(m => m === '' || m === null || m === undefined));
|
|
1030
|
+
|
|
1031
|
+
if (isEmpty) {
|
|
1032
|
+
// Hide bubble when content is empty
|
|
1033
|
+
this.bubble.style.opacity = '0';
|
|
1034
|
+
this.bubble.style.pointerEvents = 'none';
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// Track if bubble was hidden (needs special handling)
|
|
1039
|
+
const wasHidden = this.bubble.style.opacity === '0';
|
|
1040
|
+
|
|
1041
|
+
// Show bubble if it was hidden - need to make visible BEFORE measuring
|
|
1042
|
+
if (wasHidden && this.isVisible) {
|
|
1043
|
+
// Temporarily disable ALL transitions for instant position update
|
|
1044
|
+
const oldBubbleTransition = this.bubble.style.transition;
|
|
1045
|
+
const oldPointerTransition = this.pointer.style.transition;
|
|
1046
|
+
this.bubble.style.transition = 'none';
|
|
1047
|
+
this.pointer.style.transition = 'none';
|
|
1048
|
+
|
|
1049
|
+
this.bubble.style.opacity = '1';
|
|
1050
|
+
this.bubble.style.pointerEvents = '';
|
|
1051
|
+
|
|
1052
|
+
// Force reflow to apply opacity
|
|
1053
|
+
this.bubble.offsetHeight;
|
|
1054
|
+
|
|
1055
|
+
// Update position with bubble visible (so offsetHeight works)
|
|
1056
|
+
this.updatePosition();
|
|
1057
|
+
|
|
1058
|
+
// Force another reflow to apply position
|
|
1059
|
+
this.bubble.offsetHeight;
|
|
1060
|
+
|
|
1061
|
+
// Re-enable transitions after a frame
|
|
1062
|
+
requestAnimationFrame(() => {
|
|
1063
|
+
this.bubble.style.transition = oldBubbleTransition;
|
|
1064
|
+
this.pointer.style.transition = oldPointerTransition;
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1006
1068
|
// Skip if content is the same (only for string content)
|
|
1007
1069
|
if (typeof newContent === 'string' && this.bubbleText.innerHTML === newContent) {
|
|
1008
1070
|
return;
|
|
@@ -1024,7 +1086,7 @@ class Pointy {
|
|
|
1024
1086
|
* @param {boolean} fromStepChange - Whether this is from a step change (internal)
|
|
1025
1087
|
* @private
|
|
1026
1088
|
*/
|
|
1027
|
-
|
|
1089
|
+
_applyMessages(content, fromStepChange = false) {
|
|
1028
1090
|
// Check if cycle was running before
|
|
1029
1091
|
const wasRunning = this._messageIntervalId !== null;
|
|
1030
1092
|
|
|
@@ -1038,8 +1100,8 @@ class Pointy {
|
|
|
1038
1100
|
// Show first message
|
|
1039
1101
|
this.updateContent(this.currentMessages[0]);
|
|
1040
1102
|
|
|
1041
|
-
// Only auto-start cycle on step changes, not on manual
|
|
1042
|
-
// For manual
|
|
1103
|
+
// Only auto-start cycle on step changes, not on manual setMessages
|
|
1104
|
+
// For manual setMessages, user must call resumeMessageCycle()
|
|
1043
1105
|
if (fromStepChange && this.messageInterval && this.currentMessages.length > 1) {
|
|
1044
1106
|
this._startMessageCycle();
|
|
1045
1107
|
} else if (wasRunning && this.currentMessages.length > 1) {
|
|
@@ -1249,16 +1311,41 @@ class Pointy {
|
|
|
1249
1311
|
}
|
|
1250
1312
|
|
|
1251
1313
|
/**
|
|
1252
|
-
* Set
|
|
1314
|
+
* Set/update the current message (at current index)
|
|
1315
|
+
* @param {string} message - New message content
|
|
1316
|
+
* @param {boolean} animate - Whether to animate the change (default: true)
|
|
1317
|
+
*/
|
|
1318
|
+
setMessage(message, animate = true) {
|
|
1319
|
+
const oldMessage = this.currentMessages[this.currentMessageIndex];
|
|
1320
|
+
this.currentMessages[this.currentMessageIndex] = message;
|
|
1321
|
+
|
|
1322
|
+
this.updateContent(message, animate);
|
|
1323
|
+
|
|
1324
|
+
// Ensure position is updated immediately for non-animated changes
|
|
1325
|
+
if (!animate) {
|
|
1326
|
+
this.updatePosition();
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
this._emit('messageUpdate', {
|
|
1330
|
+
index: this.currentMessageIndex,
|
|
1331
|
+
message: message,
|
|
1332
|
+
oldMessage: oldMessage,
|
|
1333
|
+
total: this.currentMessages.length,
|
|
1334
|
+
animated: animate
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* Set messages programmatically (replaces current messages)
|
|
1253
1340
|
* @param {string|string[]} content - Single message or array of messages
|
|
1254
1341
|
* @param {boolean} animate - Whether to animate the change (default: true)
|
|
1255
1342
|
*/
|
|
1256
|
-
|
|
1343
|
+
setMessages(content, animate = true) {
|
|
1257
1344
|
// Check if cycle was running before
|
|
1258
1345
|
const wasRunning = this._messageIntervalId !== null;
|
|
1259
1346
|
|
|
1260
1347
|
if (animate) {
|
|
1261
|
-
this.
|
|
1348
|
+
this._applyMessages(content, false); // false = not from step change
|
|
1262
1349
|
} else {
|
|
1263
1350
|
// Stop any existing auto-cycle
|
|
1264
1351
|
this._stopMessageCycle();
|
|
@@ -1277,7 +1364,7 @@ class Pointy {
|
|
|
1277
1364
|
}
|
|
1278
1365
|
}
|
|
1279
1366
|
|
|
1280
|
-
this._emit('
|
|
1367
|
+
this._emit('messagesSet', {
|
|
1281
1368
|
messages: this.currentMessages,
|
|
1282
1369
|
total: this.currentMessages.length,
|
|
1283
1370
|
animated: animate,
|
|
@@ -1636,6 +1723,10 @@ class Pointy {
|
|
|
1636
1723
|
// Set direction: step.direction can be 'up', 'down', or undefined (auto)
|
|
1637
1724
|
this.manualDirection = step.direction || null;
|
|
1638
1725
|
|
|
1726
|
+
// Reset velocity tracking for new target
|
|
1727
|
+
this._targetYHistory = [];
|
|
1728
|
+
this.lastTargetY = null;
|
|
1729
|
+
|
|
1639
1730
|
// Pause floating animation during movement
|
|
1640
1731
|
this.container.classList.add(this.classNames.moving);
|
|
1641
1732
|
if (this.moveTimeout) clearTimeout(this.moveTimeout);
|
|
@@ -1663,7 +1754,7 @@ class Pointy {
|
|
|
1663
1754
|
|
|
1664
1755
|
this._emit('move', { index: index, step: step });
|
|
1665
1756
|
|
|
1666
|
-
this.
|
|
1757
|
+
this._applyMessages(step.content, true); // true = from step change, auto-start cycle
|
|
1667
1758
|
this.targetElement = Pointy.getTargetElement(step.target);
|
|
1668
1759
|
this.updatePosition();
|
|
1669
1760
|
|
|
@@ -1956,6 +2047,10 @@ class Pointy {
|
|
|
1956
2047
|
// Set manual direction (null means auto)
|
|
1957
2048
|
this.manualDirection = direction || null;
|
|
1958
2049
|
|
|
2050
|
+
// Reset velocity tracking for new target
|
|
2051
|
+
this._targetYHistory = [];
|
|
2052
|
+
this.lastTargetY = null;
|
|
2053
|
+
|
|
1959
2054
|
// Pause floating animation during movement
|
|
1960
2055
|
this.container.classList.add(this.classNames.moving);
|
|
1961
2056
|
if (this.moveTimeout) clearTimeout(this.moveTimeout);
|
|
@@ -1972,7 +2067,7 @@ class Pointy {
|
|
|
1972
2067
|
this.targetElement = toTarget;
|
|
1973
2068
|
|
|
1974
2069
|
if (content !== undefined) {
|
|
1975
|
-
this.
|
|
2070
|
+
this._applyMessages(content, false); // false = not from step change, don't auto-start cycle
|
|
1976
2071
|
}
|
|
1977
2072
|
|
|
1978
2073
|
this.updatePosition();
|
|
@@ -2038,7 +2133,7 @@ class Pointy {
|
|
|
2038
2133
|
* Content:
|
|
2039
2134
|
* - messagesSet: When messages array is set for a step
|
|
2040
2135
|
* - messageChange: When current message changes (next/prev message) - includes isAuto flag
|
|
2041
|
-
* -
|
|
2136
|
+
* - messagesSet: When setMessages() is called
|
|
2042
2137
|
* - messageCycleStart: When auto message cycling starts
|
|
2043
2138
|
* - messageCycleStop: When auto message cycling stops
|
|
2044
2139
|
* - 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.
|
|
3
|
+
* @version 1.0.3
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
* @options
|
|
31
31
|
* - steps {Array<{target, content, direction?, duration?}>} - Tour steps
|
|
32
32
|
* - target {string|HTMLElement} - Initial target element
|
|
33
|
+
* - content {string|string[]} - Initial content/messages (single-step use)
|
|
33
34
|
* - offsetX {number} - Horizontal offset from target (default: 20)
|
|
34
35
|
* - offsetY {number} - Vertical offset from target (default: 16)
|
|
35
36
|
* - trackingFps {number} - Position update FPS, 0 = unlimited (default: 60)
|
|
@@ -84,9 +85,9 @@
|
|
|
84
85
|
* - introAnimationEnd: Initial fade-in animation completed
|
|
85
86
|
*
|
|
86
87
|
* Content:
|
|
87
|
-
* -
|
|
88
|
-
* -
|
|
89
|
-
* - messageChange: Message changed (
|
|
88
|
+
* - messagesSet: Messages array replaced via setMessages()
|
|
89
|
+
* - messageUpdate: Single message updated via setMessage()
|
|
90
|
+
* - messageChange: Message changed (navigation or auto-cycle)
|
|
90
91
|
*
|
|
91
92
|
* Message Cycle:
|
|
92
93
|
* - messageCycleStart: Auto message cycling started
|
|
@@ -137,7 +138,7 @@
|
|
|
137
138
|
* Core: show(), hide(), destroy()
|
|
138
139
|
* Navigation: next(), prev(), goToStep(index), reset(), restart()
|
|
139
140
|
* Custom Target: pointTo(target, content?, direction?)
|
|
140
|
-
* Content:
|
|
141
|
+
* Content: setMessages(content), setMessage(msg), nextMessage(), prevMessage(), goToMessage(index)
|
|
141
142
|
* Message Cycle: startMessageCycle(interval?), stopMessageCycle(), pauseMessageCycle(), resumeMessageCycle()
|
|
142
143
|
* Autoplay: startAutoplay(), stopAutoplay(), pauseAutoplay(), resumeAutoplay()
|
|
143
144
|
* Animation: animateToInitialPosition()
|
|
@@ -512,6 +513,8 @@
|
|
|
512
513
|
|
|
513
514
|
this.bubble = document.createElement('div');
|
|
514
515
|
this.bubble.className = this.classNames.bubble;
|
|
516
|
+
// Set initial bubble position for pointing up (default)
|
|
517
|
+
this.bubble.style.transform = 'translateY(28px)';
|
|
515
518
|
|
|
516
519
|
this.bubbleText = document.createElement('span');
|
|
517
520
|
this.bubbleText.className = this.classNames.bubbleText;
|
|
@@ -701,7 +704,7 @@
|
|
|
701
704
|
} else {
|
|
702
705
|
// Default: pointing up
|
|
703
706
|
this.pointer.style.transform = 'rotate(0deg)';
|
|
704
|
-
this.bubble.style.transform = 'translateY(
|
|
707
|
+
this.bubble.style.transform = 'translateY(28px)';
|
|
705
708
|
}
|
|
706
709
|
|
|
707
710
|
this.container.style.display = 'flex';
|
|
@@ -724,9 +727,17 @@
|
|
|
724
727
|
|
|
725
728
|
this._startTracking();
|
|
726
729
|
|
|
727
|
-
// Show bubble immediately with fade
|
|
730
|
+
// Show bubble immediately with fade (only if content is not empty)
|
|
731
|
+
const hasContent = this.currentMessages.length > 0 &&
|
|
732
|
+
this.currentMessages.some(m => m !== '' && m !== null && m !== undefined);
|
|
733
|
+
|
|
728
734
|
this.bubble.style.transition = `opacity ${this.bubbleFadeDuration}ms ease`;
|
|
729
|
-
|
|
735
|
+
if (hasContent) {
|
|
736
|
+
this.bubble.style.opacity = '1';
|
|
737
|
+
} else {
|
|
738
|
+
this.bubble.style.opacity = '0';
|
|
739
|
+
this.bubble.style.pointerEvents = 'none';
|
|
740
|
+
}
|
|
730
741
|
|
|
731
742
|
// Re-enable transitions after bubble fade completes
|
|
732
743
|
setTimeout(() => {
|
|
@@ -755,9 +766,18 @@
|
|
|
755
766
|
this._startTracking();
|
|
756
767
|
|
|
757
768
|
// Show bubble with fade after arriving at first target
|
|
769
|
+
// Show bubble with fade after arriving at first target (only if content is not empty)
|
|
758
770
|
setTimeout(() => {
|
|
771
|
+
const hasContent = this.currentMessages.length > 0 &&
|
|
772
|
+
this.currentMessages.some(m => m !== '' && m !== null && m !== undefined);
|
|
773
|
+
|
|
759
774
|
this.bubble.style.transition = '';
|
|
760
|
-
|
|
775
|
+
if (hasContent) {
|
|
776
|
+
this.bubble.style.opacity = '1';
|
|
777
|
+
} else {
|
|
778
|
+
this.bubble.style.opacity = '0';
|
|
779
|
+
this.bubble.style.pointerEvents = 'none';
|
|
780
|
+
}
|
|
761
781
|
|
|
762
782
|
// Start message cycle if multi-message
|
|
763
783
|
if (this.messageInterval && this.currentMessages.length > 1 && !this._messageIntervalId) {
|
|
@@ -1009,6 +1029,48 @@
|
|
|
1009
1029
|
}
|
|
1010
1030
|
|
|
1011
1031
|
updateContent(newContent, animate = true) {
|
|
1032
|
+
// Check if content is empty
|
|
1033
|
+
const isEmpty = newContent === '' || newContent === null || newContent === undefined ||
|
|
1034
|
+
(Array.isArray(newContent) && newContent.length === 0) ||
|
|
1035
|
+
(Array.isArray(newContent) && newContent.every(m => m === '' || m === null || m === undefined));
|
|
1036
|
+
|
|
1037
|
+
if (isEmpty) {
|
|
1038
|
+
// Hide bubble when content is empty
|
|
1039
|
+
this.bubble.style.opacity = '0';
|
|
1040
|
+
this.bubble.style.pointerEvents = 'none';
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// Track if bubble was hidden (needs special handling)
|
|
1045
|
+
const wasHidden = this.bubble.style.opacity === '0';
|
|
1046
|
+
|
|
1047
|
+
// Show bubble if it was hidden - need to make visible BEFORE measuring
|
|
1048
|
+
if (wasHidden && this.isVisible) {
|
|
1049
|
+
// Temporarily disable ALL transitions for instant position update
|
|
1050
|
+
const oldBubbleTransition = this.bubble.style.transition;
|
|
1051
|
+
const oldPointerTransition = this.pointer.style.transition;
|
|
1052
|
+
this.bubble.style.transition = 'none';
|
|
1053
|
+
this.pointer.style.transition = 'none';
|
|
1054
|
+
|
|
1055
|
+
this.bubble.style.opacity = '1';
|
|
1056
|
+
this.bubble.style.pointerEvents = '';
|
|
1057
|
+
|
|
1058
|
+
// Force reflow to apply opacity
|
|
1059
|
+
this.bubble.offsetHeight;
|
|
1060
|
+
|
|
1061
|
+
// Update position with bubble visible (so offsetHeight works)
|
|
1062
|
+
this.updatePosition();
|
|
1063
|
+
|
|
1064
|
+
// Force another reflow to apply position
|
|
1065
|
+
this.bubble.offsetHeight;
|
|
1066
|
+
|
|
1067
|
+
// Re-enable transitions after a frame
|
|
1068
|
+
requestAnimationFrame(() => {
|
|
1069
|
+
this.bubble.style.transition = oldBubbleTransition;
|
|
1070
|
+
this.pointer.style.transition = oldPointerTransition;
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1012
1074
|
// Skip if content is the same (only for string content)
|
|
1013
1075
|
if (typeof newContent === 'string' && this.bubbleText.innerHTML === newContent) {
|
|
1014
1076
|
return;
|
|
@@ -1030,7 +1092,7 @@
|
|
|
1030
1092
|
* @param {boolean} fromStepChange - Whether this is from a step change (internal)
|
|
1031
1093
|
* @private
|
|
1032
1094
|
*/
|
|
1033
|
-
|
|
1095
|
+
_applyMessages(content, fromStepChange = false) {
|
|
1034
1096
|
// Check if cycle was running before
|
|
1035
1097
|
const wasRunning = this._messageIntervalId !== null;
|
|
1036
1098
|
|
|
@@ -1044,8 +1106,8 @@
|
|
|
1044
1106
|
// Show first message
|
|
1045
1107
|
this.updateContent(this.currentMessages[0]);
|
|
1046
1108
|
|
|
1047
|
-
// Only auto-start cycle on step changes, not on manual
|
|
1048
|
-
// For manual
|
|
1109
|
+
// Only auto-start cycle on step changes, not on manual setMessages
|
|
1110
|
+
// For manual setMessages, user must call resumeMessageCycle()
|
|
1049
1111
|
if (fromStepChange && this.messageInterval && this.currentMessages.length > 1) {
|
|
1050
1112
|
this._startMessageCycle();
|
|
1051
1113
|
} else if (wasRunning && this.currentMessages.length > 1) {
|
|
@@ -1255,16 +1317,41 @@
|
|
|
1255
1317
|
}
|
|
1256
1318
|
|
|
1257
1319
|
/**
|
|
1258
|
-
* Set
|
|
1320
|
+
* Set/update the current message (at current index)
|
|
1321
|
+
* @param {string} message - New message content
|
|
1322
|
+
* @param {boolean} animate - Whether to animate the change (default: true)
|
|
1323
|
+
*/
|
|
1324
|
+
setMessage(message, animate = true) {
|
|
1325
|
+
const oldMessage = this.currentMessages[this.currentMessageIndex];
|
|
1326
|
+
this.currentMessages[this.currentMessageIndex] = message;
|
|
1327
|
+
|
|
1328
|
+
this.updateContent(message, animate);
|
|
1329
|
+
|
|
1330
|
+
// Ensure position is updated immediately for non-animated changes
|
|
1331
|
+
if (!animate) {
|
|
1332
|
+
this.updatePosition();
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
this._emit('messageUpdate', {
|
|
1336
|
+
index: this.currentMessageIndex,
|
|
1337
|
+
message: message,
|
|
1338
|
+
oldMessage: oldMessage,
|
|
1339
|
+
total: this.currentMessages.length,
|
|
1340
|
+
animated: animate
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* Set messages programmatically (replaces current messages)
|
|
1259
1346
|
* @param {string|string[]} content - Single message or array of messages
|
|
1260
1347
|
* @param {boolean} animate - Whether to animate the change (default: true)
|
|
1261
1348
|
*/
|
|
1262
|
-
|
|
1349
|
+
setMessages(content, animate = true) {
|
|
1263
1350
|
// Check if cycle was running before
|
|
1264
1351
|
const wasRunning = this._messageIntervalId !== null;
|
|
1265
1352
|
|
|
1266
1353
|
if (animate) {
|
|
1267
|
-
this.
|
|
1354
|
+
this._applyMessages(content, false); // false = not from step change
|
|
1268
1355
|
} else {
|
|
1269
1356
|
// Stop any existing auto-cycle
|
|
1270
1357
|
this._stopMessageCycle();
|
|
@@ -1283,7 +1370,7 @@
|
|
|
1283
1370
|
}
|
|
1284
1371
|
}
|
|
1285
1372
|
|
|
1286
|
-
this._emit('
|
|
1373
|
+
this._emit('messagesSet', {
|
|
1287
1374
|
messages: this.currentMessages,
|
|
1288
1375
|
total: this.currentMessages.length,
|
|
1289
1376
|
animated: animate,
|
|
@@ -1642,6 +1729,10 @@
|
|
|
1642
1729
|
// Set direction: step.direction can be 'up', 'down', or undefined (auto)
|
|
1643
1730
|
this.manualDirection = step.direction || null;
|
|
1644
1731
|
|
|
1732
|
+
// Reset velocity tracking for new target
|
|
1733
|
+
this._targetYHistory = [];
|
|
1734
|
+
this.lastTargetY = null;
|
|
1735
|
+
|
|
1645
1736
|
// Pause floating animation during movement
|
|
1646
1737
|
this.container.classList.add(this.classNames.moving);
|
|
1647
1738
|
if (this.moveTimeout) clearTimeout(this.moveTimeout);
|
|
@@ -1669,7 +1760,7 @@
|
|
|
1669
1760
|
|
|
1670
1761
|
this._emit('move', { index: index, step: step });
|
|
1671
1762
|
|
|
1672
|
-
this.
|
|
1763
|
+
this._applyMessages(step.content, true); // true = from step change, auto-start cycle
|
|
1673
1764
|
this.targetElement = Pointy.getTargetElement(step.target);
|
|
1674
1765
|
this.updatePosition();
|
|
1675
1766
|
|
|
@@ -1962,6 +2053,10 @@
|
|
|
1962
2053
|
// Set manual direction (null means auto)
|
|
1963
2054
|
this.manualDirection = direction || null;
|
|
1964
2055
|
|
|
2056
|
+
// Reset velocity tracking for new target
|
|
2057
|
+
this._targetYHistory = [];
|
|
2058
|
+
this.lastTargetY = null;
|
|
2059
|
+
|
|
1965
2060
|
// Pause floating animation during movement
|
|
1966
2061
|
this.container.classList.add(this.classNames.moving);
|
|
1967
2062
|
if (this.moveTimeout) clearTimeout(this.moveTimeout);
|
|
@@ -1978,7 +2073,7 @@
|
|
|
1978
2073
|
this.targetElement = toTarget;
|
|
1979
2074
|
|
|
1980
2075
|
if (content !== undefined) {
|
|
1981
|
-
this.
|
|
2076
|
+
this._applyMessages(content, false); // false = not from step change, don't auto-start cycle
|
|
1982
2077
|
}
|
|
1983
2078
|
|
|
1984
2079
|
this.updatePosition();
|
|
@@ -2044,7 +2139,7 @@
|
|
|
2044
2139
|
* Content:
|
|
2045
2140
|
* - messagesSet: When messages array is set for a step
|
|
2046
2141
|
* - messageChange: When current message changes (next/prev message) - includes isAuto flag
|
|
2047
|
-
* -
|
|
2142
|
+
* - messagesSet: When setMessages() is called
|
|
2048
2143
|
* - messageCycleStart: When auto message cycling starts
|
|
2049
2144
|
* - messageCycleStop: When auto message cycling stops
|
|
2050
2145
|
* - messageCyclePause: When message cycling is paused
|