@khanacademy/wonder-blocks-clickable 2.1.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/dist/index.js ADDED
@@ -0,0 +1,1056 @@
1
+ module.exports =
2
+ /******/ (function(modules) { // webpackBootstrap
3
+ /******/ // The module cache
4
+ /******/ var installedModules = {};
5
+ /******/
6
+ /******/ // The require function
7
+ /******/ function __webpack_require__(moduleId) {
8
+ /******/
9
+ /******/ // Check if module is in cache
10
+ /******/ if(installedModules[moduleId]) {
11
+ /******/ return installedModules[moduleId].exports;
12
+ /******/ }
13
+ /******/ // Create a new module (and put it into the cache)
14
+ /******/ var module = installedModules[moduleId] = {
15
+ /******/ i: moduleId,
16
+ /******/ l: false,
17
+ /******/ exports: {}
18
+ /******/ };
19
+ /******/
20
+ /******/ // Execute the module function
21
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22
+ /******/
23
+ /******/ // Flag the module as loaded
24
+ /******/ module.l = true;
25
+ /******/
26
+ /******/ // Return the exports of the module
27
+ /******/ return module.exports;
28
+ /******/ }
29
+ /******/
30
+ /******/
31
+ /******/ // expose the modules object (__webpack_modules__)
32
+ /******/ __webpack_require__.m = modules;
33
+ /******/
34
+ /******/ // expose the module cache
35
+ /******/ __webpack_require__.c = installedModules;
36
+ /******/
37
+ /******/ // define getter function for harmony exports
38
+ /******/ __webpack_require__.d = function(exports, name, getter) {
39
+ /******/ if(!__webpack_require__.o(exports, name)) {
40
+ /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
41
+ /******/ }
42
+ /******/ };
43
+ /******/
44
+ /******/ // define __esModule on exports
45
+ /******/ __webpack_require__.r = function(exports) {
46
+ /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
47
+ /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
48
+ /******/ }
49
+ /******/ Object.defineProperty(exports, '__esModule', { value: true });
50
+ /******/ };
51
+ /******/
52
+ /******/ // create a fake namespace object
53
+ /******/ // mode & 1: value is a module id, require it
54
+ /******/ // mode & 2: merge all properties of value into the ns
55
+ /******/ // mode & 4: return value when already ns object
56
+ /******/ // mode & 8|1: behave like require
57
+ /******/ __webpack_require__.t = function(value, mode) {
58
+ /******/ if(mode & 1) value = __webpack_require__(value);
59
+ /******/ if(mode & 8) return value;
60
+ /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
61
+ /******/ var ns = Object.create(null);
62
+ /******/ __webpack_require__.r(ns);
63
+ /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
64
+ /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
65
+ /******/ return ns;
66
+ /******/ };
67
+ /******/
68
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
69
+ /******/ __webpack_require__.n = function(module) {
70
+ /******/ var getter = module && module.__esModule ?
71
+ /******/ function getDefault() { return module['default']; } :
72
+ /******/ function getModuleExports() { return module; };
73
+ /******/ __webpack_require__.d(getter, 'a', getter);
74
+ /******/ return getter;
75
+ /******/ };
76
+ /******/
77
+ /******/ // Object.prototype.hasOwnProperty.call
78
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
79
+ /******/
80
+ /******/ // __webpack_public_path__
81
+ /******/ __webpack_require__.p = "";
82
+ /******/
83
+ /******/
84
+ /******/ // Load entry module and return exports
85
+ /******/ return __webpack_require__(__webpack_require__.s = 7);
86
+ /******/ })
87
+ /************************************************************************/
88
+ /******/ ([
89
+ /* 0 */
90
+ /***/ (function(module, exports) {
91
+
92
+ module.exports = require("react");
93
+
94
+ /***/ }),
95
+ /* 1 */
96
+ /***/ (function(module, exports) {
97
+
98
+ function _extends() {
99
+ module.exports = _extends = Object.assign || function (target) {
100
+ for (var i = 1; i < arguments.length; i++) {
101
+ var source = arguments[i];
102
+
103
+ for (var key in source) {
104
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
105
+ target[key] = source[key];
106
+ }
107
+ }
108
+ }
109
+
110
+ return target;
111
+ };
112
+
113
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
114
+ return _extends.apply(this, arguments);
115
+ }
116
+
117
+ module.exports = _extends;
118
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
119
+
120
+ /***/ }),
121
+ /* 2 */
122
+ /***/ (function(module, exports) {
123
+
124
+ module.exports = require("react-router-dom");
125
+
126
+ /***/ }),
127
+ /* 3 */
128
+ /***/ (function(module, exports) {
129
+
130
+ module.exports = require("@khanacademy/wonder-blocks-core");
131
+
132
+ /***/ }),
133
+ /* 4 */
134
+ /***/ (function(module, exports) {
135
+
136
+ function _objectWithoutPropertiesLoose(source, excluded) {
137
+ if (source == null) return {};
138
+ var target = {};
139
+ var sourceKeys = Object.keys(source);
140
+ var key, i;
141
+
142
+ for (i = 0; i < sourceKeys.length; i++) {
143
+ key = sourceKeys[i];
144
+ if (excluded.indexOf(key) >= 0) continue;
145
+ target[key] = source[key];
146
+ }
147
+
148
+ return target;
149
+ }
150
+
151
+ module.exports = _objectWithoutPropertiesLoose;
152
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
153
+
154
+ /***/ }),
155
+ /* 5 */
156
+ /***/ (function(module, exports) {
157
+
158
+ module.exports = require("aphrodite");
159
+
160
+ /***/ }),
161
+ /* 6 */
162
+ /***/ (function(module, exports) {
163
+
164
+ module.exports = require("prop-types");
165
+
166
+ /***/ }),
167
+ /* 7 */
168
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
169
+
170
+ "use strict";
171
+ // ESM COMPAT FLAG
172
+ __webpack_require__.r(__webpack_exports__);
173
+
174
+ // EXPORTS
175
+ __webpack_require__.d(__webpack_exports__, "ClickableBehavior", function() { return /* reexport */ clickable_behavior_ClickableBehavior; });
176
+ __webpack_require__.d(__webpack_exports__, "getClickableBehavior", function() { return /* reexport */ getClickableBehavior; });
177
+ __webpack_require__.d(__webpack_exports__, "isClientSideUrl", function() { return /* reexport */ isClientSideUrl; });
178
+ __webpack_require__.d(__webpack_exports__, "default", function() { return /* reexport */ clickable_Clickable; });
179
+
180
+ // EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/objectWithoutPropertiesLoose.js
181
+ var objectWithoutPropertiesLoose = __webpack_require__(4);
182
+ var objectWithoutPropertiesLoose_default = /*#__PURE__*/__webpack_require__.n(objectWithoutPropertiesLoose);
183
+
184
+ // EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/extends.js
185
+ var helpers_extends = __webpack_require__(1);
186
+ var extends_default = /*#__PURE__*/__webpack_require__.n(helpers_extends);
187
+
188
+ // EXTERNAL MODULE: external "react"
189
+ var external_react_ = __webpack_require__(0);
190
+
191
+ // EXTERNAL MODULE: external "aphrodite"
192
+ var external_aphrodite_ = __webpack_require__(5);
193
+
194
+ // EXTERNAL MODULE: external "prop-types"
195
+ var external_prop_types_ = __webpack_require__(6);
196
+
197
+ // EXTERNAL MODULE: external "react-router-dom"
198
+ var external_react_router_dom_ = __webpack_require__(2);
199
+
200
+ // EXTERNAL MODULE: external "@khanacademy/wonder-blocks-core"
201
+ var wonder_blocks_core_ = __webpack_require__(3);
202
+
203
+ // CONCATENATED MODULE: ./packages/wonder-blocks-color/dist/es/index.js
204
+
205
+ /**
206
+ * A color manipulation library useful for dynamically
207
+ * mixing colors together.
208
+ */
209
+
210
+ const color6Regexp = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
211
+ const color3Regexp = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i;
212
+ const rgbaRegexp = /^rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\s*\)$/i; // Parse a color in #abcdef, rgb(...), or rgba(...) form into an object
213
+ // with r,g,b,a keys.
214
+
215
+ const parse = color => {
216
+ if (typeof color !== "string") {
217
+ throw new Error(`Failed to parse color: ${color}`);
218
+ }
219
+
220
+ const color3Match = color.match(color3Regexp);
221
+
222
+ if (color3Match) {
223
+ return {
224
+ r: parseInt(`${color3Match[1]}${color3Match[1]}`, 16),
225
+ g: parseInt(`${color3Match[2]}${color3Match[2]}`, 16),
226
+ b: parseInt(`${color3Match[3]}${color3Match[3]}`, 16),
227
+ a: 1
228
+ };
229
+ }
230
+
231
+ const color6Match = color.match(color6Regexp);
232
+
233
+ if (color6Match) {
234
+ return {
235
+ r: parseInt(color6Match[1], 16),
236
+ g: parseInt(color6Match[2], 16),
237
+ b: parseInt(color6Match[3], 16),
238
+ a: 1
239
+ };
240
+ }
241
+
242
+ const rgbaMatch = color.match(rgbaRegexp);
243
+
244
+ if (rgbaMatch) {
245
+ return {
246
+ r: parseFloat(rgbaMatch[1]),
247
+ g: parseFloat(rgbaMatch[2]),
248
+ b: parseFloat(rgbaMatch[3]),
249
+ a: rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1
250
+ };
251
+ }
252
+
253
+ throw new Error(`Failed to parse color: ${color}`);
254
+ }; // Stringify the color in an `rgba()` or `#abcdef` format.
255
+
256
+
257
+ const format = color => {
258
+ const r = Math.round(color.r);
259
+ const g = Math.round(color.g);
260
+ const b = Math.round(color.b);
261
+
262
+ if (color.a === 1) {
263
+ const _s = c => {
264
+ const asString = c.toString(16);
265
+ return asString.length === 1 ? asString + asString : asString;
266
+ };
267
+
268
+ return `#${_s(r)}${_s(g)}${_s(b)}`;
269
+ } else {
270
+ return `rgba(${r},${g},${b},${color.a.toFixed(2)})`;
271
+ }
272
+ }; // Adjust the alpha value of a color.
273
+
274
+
275
+ const fade = (color, percentage) => {
276
+ if (percentage < 0 || percentage > 1) {
277
+ throw new Error("Percentage must be between 0 and 1");
278
+ }
279
+
280
+ const components = parse(color);
281
+ return format(extends_default()({}, components, {
282
+ a: components.a * percentage
283
+ }));
284
+ }; // Mix a color into a background color, using the alpha channel of the base
285
+ // color to determine the linear blend.
286
+
287
+
288
+ const mix = (color, background) => {
289
+ const colorObj = parse(color);
290
+ const bgObj = parse(background);
291
+ return format({
292
+ r: colorObj.r * colorObj.a + bgObj.r * (1 - colorObj.a),
293
+ g: colorObj.g * colorObj.a + bgObj.g * (1 - colorObj.a),
294
+ b: colorObj.b * colorObj.a + bgObj.b * (1 - colorObj.a),
295
+ a: bgObj.a
296
+ });
297
+ };
298
+
299
+ const offBlack = "#21242c";
300
+ const white = "#ffffff";
301
+ const Color = {
302
+ // Product
303
+ blue: "#1865f2",
304
+ purple: "#9059ff",
305
+ green: "#00a60e",
306
+ gold: "#ffb100",
307
+ red: "#d92916",
308
+ // Neutral
309
+ offBlack,
310
+ offBlack64: fade(offBlack, 0.64),
311
+ offBlack50: fade(offBlack, 0.5),
312
+ offBlack32: fade(offBlack, 0.32),
313
+ offBlack16: fade(offBlack, 0.16),
314
+ offBlack8: fade(offBlack, 0.08),
315
+ offWhite: "#f7f8fa",
316
+ white,
317
+ white64: fade(white, 0.64),
318
+ white50: fade(white, 0.5),
319
+ // Brand
320
+ darkBlue: "#0a2a66",
321
+ teal: "#14bf96",
322
+ lightBlue: "#37c5fd",
323
+ pink: "#fa50ae"
324
+ };
325
+ const SemanticColor = {
326
+ controlDefault: Color.blue,
327
+ controlDestructive: Color.red
328
+ };
329
+ /* harmony default export */ var es = (Color);
330
+
331
+ // CONCATENATED MODULE: ./packages/wonder-blocks-clickable/src/components/clickable-behavior.js
332
+ // NOTE: Potentially add to this as more cases come up.
333
+
334
+ const getAppropriateTriggersForRole = role => {
335
+ switch (role) {
336
+ // Triggers on ENTER, but not SPACE
337
+ case "link":
338
+ return {
339
+ triggerOnEnter: true,
340
+ triggerOnSpace: false
341
+ };
342
+ // Triggers on SPACE, but not ENTER
343
+
344
+ case "checkbox":
345
+ case "radio":
346
+ case "listbox":
347
+ case "option":
348
+ return {
349
+ triggerOnEnter: false,
350
+ triggerOnSpace: true
351
+ };
352
+ // Triggers on both ENTER and SPACE
353
+
354
+ case "button":
355
+ case "menuitem":
356
+ case "menu":
357
+ default:
358
+ return {
359
+ triggerOnEnter: true,
360
+ triggerOnSpace: true
361
+ };
362
+ }
363
+ };
364
+
365
+ const disabledHandlers = {
366
+ onClick: () => void 0,
367
+ onMouseEnter: () => void 0,
368
+ onMouseLeave: () => void 0,
369
+ onMouseDown: () => void 0,
370
+ onMouseUp: () => void 0,
371
+ onDragStart: () => void 0,
372
+ onTouchStart: () => void 0,
373
+ onTouchEnd: () => void 0,
374
+ onTouchCancel: () => void 0,
375
+ onKeyDown: () => void 0,
376
+ onKeyUp: () => void 0,
377
+ onFocus: () => void 0,
378
+ onBlur: () => void 0,
379
+ tabIndex: -1
380
+ };
381
+ const keyCodes = {
382
+ enter: 13,
383
+ space: 32
384
+ };
385
+ const startState = {
386
+ hovered: false,
387
+ focused: false,
388
+ pressed: false,
389
+ waiting: false
390
+ };
391
+ /**
392
+ * Add hover, focus, and active status updates to a clickable component.
393
+ *
394
+ * Via mouse:
395
+ *
396
+ * 1. Hover over button -> hover state
397
+ * 2. Mouse down -> active state
398
+ * 3. Mouse up -> default state
399
+ * 4. Press tab -> focus state
400
+ *
401
+ * Via touch:
402
+ *
403
+ * 1. Touch down -> press state
404
+ * 2. Touch up -> default state
405
+ *
406
+ * Via keyboard:
407
+ *
408
+ * 1. Tab to focus -> focus state
409
+ * 2. Keydown (spacebar/enter) -> active state
410
+ * 3. Keyup (spacebar/enter) -> focus state
411
+ *
412
+ * Warning: The event handlers returned (onClick, onMouseEnter, onMouseLeave,
413
+ * onMouseDown, onMouseUp, onDragStart, onTouchStart, onTouchEnd, onTouchCancel, onKeyDown,
414
+ * onKeyUp, onFocus, onBlur, tabIndex) should be passed on to the component
415
+ * that has the ClickableBehavior. You cannot override these handlers without
416
+ * potentially breaking the functionality of ClickableBehavior.
417
+ *
418
+ * There are internal props triggerOnEnter and triggerOnSpace that can be set
419
+ * to false if one of those keys shouldn't count as a click on this component.
420
+ * Be careful about setting those to false -- make certain that the component
421
+ * shouldn't process that key.
422
+ *
423
+ * See [this document](https://docs.google.com/document/d/1DG5Rg2f0cawIL5R8UqnPQpd7pbdObk8OyjO5ryYQmBM/edit#)
424
+ * for a more thorough explanation of expected behaviors and potential cavaets.
425
+ *
426
+ * `ClickableBehavior` accepts a function as `children` which is passed state
427
+ * and an object containing event handlers and some other props. The `children`
428
+ * function should return a clickable React Element of some sort.
429
+ *
430
+ * Example:
431
+ *
432
+ * ```js
433
+ * class MyClickableComponent extends React.Component<Props> {
434
+ * render(): React.Node {
435
+ * const ClickableBehavior = getClickableBehavior();
436
+ * return <ClickableBehavior
437
+ * disabled={this.props.disabled}
438
+ * onClick={this.props.onClick}
439
+ * >
440
+ * {({hovered}, childrenProps) =>
441
+ * <RoundRect
442
+ * textcolor='white'
443
+ * backgroundColor={hovered ? 'red' : 'blue'}}
444
+ * {...childrenProps}
445
+ * >
446
+ * {this.props.children}
447
+ * </RoundRect>
448
+ * }
449
+ * </ClickableBehavior>
450
+ * }
451
+ * }
452
+ * ```
453
+ *
454
+ * This follows a pattern called [Function as Child Components]
455
+ * (https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9).
456
+ *
457
+ * WARNING: Do not use this component directly, use getClickableBehavior
458
+ * instead. getClickableBehavior takes three arguments (href, directtNav, and
459
+ * router) and returns either the default ClickableBehavior or a react-router
460
+ * aware version.
461
+ *
462
+ * The react-router aware version is returned if `router` is a react-router-dom
463
+ * router, `skipClientNav` is not `true`, and `href` is an internal URL.
464
+ *
465
+ * The `router` can be accessed via this.context.router from a component
466
+ * rendered as a descendant of a BrowserRouter.
467
+ * See https://reacttraining.com/react-router/web/guides/basic-components.
468
+ */
469
+
470
+ class clickable_behavior_ClickableBehavior extends external_react_["Component"] {
471
+ static getDerivedStateFromProps(props, state) {
472
+ // If new props are disabled, reset the hovered/focused/pressed states
473
+ if (props.disabled) {
474
+ return startState;
475
+ } else {
476
+ // Cannot return undefined
477
+ return null;
478
+ }
479
+ }
480
+
481
+ constructor(props) {
482
+ super(props);
483
+
484
+ this.handleClick = e => {
485
+ const {
486
+ onClick = undefined,
487
+ beforeNav = undefined,
488
+ safeWithNav = undefined
489
+ } = this.props;
490
+
491
+ if (this.enterClick) {
492
+ return;
493
+ }
494
+
495
+ if (onClick || beforeNav || safeWithNav) {
496
+ this.waitingForClick = false;
497
+ }
498
+
499
+ this.runCallbackAndMaybeNavigate(e);
500
+ };
501
+
502
+ this.handleMouseEnter = e => {
503
+ // When the left button is pressed already, we want it to be pressed
504
+ if (e.buttons === 1) {
505
+ this.dragging = true;
506
+ this.setState({
507
+ pressed: true
508
+ });
509
+ } else if (!this.waitingForClick) {
510
+ this.setState({
511
+ hovered: true
512
+ });
513
+ }
514
+ };
515
+
516
+ this.handleMouseLeave = () => {
517
+ if (!this.waitingForClick) {
518
+ this.dragging = false;
519
+ this.setState({
520
+ hovered: false,
521
+ pressed: false,
522
+ focused: false
523
+ });
524
+ }
525
+ };
526
+
527
+ this.handleMouseDown = () => {
528
+ this.setState({
529
+ pressed: true
530
+ });
531
+ };
532
+
533
+ this.handleMouseUp = e => {
534
+ if (this.dragging) {
535
+ this.dragging = false;
536
+ this.handleClick(e);
537
+ }
538
+
539
+ this.setState({
540
+ pressed: false,
541
+ focused: false
542
+ });
543
+ };
544
+
545
+ this.handleDragStart = e => {
546
+ this.dragging = true;
547
+ e.preventDefault();
548
+ };
549
+
550
+ this.handleTouchStart = () => {
551
+ this.setState({
552
+ pressed: true
553
+ });
554
+ };
555
+
556
+ this.handleTouchEnd = () => {
557
+ this.setState({
558
+ pressed: false
559
+ });
560
+ this.waitingForClick = true;
561
+ };
562
+
563
+ this.handleTouchCancel = () => {
564
+ this.setState({
565
+ pressed: false
566
+ });
567
+ this.waitingForClick = true;
568
+ };
569
+
570
+ this.handleKeyDown = e => {
571
+ const {
572
+ onKeyDown,
573
+ role
574
+ } = this.props;
575
+
576
+ if (onKeyDown) {
577
+ onKeyDown(e);
578
+ }
579
+
580
+ const keyCode = e.which || e.keyCode;
581
+ const {
582
+ triggerOnEnter,
583
+ triggerOnSpace
584
+ } = getAppropriateTriggersForRole(role);
585
+
586
+ if (triggerOnEnter && keyCode === keyCodes.enter || triggerOnSpace && keyCode === keyCodes.space) {
587
+ // This prevents space from scrolling down. It also prevents the
588
+ // space and enter keys from triggering click events. We manually
589
+ // call the supplied onClick and handle potential navigation in
590
+ // handleKeyUp instead.
591
+ e.preventDefault();
592
+ this.setState({
593
+ pressed: true
594
+ });
595
+ } else if (!triggerOnEnter && keyCode === keyCodes.enter) {
596
+ // If the component isn't supposed to trigger on enter, we have to
597
+ // keep track of the enter keydown to negate the onClick callback
598
+ this.enterClick = true;
599
+ }
600
+ };
601
+
602
+ this.handleKeyUp = e => {
603
+ const {
604
+ onKeyUp,
605
+ role
606
+ } = this.props;
607
+
608
+ if (onKeyUp) {
609
+ onKeyUp(e);
610
+ }
611
+
612
+ const keyCode = e.which || e.keyCode;
613
+ const {
614
+ triggerOnEnter,
615
+ triggerOnSpace
616
+ } = getAppropriateTriggersForRole(role);
617
+
618
+ if (triggerOnEnter && keyCode === keyCodes.enter || triggerOnSpace && keyCode === keyCodes.space) {
619
+ this.setState({
620
+ pressed: false,
621
+ focused: true
622
+ });
623
+ this.runCallbackAndMaybeNavigate(e);
624
+ } else if (!triggerOnEnter && keyCode === keyCodes.enter) {
625
+ this.enterClick = false;
626
+ }
627
+ };
628
+
629
+ this.handleFocus = e => {
630
+ this.setState({
631
+ focused: true
632
+ });
633
+ };
634
+
635
+ this.handleBlur = e => {
636
+ this.setState({
637
+ focused: false,
638
+ pressed: false
639
+ });
640
+ };
641
+
642
+ this.state = startState;
643
+ this.waitingForClick = false;
644
+ this.enterClick = false;
645
+ this.dragging = false;
646
+ }
647
+
648
+ navigateOrReset(shouldNavigate) {
649
+ if (shouldNavigate) {
650
+ const {
651
+ history,
652
+ href,
653
+ skipClientNav,
654
+ target = undefined
655
+ } = this.props;
656
+
657
+ if (href) {
658
+ if (target === "_blank") {
659
+ window.open(href, "_blank");
660
+ this.setState({
661
+ waiting: false
662
+ });
663
+ } else if (history && !skipClientNav) {
664
+ history.push(href);
665
+ this.setState({
666
+ waiting: false
667
+ });
668
+ } else {
669
+ window.location.assign(href); // We don't bother clearing the waiting state, the full page
670
+ // load navigation will do that for us by loading a new page.
671
+ }
672
+ }
673
+ } else {
674
+ this.setState({
675
+ waiting: false
676
+ });
677
+ }
678
+ }
679
+
680
+ handleSafeWithNav(safeWithNav, shouldNavigate) {
681
+ const {
682
+ skipClientNav,
683
+ history
684
+ } = this.props;
685
+
686
+ if (history && !skipClientNav || this.props.target === "_blank") {
687
+ // client-side nav
688
+ safeWithNav();
689
+ this.navigateOrReset(shouldNavigate);
690
+ return Promise.resolve();
691
+ } else {
692
+ if (!this.state.waiting) {
693
+ // We only show the spinner for safeWithNav when doing
694
+ // a full page load navigation since since the spinner is
695
+ // indicating that we're waiting for navigation to occur.
696
+ this.setState({
697
+ waiting: true
698
+ });
699
+ }
700
+
701
+ return safeWithNav().then(() => {
702
+ if (!this.state.waiting) {
703
+ // We only show the spinner for safeWithNav when doing
704
+ // a full page load navigation since since the spinner is
705
+ // indicating that we're waiting for navigation to occur.
706
+ this.setState({
707
+ waiting: true
708
+ });
709
+ }
710
+
711
+ return;
712
+ }).catch(error => {// We ignore the error here so that we always
713
+ // navigate when using safeWithNav regardless of
714
+ // whether we're doing a client-side nav or not.
715
+ }).finally(() => {
716
+ this.navigateOrReset(shouldNavigate);
717
+ });
718
+ }
719
+ }
720
+
721
+ runCallbackAndMaybeNavigate(e) {
722
+ const {
723
+ onClick = undefined,
724
+ beforeNav = undefined,
725
+ safeWithNav = undefined,
726
+ href,
727
+ type
728
+ } = this.props;
729
+ let shouldNavigate = true;
730
+ let canSubmit = true;
731
+
732
+ if (onClick) {
733
+ onClick(e);
734
+ } // If onClick() has called e.preventDefault() then we shouldn't
735
+ // navigate.
736
+
737
+
738
+ if (e.defaultPrevented) {
739
+ shouldNavigate = false;
740
+ canSubmit = false;
741
+ }
742
+
743
+ e.preventDefault();
744
+
745
+ if (!href && type === "submit" && canSubmit) {
746
+ let target = e.currentTarget;
747
+
748
+ while (target) {
749
+ if (target instanceof window.HTMLFormElement) {
750
+ // This event must be marked as cancelable otherwise calling
751
+ // e.preventDefault() on it won't do anything in Firefox.
752
+ // Chrome and Safari allow calling e.preventDefault() on
753
+ // non-cancelable events, but really they shouldn't.
754
+ const event = new window.Event("submit", {
755
+ cancelable: true
756
+ });
757
+ target.dispatchEvent(event);
758
+ break;
759
+ } // All events should be typed as SyntheticEvent<HTMLElement>.
760
+ // Updating all of the places will take some time so I'll do
761
+ // this later
762
+ // $FlowFixMe[prop-missing]
763
+
764
+
765
+ target = target.parentElement;
766
+ }
767
+ }
768
+
769
+ if (beforeNav) {
770
+ this.setState({
771
+ waiting: true
772
+ });
773
+ beforeNav().then(() => {
774
+ if (safeWithNav) {
775
+ return this.handleSafeWithNav(safeWithNav, shouldNavigate);
776
+ } else {
777
+ return this.navigateOrReset(shouldNavigate);
778
+ }
779
+ }).catch(() => {});
780
+ } else if (safeWithNav) {
781
+ return this.handleSafeWithNav(safeWithNav, shouldNavigate);
782
+ } else {
783
+ this.navigateOrReset(shouldNavigate);
784
+ }
785
+ }
786
+
787
+ render() {
788
+ const childrenProps = this.props.disabled ? disabledHandlers : {
789
+ onClick: this.handleClick,
790
+ onMouseEnter: this.handleMouseEnter,
791
+ onMouseLeave: this.handleMouseLeave,
792
+ onMouseDown: this.handleMouseDown,
793
+ onMouseUp: this.handleMouseUp,
794
+ onDragStart: this.handleDragStart,
795
+ onTouchStart: this.handleTouchStart,
796
+ onTouchEnd: this.handleTouchEnd,
797
+ onTouchCancel: this.handleTouchCancel,
798
+ onKeyDown: this.handleKeyDown,
799
+ onKeyUp: this.handleKeyUp,
800
+ onFocus: this.handleFocus,
801
+ onBlur: this.handleBlur,
802
+ // We set tabIndex to 0 so that users can tab to clickable
803
+ // things that aren't buttons or anchors.
804
+ tabIndex: 0
805
+ }; // When the link is set to open in a new window, we want to set some
806
+ // `rel` attributes. This is to ensure that the links we're sending folks
807
+ // to can't hijack the existing page. These defaults can be overriden
808
+ // by passing in a different value for the `rel` prop.
809
+ // More info: https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/
810
+
811
+ childrenProps.rel = this.props.rel || (this.props.target === "_blank" ? "noopener noreferrer" : undefined);
812
+ const {
813
+ children
814
+ } = this.props;
815
+ return children && children(this.state, childrenProps);
816
+ }
817
+
818
+ }
819
+ clickable_behavior_ClickableBehavior.defaultProps = {
820
+ disabled: false
821
+ };
822
+ // CONCATENATED MODULE: ./packages/wonder-blocks-clickable/src/util/is-client-side-url.js
823
+ /**
824
+ * Returns:
825
+ * - false for hrefs staring with http://, https://, //.
826
+ * - false for '#', 'javascript:...', 'mailto:...', 'tel:...', etc.
827
+ * - true for all other values, e.g. /foo/bar
828
+ */
829
+ const isClientSideUrl = href => {
830
+ if (typeof href !== "string") {
831
+ return false;
832
+ }
833
+
834
+ return !/^(https?:)?\/\//i.test(href) && !/^([^#]*#[\w-]*|[\w\-.]+:)/.test(href);
835
+ };
836
+ // CONCATENATED MODULE: ./packages/wonder-blocks-clickable/src/util/get-clickable-behavior.js
837
+ /**
838
+ * Returns either the default ClickableBehavior or a react-router aware version.
839
+ *
840
+ * The react-router aware version is returned if `router` is a react-router-dom
841
+ * router, `skipClientNav` is not `true`, and `href` is an internal URL.
842
+ *
843
+ * The `router` can be accessed via this.context.router from a component rendered
844
+ * as a descendant of a BrowserRouter.
845
+ * See https://reacttraining.com/react-router/web/guides/basic-components.
846
+ */
847
+
848
+
849
+
850
+
851
+ const ClickableBehaviorWithRouter = Object(external_react_router_dom_["withRouter"])(clickable_behavior_ClickableBehavior);
852
+ function getClickableBehavior(
853
+ /**
854
+ * The URL to navigate to.
855
+ */
856
+ href,
857
+ /**
858
+ * Should we skip using the react router and go to the page directly.
859
+ */
860
+ skipClientNav,
861
+ /**
862
+ * router object added to the React context object by react-router-dom.
863
+ */
864
+ router) {
865
+ if (router && skipClientNav !== true && href && isClientSideUrl(href)) {
866
+ // We cast to `any` here since the type of ClickableBehaviorWithRouter
867
+ // is slightly different from the return type of this function.
868
+ // TODO(WB-1037): Always return the wrapped version once all routes have
869
+ // been ported to the app-shell in webapp.
870
+ return ClickableBehaviorWithRouter;
871
+ }
872
+
873
+ return clickable_behavior_ClickableBehavior;
874
+ }
875
+ // CONCATENATED MODULE: ./packages/wonder-blocks-clickable/src/components/clickable.js
876
+
877
+
878
+ const _excluded = ["href", "onClick", "skipClientNav", "beforeNav", "safeWithNav", "style", "target", "testId", "onKeyDown", "onKeyUp", "hideDefaultFocusRing", "light", "disabled"];
879
+
880
+
881
+
882
+
883
+
884
+
885
+
886
+
887
+ const StyledAnchor = Object(wonder_blocks_core_["addStyle"])("a");
888
+ const StyledButton = Object(wonder_blocks_core_["addStyle"])("button");
889
+ const StyledLink = Object(wonder_blocks_core_["addStyle"])(external_react_router_dom_["Link"]);
890
+ /**
891
+ * A component to turn any custom component into a clickable one.
892
+ *
893
+ * Works by wrapping ClickableBehavior around the child element and styling the
894
+ * child appropriately and encapsulates routing logic which can be customized.
895
+ * Expects a function which returns an element as it's child.
896
+ *
897
+ * Example usage:
898
+ * ```jsx
899
+ * <Clickable onClick={() => alert("You clicked me!")}>
900
+ * {({hovered, focused, pressed}) =>
901
+ * <div
902
+ * style={[
903
+ * hovered && styles.hovered,
904
+ * focused && styles.focused,
905
+ * pressed && styles.pressed,
906
+ * ]}
907
+ * >
908
+ * Click Me!
909
+ * </div>
910
+ * }
911
+ * </Clickable>
912
+ * ```
913
+ */
914
+
915
+ class clickable_Clickable extends external_react_["Component"] {
916
+ constructor(...args) {
917
+ super(...args);
918
+
919
+ this.getCorrectTag = (clickableState, commonProps) => {
920
+ const activeHref = this.props.href && !this.props.disabled;
921
+ const useClient = this.context.router && !this.props.skipClientNav && isClientSideUrl(this.props.href || ""); // NOTE: checking this.props.href here is redundant, but flow
922
+ // needs it to refine this.props.href to a string.
923
+
924
+ if (activeHref && useClient && this.props.href) {
925
+ return /*#__PURE__*/external_react_["createElement"](StyledLink, extends_default()({}, commonProps, {
926
+ to: this.props.href,
927
+ role: this.props.role,
928
+ target: this.props.target || undefined,
929
+ "aria-disabled": this.props.disabled ? "true" : undefined
930
+ }), this.props.children(clickableState));
931
+ } else if (activeHref && !useClient) {
932
+ return /*#__PURE__*/external_react_["createElement"](StyledAnchor, extends_default()({}, commonProps, {
933
+ href: this.props.href,
934
+ role: this.props.role,
935
+ target: this.props.target || undefined,
936
+ "aria-disabled": this.props.disabled ? "true" : undefined
937
+ }), this.props.children(clickableState));
938
+ } else {
939
+ return /*#__PURE__*/external_react_["createElement"](StyledButton, extends_default()({}, commonProps, {
940
+ type: "button",
941
+ disabled: this.props.disabled
942
+ }), this.props.children(clickableState));
943
+ }
944
+ };
945
+ }
946
+
947
+ render() {
948
+ const _this$props = this.props,
949
+ {
950
+ href,
951
+ onClick,
952
+ skipClientNav,
953
+ beforeNav = undefined,
954
+ safeWithNav = undefined,
955
+ style,
956
+ target = undefined,
957
+ testId,
958
+ onKeyDown,
959
+ onKeyUp,
960
+ hideDefaultFocusRing,
961
+ light,
962
+ disabled
963
+ } = _this$props,
964
+ restProps = objectWithoutPropertiesLoose_default()(_this$props, _excluded);
965
+
966
+ const ClickableBehavior = getClickableBehavior(href, skipClientNav, this.context.router);
967
+
968
+ const getStyle = state => [styles.reset, styles.link, !hideDefaultFocusRing && state.focused && (light ? styles.focusedLight : styles.focused), style];
969
+
970
+ if (beforeNav) {
971
+ return /*#__PURE__*/external_react_["createElement"](ClickableBehavior, {
972
+ href: href,
973
+ onClick: onClick,
974
+ beforeNav: beforeNav,
975
+ safeWithNav: safeWithNav,
976
+ onKeyDown: onKeyDown,
977
+ onKeyUp: onKeyUp,
978
+ disabled: disabled
979
+ }, (state, childrenProps) => this.getCorrectTag(state, extends_default()({}, restProps, {
980
+ "data-test-id": testId,
981
+ style: getStyle(state)
982
+ }, childrenProps)));
983
+ } else {
984
+ return /*#__PURE__*/external_react_["createElement"](ClickableBehavior, {
985
+ href: href,
986
+ onClick: onClick,
987
+ safeWithNav: safeWithNav,
988
+ onKeyDown: onKeyDown,
989
+ onKeyUp: onKeyUp,
990
+ target: target,
991
+ disabled: disabled
992
+ }, (state, childrenProps) => this.getCorrectTag(state, extends_default()({}, restProps, {
993
+ "data-test-id": testId,
994
+ style: getStyle(state)
995
+ }, childrenProps)));
996
+ }
997
+ }
998
+
999
+ } // Source: https://gist.github.com/MoOx/9137295
1000
+
1001
+ clickable_Clickable.contextTypes = {
1002
+ router: external_prop_types_["any"]
1003
+ };
1004
+ clickable_Clickable.defaultProps = {
1005
+ light: false,
1006
+ disabled: false,
1007
+ "aria-label": ""
1008
+ };
1009
+ const styles = external_aphrodite_["StyleSheet"].create({
1010
+ reset: {
1011
+ border: "none",
1012
+ margin: 0,
1013
+ padding: 0,
1014
+ width: "auto",
1015
+ overflow: "visible",
1016
+ background: "transparent",
1017
+ textDecoration: "none",
1018
+
1019
+ /* inherit font & color from ancestor */
1020
+ color: "inherit",
1021
+ font: "inherit",
1022
+ boxSizing: "border-box",
1023
+ // This removes the 300ms click delay on mobile browsers by indicating that
1024
+ // "double-tap to zoom" shouldn't be used on this element.
1025
+ touchAction: "manipulation",
1026
+ userSelect: "none",
1027
+ // This is usual frowned upon b/c of accessibility. We expect users of Clickable
1028
+ // to define their own focus styles.
1029
+ outline: "none",
1030
+
1031
+ /* Normalize `line-height`. Cannot be changed from `normal` in Firefox 4+. */
1032
+ lineHeight: "normal",
1033
+
1034
+ /* Corrects font smoothing for webkit */
1035
+ WebkitFontSmoothing: "inherit",
1036
+ MozOsxFontSmoothing: "inherit"
1037
+ },
1038
+ link: {
1039
+ cursor: "pointer"
1040
+ },
1041
+ focused: {
1042
+ outline: `solid 2px ${es.blue}`
1043
+ },
1044
+ focusedLight: {
1045
+ outline: `solid 2px ${es.white}`
1046
+ }
1047
+ });
1048
+ // CONCATENATED MODULE: ./packages/wonder-blocks-clickable/src/index.js
1049
+
1050
+
1051
+
1052
+
1053
+
1054
+
1055
+ /***/ })
1056
+ /******/ ]);