@khanacademy/wonder-blocks-tooltip 1.4.3 → 1.4.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @khanacademy/wonder-blocks-tooltip
2
2
 
3
+ ## 1.4.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [496119f2]
8
+ - @khanacademy/wonder-blocks-core@4.6.2
9
+ - @khanacademy/wonder-blocks-modal@3.0.5
10
+ - @khanacademy/wonder-blocks-layout@1.4.15
11
+ - @khanacademy/wonder-blocks-typography@1.1.37
12
+
3
13
  ## 1.4.3
4
14
 
5
15
  ### Patch Changes
package/dist/index.js ADDED
@@ -0,0 +1,913 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var React = require('react');
6
+ var ReactDOM = require('react-dom');
7
+ var wonderBlocksCore = require('@khanacademy/wonder-blocks-core');
8
+ var wonderBlocksModal = require('@khanacademy/wonder-blocks-modal');
9
+ var aphrodite = require('aphrodite');
10
+ var Colors = require('@khanacademy/wonder-blocks-color');
11
+ var Spacing = require('@khanacademy/wonder-blocks-spacing');
12
+ var _extends = require('@babel/runtime/helpers/extends');
13
+ var wonderBlocksLayout = require('@khanacademy/wonder-blocks-layout');
14
+ var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
15
+ var reactPopper = require('react-popper');
16
+
17
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
18
+
19
+ function _interopNamespace(e) {
20
+ if (e && e.__esModule) return e;
21
+ var n = Object.create(null);
22
+ if (e) {
23
+ Object.keys(e).forEach(function (k) {
24
+ if (k !== 'default') {
25
+ var d = Object.getOwnPropertyDescriptor(e, k);
26
+ Object.defineProperty(n, k, d.get ? d : {
27
+ enumerable: true,
28
+ get: function () { return e[k]; }
29
+ });
30
+ }
31
+ });
32
+ }
33
+ n["default"] = e;
34
+ return Object.freeze(n);
35
+ }
36
+
37
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
38
+ var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
39
+ var Colors__default = /*#__PURE__*/_interopDefaultLegacy(Colors);
40
+ var Spacing__default = /*#__PURE__*/_interopDefaultLegacy(Spacing);
41
+ var _extends__default = /*#__PURE__*/_interopDefaultLegacy(_extends);
42
+
43
+ class ActiveTracker {
44
+ constructor() {
45
+ this._subscribers = [];
46
+ }
47
+
48
+ _getIndex(who) {
49
+ return this._subscribers.findIndex(v => v === who);
50
+ }
51
+
52
+ steal(who) {
53
+ const wasActive = !!this._active;
54
+ this._active = true;
55
+
56
+ for (const anchor of this._subscribers) {
57
+ if (anchor === who) {
58
+ continue;
59
+ }
60
+
61
+ anchor.activeStateStolen();
62
+ }
63
+
64
+ return wasActive;
65
+ }
66
+
67
+ giveup() {
68
+ this._active = false;
69
+ }
70
+
71
+ subscribe(who) {
72
+ if (this._getIndex(who) >= 0) {
73
+ throw new Error("Already subscribed.");
74
+ }
75
+
76
+ this._subscribers.push(who);
77
+
78
+ const unsubscribe = () => {
79
+ const index = this._getIndex(who);
80
+
81
+ this._subscribers.splice(index, 1);
82
+ };
83
+
84
+ return unsubscribe;
85
+ }
86
+
87
+ }
88
+
89
+ const TooltipAppearanceDelay = 100;
90
+ const TooltipDisappearanceDelay = 75;
91
+
92
+ const TRACKER = new ActiveTracker();
93
+ class TooltipAnchor extends React__namespace.Component {
94
+ constructor(props) {
95
+ super(props);
96
+
97
+ this.activeStateStolen = () => {
98
+ this._stolenFromUs = this.state.active || !!this._timeoutID;
99
+ this._focused = false;
100
+
101
+ this._setActiveState(false, true);
102
+ };
103
+
104
+ this._handleFocusIn = () => {
105
+ this._updateActiveState(this._hovered, true);
106
+ };
107
+
108
+ this._handleFocusOut = () => {
109
+ this._updateActiveState(this._hovered, false);
110
+ };
111
+
112
+ this._handleMouseEnter = () => {
113
+ this._updateActiveState(true, this._focused);
114
+ };
115
+
116
+ this._handleMouseLeave = () => {
117
+ this._updateActiveState(false, this._focused);
118
+ };
119
+
120
+ this._handleKeyUp = e => {
121
+ if (e.key === "Escape" && this.state.active) {
122
+ e.preventDefault();
123
+ e.stopPropagation();
124
+
125
+ this._updateActiveState(false, false);
126
+ }
127
+ };
128
+
129
+ this._focused = false;
130
+ this._hovered = false;
131
+ this.state = {
132
+ active: false
133
+ };
134
+ }
135
+
136
+ componentDidMount() {
137
+ const anchorNode = ReactDOM__namespace.findDOMNode(this);
138
+
139
+ if (anchorNode instanceof Text) {
140
+ throw new Error("TooltipAnchor must be applied to an Element. Text content is not supported.");
141
+ }
142
+
143
+ this._unsubscribeFromTracker = TRACKER.subscribe(this);
144
+ this._anchorNode = anchorNode;
145
+
146
+ this._updateFocusivity();
147
+
148
+ if (anchorNode) {
149
+ anchorNode.addEventListener("focusin", this._handleFocusIn);
150
+ anchorNode.addEventListener("focusout", this._handleFocusOut);
151
+ anchorNode.addEventListener("mouseenter", this._handleMouseEnter);
152
+ anchorNode.addEventListener("mouseleave", this._handleMouseLeave);
153
+ this.props.anchorRef(this._anchorNode);
154
+ }
155
+ }
156
+
157
+ componentDidUpdate(prevProps) {
158
+ if (prevProps.forceAnchorFocusivity !== this.props.forceAnchorFocusivity || prevProps.children !== this.props.children) {
159
+ this._updateFocusivity();
160
+ }
161
+ }
162
+
163
+ componentWillUnmount() {
164
+ if (this._unsubscribeFromTracker) {
165
+ this._unsubscribeFromTracker();
166
+ }
167
+
168
+ this._clearPendingAction();
169
+
170
+ const anchorNode = this._anchorNode;
171
+
172
+ if (anchorNode) {
173
+ anchorNode.removeEventListener("focusin", this._handleFocusIn);
174
+ anchorNode.removeEventListener("focusout", this._handleFocusOut);
175
+ anchorNode.removeEventListener("mouseenter", this._handleMouseEnter);
176
+ anchorNode.removeEventListener("mouseleave", this._handleMouseLeave);
177
+ }
178
+
179
+ if (this.state.active) {
180
+ document.removeEventListener("keyup", this._handleKeyUp);
181
+ }
182
+ }
183
+
184
+ _updateFocusivity() {
185
+ const anchorNode = this._anchorNode;
186
+
187
+ if (!anchorNode) {
188
+ return;
189
+ }
190
+
191
+ const {
192
+ forceAnchorFocusivity
193
+ } = this.props;
194
+ const currentTabIndex = anchorNode.getAttribute("tabindex");
195
+
196
+ if (forceAnchorFocusivity && !currentTabIndex) {
197
+ anchorNode.setAttribute("tabindex", "0");
198
+ this._weSetFocusivity = true;
199
+ } else if (!forceAnchorFocusivity && currentTabIndex) {
200
+ if (this._weSetFocusivity) {
201
+ anchorNode.removeAttribute("tabindex");
202
+ this._weSetFocusivity = false;
203
+ }
204
+ }
205
+ }
206
+
207
+ _updateActiveState(hovered, focused) {
208
+ this._hovered = hovered;
209
+ this._focused = focused;
210
+
211
+ this._setActiveState(hovered || focused);
212
+ }
213
+
214
+ _clearPendingAction() {
215
+ if (this._timeoutID) {
216
+ clearTimeout(this._timeoutID);
217
+ this._timeoutID = null;
218
+ }
219
+ }
220
+
221
+ _setActiveState(active, instant) {
222
+ if (this._stolenFromUs || active !== this.state.active || !this.state.active && this._timeoutID) {
223
+ this._clearPendingAction();
224
+ } else if (active === this.state.active && !this._timeoutID) {
225
+ return;
226
+ }
227
+
228
+ instant = instant || active && TRACKER.steal(this);
229
+
230
+ if (instant) {
231
+ if (active) {
232
+ document.addEventListener("keyup", this._handleKeyUp);
233
+ } else {
234
+ document.removeEventListener("keyup", this._handleKeyUp);
235
+ }
236
+
237
+ this.setState({
238
+ active
239
+ });
240
+ this.props.onActiveChanged(active);
241
+
242
+ if (!this._stolenFromUs && !active) {
243
+ TRACKER.giveup();
244
+ }
245
+
246
+ this._stolenFromUs = false;
247
+ } else {
248
+ const delay = active ? TooltipAppearanceDelay : TooltipDisappearanceDelay;
249
+ this._timeoutID = setTimeout(() => {
250
+ this._timeoutID = null;
251
+
252
+ this._setActiveState(active, true);
253
+ }, delay);
254
+ }
255
+ }
256
+
257
+ _renderAnchorableChildren() {
258
+ const {
259
+ children
260
+ } = this.props;
261
+ return typeof children === "string" ? React__namespace.createElement(wonderBlocksCore.Text, null, children) : children;
262
+ }
263
+
264
+ _renderAccessibleChildren(ids) {
265
+ const anchorableChildren = this._renderAnchorableChildren();
266
+
267
+ return React__namespace.cloneElement(anchorableChildren, {
268
+ "aria-describedby": ids.get(TooltipAnchor.ariaContentId)
269
+ });
270
+ }
271
+
272
+ render() {
273
+ if (this.props.ids) {
274
+ return this._renderAccessibleChildren(this.props.ids);
275
+ }
276
+
277
+ return this._renderAnchorableChildren();
278
+ }
279
+
280
+ }
281
+ TooltipAnchor.defaultProps = {
282
+ forceAnchorFocusivity: true
283
+ };
284
+ TooltipAnchor.ariaContentId = "aria-content";
285
+
286
+ let tempIdCounter = 0;
287
+ class TooltipTail extends React__namespace.Component {
288
+ _calculateDimensionsFromPlacement() {
289
+ const {
290
+ placement
291
+ } = this.props;
292
+ const trimlineOffset = 0.5;
293
+
294
+ switch (placement) {
295
+ case "top":
296
+ return {
297
+ trimlinePoints: [`0,-${trimlineOffset}`, `${ARROW_WIDTH},-${trimlineOffset}`],
298
+ points: ["0,0", `${ARROW_WIDTH / 2},${ARROW_HEIGHT}`, `${ARROW_WIDTH},0`],
299
+ height: ARROW_HEIGHT,
300
+ width: ARROW_WIDTH
301
+ };
302
+
303
+ case "right":
304
+ return {
305
+ trimlinePoints: [`${ARROW_HEIGHT + trimlineOffset},0`, `${ARROW_HEIGHT + trimlineOffset},${ARROW_WIDTH}`],
306
+ points: [`${ARROW_HEIGHT},0`, `0,${ARROW_WIDTH / 2}`, `${ARROW_HEIGHT},${ARROW_WIDTH}`],
307
+ width: ARROW_HEIGHT,
308
+ height: ARROW_WIDTH
309
+ };
310
+
311
+ case "bottom":
312
+ return {
313
+ trimlinePoints: [`0, ${ARROW_HEIGHT + trimlineOffset}`, `${ARROW_WIDTH},${ARROW_HEIGHT + trimlineOffset}`],
314
+ points: [`0, ${ARROW_HEIGHT}`, `${ARROW_WIDTH / 2},0`, `${ARROW_WIDTH},${ARROW_HEIGHT}`],
315
+ width: ARROW_WIDTH,
316
+ height: ARROW_HEIGHT
317
+ };
318
+
319
+ case "left":
320
+ return {
321
+ trimlinePoints: [`-${trimlineOffset},0`, `-${trimlineOffset},${ARROW_WIDTH}`],
322
+ points: [`0,0`, `${ARROW_HEIGHT},${ARROW_WIDTH / 2}`, `0,${ARROW_WIDTH}`],
323
+ width: ARROW_HEIGHT,
324
+ height: ARROW_WIDTH
325
+ };
326
+
327
+ default:
328
+ throw new Error(`Unknown placement: ${placement}`);
329
+ }
330
+ }
331
+
332
+ _getFilterPositioning() {
333
+ const {
334
+ placement
335
+ } = this.props;
336
+
337
+ switch (placement) {
338
+ case "top":
339
+ return {
340
+ y: "-50%",
341
+ x: "-50%",
342
+ offsetShadowX: 0
343
+ };
344
+
345
+ case "bottom":
346
+ return null;
347
+
348
+ case "left":
349
+ return {
350
+ y: "-50%",
351
+ x: "0%",
352
+ offsetShadowX: 1
353
+ };
354
+
355
+ case "right":
356
+ return {
357
+ y: "-50%",
358
+ x: "-100%",
359
+ offsetShadowX: -1
360
+ };
361
+
362
+ default:
363
+ throw new Error(`Unknown placement: ${placement}`);
364
+ }
365
+ }
366
+
367
+ _maybeRenderDropshadow(points) {
368
+ const position = this._getFilterPositioning();
369
+
370
+ if (!position) {
371
+ return null;
372
+ }
373
+
374
+ const {
375
+ placement
376
+ } = this.props;
377
+ const {
378
+ y,
379
+ x,
380
+ offsetShadowX
381
+ } = position;
382
+ const dropShadowFilterId = `tooltip-dropshadow-${placement}-${tempIdCounter++}`;
383
+ return [React__namespace.createElement("filter", {
384
+ key: "filter",
385
+ id: dropShadowFilterId,
386
+ width: "200%",
387
+ height: "200%",
388
+ x: x,
389
+ y: y
390
+ }, React__namespace.createElement("feGaussianBlur", {
391
+ in: "SourceAlpha",
392
+ stdDeviation: Spacing__default["default"].xxSmall_6 / 2
393
+ }), React__namespace.createElement("feComponentTransfer", null, React__namespace.createElement("feFuncA", {
394
+ type: "linear",
395
+ slope: "0.3"
396
+ }))), React__namespace.createElement("g", {
397
+ key: "dropshadow",
398
+ transform: `translate(${offsetShadowX},5.5)`
399
+ }, React__namespace.createElement("polyline", {
400
+ fill: Colors__default["default"].offBlack16,
401
+ points: points.join(" "),
402
+ stroke: Colors__default["default"].offBlack32,
403
+ filter: `url(#${dropShadowFilterId})`
404
+ }))];
405
+ }
406
+
407
+ _getFullTailWidth() {
408
+ return ARROW_WIDTH + 2 * MIN_DISTANCE_FROM_CORNERS;
409
+ }
410
+
411
+ _getFullTailHeight() {
412
+ return ARROW_HEIGHT + DISTANCE_FROM_ANCHOR;
413
+ }
414
+
415
+ _getContainerStyle() {
416
+ const {
417
+ placement
418
+ } = this.props;
419
+
420
+ const fullTailWidth = this._getFullTailWidth();
421
+
422
+ const fullTailHeight = this._getFullTailHeight();
423
+
424
+ switch (placement) {
425
+ case "top":
426
+ return {
427
+ top: -1,
428
+ width: fullTailWidth,
429
+ height: fullTailHeight
430
+ };
431
+
432
+ case "right":
433
+ return {
434
+ left: 1,
435
+ width: fullTailHeight,
436
+ height: fullTailWidth
437
+ };
438
+
439
+ case "bottom":
440
+ return {
441
+ top: 1,
442
+ width: fullTailWidth,
443
+ height: fullTailHeight
444
+ };
445
+
446
+ case "left":
447
+ return {
448
+ left: -1,
449
+ width: fullTailHeight,
450
+ height: fullTailWidth
451
+ };
452
+
453
+ default:
454
+ throw new Error(`Unknown placement: ${placement}`);
455
+ }
456
+ }
457
+
458
+ _getArrowStyle() {
459
+ const {
460
+ placement
461
+ } = this.props;
462
+
463
+ switch (placement) {
464
+ case "top":
465
+ return {
466
+ marginLeft: MIN_DISTANCE_FROM_CORNERS,
467
+ marginRight: MIN_DISTANCE_FROM_CORNERS,
468
+ paddingBottom: DISTANCE_FROM_ANCHOR
469
+ };
470
+
471
+ case "right":
472
+ return {
473
+ marginTop: MIN_DISTANCE_FROM_CORNERS,
474
+ marginBottom: MIN_DISTANCE_FROM_CORNERS,
475
+ paddingLeft: DISTANCE_FROM_ANCHOR
476
+ };
477
+
478
+ case "bottom":
479
+ return {
480
+ marginLeft: MIN_DISTANCE_FROM_CORNERS,
481
+ marginRight: MIN_DISTANCE_FROM_CORNERS,
482
+ paddingTop: DISTANCE_FROM_ANCHOR
483
+ };
484
+
485
+ case "left":
486
+ return {
487
+ marginTop: MIN_DISTANCE_FROM_CORNERS,
488
+ marginBottom: MIN_DISTANCE_FROM_CORNERS,
489
+ paddingRight: DISTANCE_FROM_ANCHOR
490
+ };
491
+
492
+ default:
493
+ throw new Error(`Unknown placement: ${placement}`);
494
+ }
495
+ }
496
+
497
+ _renderArrow() {
498
+ const {
499
+ trimlinePoints,
500
+ points,
501
+ height,
502
+ width
503
+ } = this._calculateDimensionsFromPlacement();
504
+
505
+ const {
506
+ color
507
+ } = this.props;
508
+ return React__namespace.createElement("svg", {
509
+ className: aphrodite.css(styles$2.arrow),
510
+ style: this._getArrowStyle(),
511
+ width: width,
512
+ height: height,
513
+ "aria-hidden": true
514
+ }, this._maybeRenderDropshadow(points), React__namespace.createElement("polyline", {
515
+ fill: Colors__default["default"][color],
516
+ stroke: Colors__default["default"][color],
517
+ points: points.join(" ")
518
+ }), React__namespace.createElement("polyline", {
519
+ fill: Colors__default["default"][color],
520
+ points: points.join(" "),
521
+ stroke: Colors__default["default"].offBlack16
522
+ }), React__namespace.createElement("polyline", {
523
+ stroke: Colors__default["default"][color],
524
+ points: trimlinePoints.join(" ")
525
+ }));
526
+ }
527
+
528
+ render() {
529
+ const {
530
+ offset,
531
+ placement,
532
+ updateRef
533
+ } = this.props;
534
+ return React__namespace.createElement(wonderBlocksCore.View, {
535
+ style: [styles$2.tailContainer, _extends__default["default"]({}, offset), this._getContainerStyle()],
536
+ "data-placement": placement,
537
+ ref: updateRef
538
+ }, this._renderArrow());
539
+ }
540
+
541
+ }
542
+ TooltipTail.defaultProps = {
543
+ color: "white"
544
+ };
545
+ const DISTANCE_FROM_ANCHOR = Spacing__default["default"].xSmall_8;
546
+ const MIN_DISTANCE_FROM_CORNERS = Spacing__default["default"].xSmall_8;
547
+ const ARROW_WIDTH = Spacing__default["default"].large_24;
548
+ const ARROW_HEIGHT = Spacing__default["default"].small_12;
549
+ const styles$2 = aphrodite.StyleSheet.create({
550
+ tailContainer: {
551
+ position: "relative",
552
+ pointerEvents: "none"
553
+ },
554
+ arrow: {
555
+ overflow: "visible"
556
+ }
557
+ });
558
+
559
+ class TooltipBubble extends React__namespace.Component {
560
+ constructor(...args) {
561
+ super(...args);
562
+ this.state = {
563
+ active: false
564
+ };
565
+
566
+ this.handleMouseEnter = () => {
567
+ this._setActiveState(true);
568
+ };
569
+
570
+ this.handleMouseLeave = () => {
571
+ this.props.onActiveChanged(false);
572
+ };
573
+ }
574
+
575
+ _setActiveState(active) {
576
+ this.setState({
577
+ active
578
+ });
579
+ this.props.onActiveChanged(active);
580
+ }
581
+
582
+ render() {
583
+ const {
584
+ id,
585
+ children,
586
+ updateBubbleRef,
587
+ placement,
588
+ isReferenceHidden,
589
+ style,
590
+ updateTailRef,
591
+ tailOffset
592
+ } = this.props;
593
+ return React__namespace.createElement(wonderBlocksCore.View, {
594
+ id: id,
595
+ role: "tooltip",
596
+ "data-placement": placement,
597
+ onMouseEnter: this.handleMouseEnter,
598
+ onMouseLeave: this.handleMouseLeave,
599
+ ref: updateBubbleRef,
600
+ style: [isReferenceHidden && styles$1.hide, styles$1.bubble, styles$1[`content-${placement}`], style]
601
+ }, React__namespace.createElement(wonderBlocksCore.View, {
602
+ style: styles$1.content
603
+ }, children), React__namespace.createElement(TooltipTail, {
604
+ updateRef: updateTailRef,
605
+ placement: placement,
606
+ offset: tailOffset
607
+ }));
608
+ }
609
+
610
+ }
611
+ const styles$1 = aphrodite.StyleSheet.create({
612
+ bubble: {
613
+ position: "absolute"
614
+ },
615
+ hide: {
616
+ pointerEvents: "none",
617
+ opacity: 0,
618
+ backgroundColor: "transparent",
619
+ color: "transparent"
620
+ },
621
+ "content-top": {
622
+ flexDirection: "column"
623
+ },
624
+ "content-right": {
625
+ flexDirection: "row-reverse"
626
+ },
627
+ "content-bottom": {
628
+ flexDirection: "column-reverse"
629
+ },
630
+ "content-left": {
631
+ flexDirection: "row"
632
+ },
633
+ content: {
634
+ maxWidth: 472,
635
+ borderRadius: Spacing__default["default"].xxxSmall_4,
636
+ border: `solid 1px ${Colors__default["default"].offBlack16}`,
637
+ backgroundColor: Colors__default["default"].white,
638
+ boxShadow: `0 ${Spacing__default["default"].xSmall_8}px ${Spacing__default["default"].xSmall_8}px 0 ${Colors__default["default"].offBlack8}`,
639
+ justifyContent: "center"
640
+ }
641
+ });
642
+
643
+ class TooltipContent extends React__namespace.Component {
644
+ _renderTitle() {
645
+ const {
646
+ title
647
+ } = this.props;
648
+
649
+ if (title) {
650
+ if (typeof title === "string") {
651
+ return React__namespace.createElement(wonderBlocksTypography.HeadingSmall, null, title);
652
+ } else {
653
+ return title;
654
+ }
655
+ }
656
+
657
+ return null;
658
+ }
659
+
660
+ _renderChildren() {
661
+ const {
662
+ children
663
+ } = this.props;
664
+
665
+ if (typeof children === "string") {
666
+ return React__namespace.createElement(wonderBlocksTypography.LabelMedium, null, children);
667
+ } else {
668
+ return children;
669
+ }
670
+ }
671
+
672
+ render() {
673
+ const title = this._renderTitle();
674
+
675
+ const children = this._renderChildren();
676
+
677
+ const containerStyle = title ? styles.withTitle : styles.withoutTitle;
678
+ return React__namespace.createElement(wonderBlocksCore.View, {
679
+ style: containerStyle
680
+ }, title, title && children && React__namespace.createElement(wonderBlocksLayout.Strut, {
681
+ size: Spacing__default["default"].xxxSmall_4
682
+ }), children);
683
+ }
684
+
685
+ }
686
+ const styles = aphrodite.StyleSheet.create({
687
+ withoutTitle: {
688
+ padding: `10px ${Spacing__default["default"].medium_16}px`
689
+ },
690
+ withTitle: {
691
+ padding: Spacing__default["default"].medium_16
692
+ }
693
+ });
694
+
695
+ class RefTracker {
696
+ constructor() {
697
+ this.updateRef = ref => {
698
+ if (ref) {
699
+ const domNode = ReactDOM__namespace.findDOMNode(ref);
700
+
701
+ if (domNode instanceof HTMLElement && domNode !== this._lastRef) {
702
+ this._lastRef = domNode;
703
+ this._targetFn && this._targetFn(domNode);
704
+ }
705
+ }
706
+ };
707
+
708
+ this.setCallback = targetFn => {
709
+ if (this._targetFn !== targetFn) {
710
+ if (targetFn && typeof targetFn !== "function") {
711
+ throw new Error("targetFn must be a function");
712
+ }
713
+
714
+ this._targetFn = targetFn || null;
715
+
716
+ if (this._lastRef && this._targetFn) {
717
+ this._targetFn(this._lastRef);
718
+ }
719
+ }
720
+ };
721
+ }
722
+
723
+ }
724
+
725
+ class TooltipPopper extends React__namespace.Component {
726
+ constructor(...args) {
727
+ super(...args);
728
+ this._bubbleRefTracker = new RefTracker();
729
+ this._tailRefTracker = new RefTracker();
730
+ }
731
+
732
+ _renderPositionedContent(popperProps) {
733
+ const {
734
+ children
735
+ } = this.props;
736
+ const placement = popperProps.placement || this.props.placement;
737
+
738
+ this._bubbleRefTracker.setCallback(popperProps.ref);
739
+
740
+ this._tailRefTracker.setCallback(popperProps.arrowProps.ref);
741
+
742
+ const bubbleProps = {
743
+ placement,
744
+ style: {
745
+ top: popperProps.style.top,
746
+ left: popperProps.style.left,
747
+ bottom: popperProps.style.bottom,
748
+ right: popperProps.style.right,
749
+ position: popperProps.style.position,
750
+ transform: popperProps.style.transform
751
+ },
752
+ updateBubbleRef: this._bubbleRefTracker.updateRef,
753
+ tailOffset: {
754
+ bottom: popperProps.arrowProps.style.bottom,
755
+ right: popperProps.arrowProps.style.right,
756
+ top: popperProps.arrowProps.style.top,
757
+ left: popperProps.arrowProps.style.left,
758
+ transform: popperProps.arrowProps.style.transform
759
+ },
760
+ updateTailRef: this._tailRefTracker.updateRef,
761
+ isReferenceHidden: popperProps.isReferenceHidden
762
+ };
763
+ return children(bubbleProps);
764
+ }
765
+
766
+ render() {
767
+ const {
768
+ anchorElement,
769
+ placement
770
+ } = this.props;
771
+ return React__namespace.createElement(reactPopper.Popper, {
772
+ referenceElement: anchorElement,
773
+ strategy: "fixed",
774
+ placement: placement,
775
+ modifiers: [{
776
+ name: "preventOverflow",
777
+ options: {
778
+ rootBoundary: "viewport"
779
+ }
780
+ }]
781
+ }, props => this._renderPositionedContent(props));
782
+ }
783
+
784
+ }
785
+
786
+ class Tooltip extends React__namespace.Component {
787
+ constructor(...args) {
788
+ super(...args);
789
+ this.state = {
790
+ active: false,
791
+ activeBubble: false,
792
+ anchorElement: null
793
+ };
794
+ }
795
+
796
+ static getDerivedStateFromProps(props, state) {
797
+ return {
798
+ active: typeof props.opened === "boolean" ? props.opened : state.active
799
+ };
800
+ }
801
+
802
+ _updateAnchorElement(ref) {
803
+ if (ref && ref !== this.state.anchorElement) {
804
+ this.setState({
805
+ anchorElement: ref
806
+ });
807
+ }
808
+ }
809
+
810
+ _renderBubbleContent() {
811
+ const {
812
+ title,
813
+ content
814
+ } = this.props;
815
+
816
+ if (typeof content === "string") {
817
+ return React__namespace.createElement(TooltipContent, {
818
+ title: title
819
+ }, content);
820
+ } else if (title) {
821
+ return React__namespace.cloneElement(content, {
822
+ title
823
+ });
824
+ } else {
825
+ return content;
826
+ }
827
+ }
828
+
829
+ _renderPopper(ids) {
830
+ const {
831
+ id
832
+ } = this.props;
833
+ const bubbleId = ids ? ids.get(Tooltip.ariaContentId) : id;
834
+
835
+ if (!bubbleId) {
836
+ throw new Error("Did not get an identifier factory nor a id prop");
837
+ }
838
+
839
+ const {
840
+ placement
841
+ } = this.props;
842
+ return React__namespace.createElement(TooltipPopper, {
843
+ anchorElement: this.state.anchorElement,
844
+ placement: placement
845
+ }, props => React__namespace.createElement(TooltipBubble, {
846
+ id: bubbleId,
847
+ style: props.style,
848
+ tailOffset: props.tailOffset,
849
+ isReferenceHidden: props.isReferenceHidden,
850
+ placement: props.placement,
851
+ updateTailRef: props.updateTailRef,
852
+ updateBubbleRef: props.updateBubbleRef,
853
+ onActiveChanged: active => this.setState({
854
+ activeBubble: active
855
+ })
856
+ }, this._renderBubbleContent()));
857
+ }
858
+
859
+ _getHost() {
860
+ const {
861
+ anchorElement
862
+ } = this.state;
863
+ return wonderBlocksModal.maybeGetPortalMountedModalHostElement(anchorElement) || document.body;
864
+ }
865
+
866
+ _renderTooltipAnchor(ids) {
867
+ const {
868
+ children,
869
+ forceAnchorFocusivity
870
+ } = this.props;
871
+ const {
872
+ active,
873
+ activeBubble
874
+ } = this.state;
875
+
876
+ const popperHost = this._getHost();
877
+
878
+ return React__namespace.createElement(React__namespace.Fragment, null, React__namespace.createElement(TooltipAnchor, {
879
+ forceAnchorFocusivity: forceAnchorFocusivity,
880
+ anchorRef: r => this._updateAnchorElement(r),
881
+ onActiveChanged: active => this.setState({
882
+ active
883
+ }),
884
+ ids: ids
885
+ }, children), popperHost && (active || activeBubble) && ReactDOM__namespace.createPortal(this._renderPopper(ids), popperHost));
886
+ }
887
+
888
+ render() {
889
+ const {
890
+ id
891
+ } = this.props;
892
+
893
+ if (id) {
894
+ return this._renderTooltipAnchor();
895
+ } else {
896
+ return React__namespace.createElement(wonderBlocksCore.UniqueIDProvider, {
897
+ scope: "tooltip",
898
+ mockOnFirstRender: true
899
+ }, ids => this._renderTooltipAnchor(ids));
900
+ }
901
+ }
902
+
903
+ }
904
+ Tooltip.defaultProps = {
905
+ forceAnchorFocusivity: true,
906
+ placement: "top"
907
+ };
908
+ Tooltip.ariaContentId = "aria-content";
909
+
910
+ exports.TooltipContent = TooltipContent;
911
+ exports.TooltipPopper = TooltipPopper;
912
+ exports.TooltipTail = TooltipTail;
913
+ exports["default"] = Tooltip;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-tooltip",
3
- "version": "1.4.3",
3
+ "version": "1.4.4",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -17,11 +17,11 @@
17
17
  "dependencies": {
18
18
  "@babel/runtime": "^7.18.6",
19
19
  "@khanacademy/wonder-blocks-color": "^1.2.0",
20
- "@khanacademy/wonder-blocks-core": "^4.6.1",
21
- "@khanacademy/wonder-blocks-layout": "^1.4.14",
22
- "@khanacademy/wonder-blocks-modal": "^3.0.4",
20
+ "@khanacademy/wonder-blocks-core": "^4.6.2",
21
+ "@khanacademy/wonder-blocks-layout": "^1.4.15",
22
+ "@khanacademy/wonder-blocks-modal": "^3.0.5",
23
23
  "@khanacademy/wonder-blocks-spacing": "^3.0.5",
24
- "@khanacademy/wonder-blocks-typography": "^1.1.36"
24
+ "@khanacademy/wonder-blocks-typography": "^1.1.37"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "@popperjs/core": "^2.10.1",
@@ -31,6 +31,6 @@
31
31
  "react-popper": "^2.0.0"
32
32
  },
33
33
  "devDependencies": {
34
- "wb-dev-build-settings": "^0.6.0"
34
+ "wb-dev-build-settings": "^0.7.0"
35
35
  }
36
36
  }