@twreporter/react-article-components 1.6.1 → 1.6.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.
@@ -15,8 +15,6 @@ var _react = _interopRequireWildcard(require("react"));
15
15
 
16
16
  var _styledComponents = _interopRequireWildcard(require("styled-components"));
17
17
 
18
- var _reactScroll = require("react-scroll");
19
-
20
18
  var _body = _interopRequireDefault(require("./body"));
21
19
 
22
20
  var _desktopAside = _interopRequireDefault(require("./aside/desktop-aside"));
@@ -384,14 +382,12 @@ var Article = /*#__PURE__*/function (_PureComponent) {
384
382
  key: _.get(post, 'slug', 'article-page-body-key'),
385
383
  brief: _.get(post, 'brief.api_data'),
386
384
  content: _.get(post, 'content.api_data')
387
- })), metadataAndToolsJSX), /*#__PURE__*/_react["default"].createElement(_donationBox["default"], null), /*#__PURE__*/_react["default"].createElement(_reactScroll.Element, {
388
- name: _anchor.relatedPostAnchor
389
- }, /*#__PURE__*/_react["default"].createElement(_license["default"], {
385
+ })), metadataAndToolsJSX), /*#__PURE__*/_react["default"].createElement(_donationBox["default"], null), /*#__PURE__*/_react["default"].createElement(_license["default"], {
390
386
  license: post.copyright,
391
387
  publishedDate: post.published_date,
392
- id: _anchor.relatedPostAnchor // current scroll to releated post anchor
388
+ id: _anchor.RELATED_POST_ANCHOR_ID // current scroll to releated post anchor
393
389
 
394
- })), /*#__PURE__*/_react["default"].createElement(StyledSeparationCurve, null), /*#__PURE__*/_react["default"].createElement(RelatedBlock, null, /*#__PURE__*/_react["default"].createElement(_related["default"], {
390
+ }), /*#__PURE__*/_react["default"].createElement(StyledSeparationCurve, null), /*#__PURE__*/_react["default"].createElement(RelatedBlock, null, /*#__PURE__*/_react["default"].createElement(_related["default"], {
395
391
  id: post.id,
396
392
  data: relatedPosts,
397
393
  hasMore: hasMoreRelateds,
@@ -13,8 +13,6 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
13
13
 
14
14
  var _styledComponents = _interopRequireWildcard(require("styled-components"));
15
15
 
16
- var _reactScroll = require("react-scroll");
17
-
18
16
  var _dynamicComponentsContext = _interopRequireDefault(require("../../contexts/dynamic-components-context"));
19
17
 
20
18
  var _aside = _interopRequireDefault(require("../../constants/prop-types/aside"));
@@ -43,6 +41,10 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio
43
41
 
44
42
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
45
43
 
44
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
45
+
46
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
47
+
46
48
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
47
49
 
48
50
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
@@ -455,23 +457,18 @@ var RelatedPost = function RelatedPost() {
455
457
  var theme = themeContext.name === _theme["default"].article.v2.photo ? 'photography' : 'normal';
456
458
  var releaseBranch = themeContext.releaseBranch;
457
459
 
458
- var scrollToBottom = function scrollToBottom() {
459
- _reactScroll.scroller.scrollTo(_anchor.relatedPostAnchor, {
460
- duration: 800,
461
- smooth: 'easeInOutQuint'
462
- });
463
- };
460
+ var anchorScrollAttr = _defineProperty({}, _anchor.ARTICLE_ANCHOR_SCROLL, 'true');
464
461
 
465
- return /*#__PURE__*/_react["default"].createElement(ButtonContainer, {
466
- onClick: scrollToBottom
467
- }, /*#__PURE__*/_react["default"].createElement(_button.IconWithTextButton, {
462
+ return /*#__PURE__*/_react["default"].createElement(ButtonContainer, null, /*#__PURE__*/_react["default"].createElement("a", _extends({
463
+ href: "#".concat(_anchor.RELATED_POST_ANCHOR_ID)
464
+ }, anchorScrollAttr), /*#__PURE__*/_react["default"].createElement(_button.IconWithTextButton, {
468
465
  text: "\u76F8\u95DC\u6587\u7AE0",
469
466
  iconComponent: /*#__PURE__*/_react["default"].createElement(_icon.Article, {
470
467
  releaseBranch: releaseBranch
471
468
  }),
472
469
  theme: theme,
473
470
  hideText: hideText
474
- }));
471
+ })));
475
472
  };
476
473
 
477
474
  var ToolBar = function ToolBar(_ref9) {
@@ -7,17 +7,13 @@ Object.defineProperty(exports, "__esModule", {
7
7
  });
8
8
  exports["default"] = exports.Caption = exports.Block = void 0;
9
9
 
10
- var _body = _interopRequireDefault(require("../../constants/prop-types/body"));
10
+ var _react = _interopRequireWildcard(require("react"));
11
11
 
12
12
  var _propTypes = _interopRequireDefault(require("prop-types"));
13
13
 
14
- var _react = _interopRequireDefault(require("react"));
15
-
16
14
  var _styledComponents = _interopRequireDefault(require("styled-components"));
17
15
 
18
- var _theme = _interopRequireDefault(require("../../constants/theme"));
19
-
20
- var _color = _interopRequireDefault(require("../../constants/color"));
16
+ var _reactWaypoint = require("react-waypoint");
21
17
 
22
18
  var _forEach = _interopRequireDefault(require("lodash/forEach"));
23
19
 
@@ -25,8 +21,32 @@ var _get = _interopRequireDefault(require("lodash/get"));
25
21
 
26
22
  var _merge = _interopRequireDefault(require("lodash/merge"));
27
23
 
24
+ var _theme = _interopRequireDefault(require("../../constants/theme"));
25
+
26
+ var _color = _interopRequireDefault(require("../../constants/color"));
27
+
28
+ var _body = _interopRequireDefault(require("../../constants/prop-types/body"));
29
+
28
30
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
29
31
 
32
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
33
+
34
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
35
+
36
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
37
+
38
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
39
+
40
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
41
+
42
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
43
+
44
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
45
+
46
+ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
47
+
48
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
49
+
30
50
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
31
51
 
32
52
  function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
@@ -92,6 +112,8 @@ function dispatchWindowLoadEvent() {
92
112
  window.dispatchEvent(loadEvent);
93
113
  }
94
114
 
115
+ var infogramEmbed = 'infogram';
116
+
95
117
  var EmbeddedCode = /*#__PURE__*/function (_React$PureComponent) {
96
118
  _inherits(EmbeddedCode, _React$PureComponent);
97
119
 
@@ -103,18 +125,15 @@ var EmbeddedCode = /*#__PURE__*/function (_React$PureComponent) {
103
125
  _classCallCheck(this, EmbeddedCode);
104
126
 
105
127
  _this = _super.call(this, props);
106
- _this._embedded = /*#__PURE__*/_react["default"].createRef();
107
- return _this;
108
- }
109
128
 
110
- _createClass(EmbeddedCode, [{
111
- key: "componentDidMount",
112
- value: function componentDidMount() {
113
- var _this2 = this;
129
+ _defineProperty(_assertThisInitialized(_this), "state", {
130
+ isLoaded: false
131
+ });
114
132
 
115
- var node = this._embedded.current;
133
+ _defineProperty(_assertThisInitialized(_this), "executeScript", function () {
134
+ var node = _this._embedded.current;
116
135
 
117
- var scripts = _.get(this.props, ['data', 'content', 0, 'scripts']);
136
+ var scripts = _.get(_this.props, ['data', 'content', 0, 'scripts']);
118
137
 
119
138
  if (node && Array.isArray(scripts)) {
120
139
  var scriptsCount = scripts.length;
@@ -129,7 +148,7 @@ var EmbeddedCode = /*#__PURE__*/function (_React$PureComponent) {
129
148
  try {
130
149
  scriptEle.setAttribute(name, value);
131
150
  } catch (err) {
132
- console.error('Failed to set an attribute to the embbeded script.\n', "embedded element id: ".concat(_.get(_this2.props, 'data.id', ''), "\n"), "attribute name: ".concat(name, "\n"), "attribute value: ".concat(value, "\n"), 'error:\n', err);
151
+ console.error('Failed to set an attribute to the embbeded script.\n', "embedded element id: ".concat(_.get(_this.props, 'data.id', ''), "\n"), "attribute name: ".concat(name, "\n"), "attribute value: ".concat(value, "\n"), 'error:\n', err);
133
152
  }
134
153
  });
135
154
 
@@ -155,31 +174,75 @@ var EmbeddedCode = /*#__PURE__*/function (_React$PureComponent) {
155
174
 
156
175
  node.appendChild(scriptsFragment);
157
176
  }
177
+ });
178
+
179
+ _defineProperty(_assertThisInitialized(_this), "loadEmbed", function () {
180
+ if (!_this.state.isLoaded) {
181
+ _this.setState({
182
+ isLoaded: true
183
+ }, _this.executeScript);
184
+ }
185
+ });
186
+
187
+ _this._embedded = /*#__PURE__*/_react["default"].createRef();
188
+
189
+ var _$get = _.get(_this.props, ['data', 'content', 0], {}),
190
+ caption = _$get.caption,
191
+ embeddedCodeWithoutScript = _$get.embeddedCodeWithoutScript;
192
+
193
+ _this._caption = caption;
194
+ _this._embeddedCodeWithoutScript = embeddedCodeWithoutScript;
195
+ return _this;
196
+ }
197
+
198
+ _createClass(EmbeddedCode, [{
199
+ key: "componentDidMount",
200
+ value: function componentDidMount() {
201
+ var _this$_embeddedCodeWi;
202
+
203
+ // Delay loading infogram in loadEmbed()
204
+ if (!((_this$_embeddedCodeWi = this._embeddedCodeWithoutScript) !== null && _this$_embeddedCodeWi !== void 0 && _this$_embeddedCodeWi.includes(infogramEmbed))) {
205
+ this.setState({
206
+ isLoaded: true
207
+ }, this.executeScript);
208
+ }
209
+ }
210
+ }, {
211
+ key: "componentWillUnmount",
212
+ value: function componentWillUnmount() {
213
+ this._embedded = null;
214
+ this._caption = null;
215
+ this._embeddedCodeWithoutScript = null;
158
216
  }
159
217
  }, {
160
218
  key: "render",
161
219
  value: function render() {
162
- var className = this.props.className;
220
+ var _this$_embeddedCodeWi2;
163
221
 
164
- var _$get = _.get(this.props, ['data', 'content', 0], {}),
165
- caption = _$get.caption,
166
- embeddedCodeWithoutScript = _$get.embeddedCodeWithoutScript;
222
+ var className = this.props.className;
167
223
 
168
- return /*#__PURE__*/_react["default"].createElement("div", {
224
+ var embed = /*#__PURE__*/_react["default"].createElement("div", {
169
225
  className: className
170
226
  }, /*#__PURE__*/_react["default"].createElement(Block, {
171
227
  ref: this._embedded,
172
228
  dangerouslySetInnerHTML: {
173
- __html: embeddedCodeWithoutScript
229
+ __html: this._embeddedCodeWithoutScript
174
230
  }
175
- }), caption ? /*#__PURE__*/_react["default"].createElement(Caption, null, caption) : null);
231
+ }), this._caption ? /*#__PURE__*/_react["default"].createElement(Caption, null, this._caption) : null);
232
+
233
+ if ((_this$_embeddedCodeWi2 = this._embeddedCodeWithoutScript) !== null && _this$_embeddedCodeWi2 !== void 0 && _this$_embeddedCodeWi2.includes(infogramEmbed)) {
234
+ return this.state.isLoaded ? embed : null;
235
+ }
236
+
237
+ return embed;
176
238
  }
177
239
  }]);
178
240
 
179
241
  return EmbeddedCode;
180
- }(_react["default"].PureComponent);
242
+ }(_react["default"].PureComponent); // Serious layout shifts show up when loading bunch of infograms due to lack of heights,
243
+ // so here we apply waypoint wrapper to load infogram dynamically to avoid layout shifts for anchors.
244
+ // https://twreporter-org.atlassian.net/browse/TWREPORTER-60
181
245
 
182
- exports["default"] = EmbeddedCode;
183
246
 
184
247
  _defineProperty(EmbeddedCode, "propTypes", {
185
248
  className: _propTypes["default"].string,
@@ -188,4 +251,57 @@ _defineProperty(EmbeddedCode, "propTypes", {
188
251
 
189
252
  _defineProperty(EmbeddedCode, "defaultProps", {
190
253
  className: ''
191
- });
254
+ });
255
+
256
+ var WayPointWrapper = function WayPointWrapper(props) {
257
+ var isScrollingToAnchor = props.isScrollingToAnchor;
258
+
259
+ var _useState = (0, _react.useState)(false),
260
+ _useState2 = _slicedToArray(_useState, 2),
261
+ isInViewPort = _useState2[0],
262
+ setIsInViewPort = _useState2[1];
263
+
264
+ var embedRef = (0, _react.useRef)(null);
265
+ (0, _react.useEffect)(function () {
266
+ if (!isScrollingToAnchor && isInViewPort) {
267
+ embedRef.current.loadEmbed();
268
+ }
269
+ }, [isScrollingToAnchor]);
270
+
271
+ var onEnter = function onEnter() {
272
+ setIsInViewPort(true);
273
+
274
+ if (!isScrollingToAnchor) {
275
+ embedRef.current.loadEmbed();
276
+ }
277
+ };
278
+
279
+ var onLeave = function onLeave() {
280
+ setIsInViewPort(false);
281
+ };
282
+
283
+ return (
284
+ /*#__PURE__*/
285
+ // Note: When an anchor is exactly below an infogram embed, onLeave() is not fired
286
+ // when jumping to the anchor because infogram's bottom boundary is just overlapped
287
+ // with viewport's top boundary. Setting topOffset is to shorter infogram's boundary
288
+ // a little bit to make sure onLeave() fires.
289
+ _react["default"].createElement(_reactWaypoint.Waypoint, {
290
+ onEnter: onEnter,
291
+ onLeave: onLeave,
292
+ fireOnRapidScroll: false,
293
+ topOffset: 5
294
+ }, /*#__PURE__*/_react["default"].createElement("div", null, /*#__PURE__*/_react["default"].createElement(EmbeddedCode, _extends({}, props, {
295
+ ref: embedRef
296
+ }))))
297
+ );
298
+ };
299
+
300
+ WayPointWrapper.defaultProps = {
301
+ isScrollingToAnchor: false
302
+ };
303
+ WayPointWrapper.propTypes = {
304
+ isScrollingToAnchor: _propTypes["default"].bool
305
+ };
306
+ var _default = WayPointWrapper;
307
+ exports["default"] = _default;
@@ -56,6 +56,10 @@ var _get = _interopRequireDefault(require("lodash/get"));
56
56
 
57
57
  var _map = _interopRequireDefault(require("lodash/map"));
58
58
 
59
+ var _smoothScroll = _interopRequireDefault(require("smooth-scroll/dist/smooth-scroll"));
60
+
61
+ var _anchor = require("../../constants/anchor");
62
+
59
63
  var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7, _templateObject8, _templateObject9, _templateObject10, _templateObject11, _templateObject12, _templateObject13, _templateObject14, _templateObject15, _templateObject16, _templateObject17, _templateObject18, _templateObject19, _templateObject20, _templateObject21, _templateObject22, _templateObject23;
60
64
 
61
65
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -203,12 +207,14 @@ var ClearFloat = /*#__PURE__*/_styledComponents["default"].div.withConfig({
203
207
  *
204
208
  * @export
205
209
  * @param {ElementData} [data={}]
210
+ * @param {boolean} isScrollingToAnchor - state of scrolling to an anchor or not
206
211
  * @returns
207
212
  */
208
213
 
209
214
 
210
215
  function renderElement() {
211
216
  var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
217
+ var isScrollingToAnchor = arguments.length > 1 ? arguments[1] : undefined;
212
218
  var isCenterAligned = data.alignment === _elementAlignment["default"].center || data.alignment === _elementAlignment["default"].centerSmall;
213
219
 
214
220
  switch (data.type) {
@@ -270,14 +276,16 @@ function renderElement() {
270
276
  return /*#__PURE__*/_react["default"].createElement(AlignRight, {
271
277
  key: data.id
272
278
  }, /*#__PURE__*/_react["default"].createElement(StyledEmbedded, {
273
- data: data
279
+ data: data,
280
+ isScrollingToAnchor: isScrollingToAnchor
274
281
  }));
275
282
  }
276
283
 
277
284
  case _elementAlignment["default"].centerSmall:
278
285
  return /*#__PURE__*/_react["default"].createElement(StyledEmbeddedNormal, {
279
286
  key: data.id,
280
- data: data
287
+ data: data,
288
+ isScrollingToAnchor: isScrollingToAnchor
281
289
  });
282
290
 
283
291
  case _elementAlignment["default"].center:
@@ -285,7 +293,8 @@ function renderElement() {
285
293
  {
286
294
  return /*#__PURE__*/_react["default"].createElement(StyledEmbedded, {
287
295
  key: data.id,
288
- data: data
296
+ data: data,
297
+ isScrollingToAnchor: isScrollingToAnchor
289
298
  });
290
299
  }
291
300
  }
@@ -382,36 +391,77 @@ function renderElement() {
382
391
  }
383
392
  }
384
393
 
385
- var Body = /*#__PURE__*/function (_PureComponent) {
386
- _inherits(Body, _PureComponent);
394
+ var Body = /*#__PURE__*/function (_Component) {
395
+ _inherits(Body, _Component);
387
396
 
388
397
  var _super = _createSuper(Body);
389
398
 
390
- function Body() {
399
+ function Body(props) {
391
400
  var _this;
392
401
 
393
402
  _classCallCheck(this, Body);
394
403
 
395
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
396
- args[_key] = arguments[_key];
397
- }
398
-
399
- _this = _super.call.apply(_super, [this].concat(args));
404
+ _this = _super.call(this, props);
400
405
 
401
406
  _defineProperty(_assertThisInitialized(_this), "tocManager", _tableOfContents["default"].createManager());
402
407
 
408
+ _defineProperty(_assertThisInitialized(_this), "_scrollStartHandler", function () {
409
+ _this._onStartScrollingToAnchor(true);
410
+ });
411
+
412
+ _defineProperty(_assertThisInitialized(_this), "_scrollStopHandler", function () {
413
+ // Wait for a short time to avoid trigger waypoint's onEnter() of infogram embed close to the anchor
414
+ setTimeout(function () {
415
+ return _this._onStartScrollingToAnchor(false);
416
+ }, _anchor.WAIT_AFTER_REACH_ANCHOR);
417
+ });
418
+
419
+ _defineProperty(_assertThisInitialized(_this), "_onStartScrollingToAnchor", function (isScrollingToAnchor, callback) {
420
+ _this.setState({
421
+ isScrollingToAnchor: isScrollingToAnchor
422
+ }, function () {
423
+ if (callback) {
424
+ callback();
425
+ }
426
+ });
427
+ });
428
+
403
429
  _defineProperty(_assertThisInitialized(_this), "_buildContentElement", function (data, index) {
404
430
  if (!data.id) {
405
431
  data.id = "body_element_".concat(index);
406
432
  }
407
433
 
408
- return renderElement(data);
434
+ return renderElement(data, _this.state.isScrollingToAnchor);
409
435
  });
410
436
 
437
+ _this.state = {
438
+ isScrollingToAnchor: false
439
+ };
411
440
  return _this;
412
441
  }
413
442
 
414
443
  _createClass(Body, [{
444
+ key: "componentDidMount",
445
+ value: function componentDidMount() {
446
+ // We apply smooth scroll only to ARTICLE_ANCHOR_SCROLL('article-anchor-scroll') selector
447
+ // ex: <a article-anchor-scroll="true" href="#..."" />
448
+ // eslint-disable-next-line no-new
449
+ new _smoothScroll["default"]("[".concat(_anchor.ARTICLE_ANCHOR_SCROLL, "]"), {
450
+ speed: _anchor.ANCHOR_SCROLL_DURATION,
451
+ speedAsDuration: true,
452
+ easing: 'easeInOutQuint',
453
+ emitEvents: true
454
+ });
455
+ document.addEventListener('scrollStart', this._scrollStartHandler, false);
456
+ document.addEventListener('scrollStop', this._scrollStopHandler, false);
457
+ }
458
+ }, {
459
+ key: "componentWillUnmount",
460
+ value: function componentWillUnmount() {
461
+ document.removeEventListener('scrollStart', this._scrollStartHandler);
462
+ document.removeEventListener('scrollStop', this._scrollStopHandler);
463
+ }
464
+ }, {
415
465
  key: "render",
416
466
  value: function render() {
417
467
  var _this2 = this;
@@ -439,13 +489,14 @@ var Body = /*#__PURE__*/function (_PureComponent) {
439
489
  return /*#__PURE__*/_react["default"].createElement("div", null, /*#__PURE__*/_react["default"].createElement(StyledBrief, {
440
490
  data: brief
441
491
  }), contentJsx, /*#__PURE__*/_react["default"].createElement(ClearFloat, null), enableTOC ? /*#__PURE__*/_react["default"].createElement(_tableOfContents["default"].React.TableOfContents, {
442
- manager: this.tocManager
492
+ manager: this.tocManager,
493
+ onStartScrollingToAnchor: this._onStartScrollingToAnchor
443
494
  }) : null);
444
495
  }
445
496
  }]);
446
497
 
447
498
  return Body;
448
- }(_react.PureComponent);
499
+ }(_react.Component);
449
500
 
450
501
  exports["default"] = Body;
451
502
 
@@ -7,17 +7,19 @@ Object.defineProperty(exports, "__esModule", {
7
7
  });
8
8
  exports["default"] = void 0;
9
9
 
10
- var _mediaQuery = _interopRequireDefault(require("@twreporter/core/lib/utils/media-query"));
10
+ var _react = _interopRequireWildcard(require("react"));
11
11
 
12
12
  var _propTypes = _interopRequireDefault(require("prop-types"));
13
13
 
14
- var _body = _interopRequireDefault(require("../../constants/prop-types/body"));
14
+ var _styledComponents = _interopRequireWildcard(require("styled-components"));
15
15
 
16
- var _react = _interopRequireWildcard(require("react"));
16
+ var _get = _interopRequireDefault(require("lodash/get"));
17
17
 
18
- var _css = _interopRequireDefault(require("../../constants/css"));
18
+ var _mediaQuery = _interopRequireDefault(require("@twreporter/core/lib/utils/media-query"));
19
19
 
20
- var _styledComponents = _interopRequireWildcard(require("styled-components"));
20
+ var _body = _interopRequireDefault(require("../../constants/prop-types/body"));
21
+
22
+ var _css = _interopRequireDefault(require("../../constants/css"));
21
23
 
22
24
  var _theme = _interopRequireDefault(require("../../constants/theme"));
23
25
 
@@ -25,16 +27,16 @@ var _typography = _interopRequireDefault(require("../../constants/typography"));
25
27
 
26
28
  var _color = _interopRequireDefault(require("../../constants/color"));
27
29
 
28
- var _get = _interopRequireDefault(require("lodash/get"));
30
+ var _anchor = require("../../constants/anchor");
29
31
 
30
32
  var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6;
31
33
 
34
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
35
+
32
36
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
33
37
 
34
38
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
35
39
 
36
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
37
-
38
40
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
39
41
 
40
42
  function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
@@ -121,11 +123,18 @@ var Infobox = /*#__PURE__*/function (_PureComponent) {
121
123
 
122
124
  var title = _.get(data, ['content', 0, 'title'], '');
123
125
 
124
- return contentHtmlString ? /*#__PURE__*/_react["default"].createElement(Container, {
126
+ var anchorRegex = /<a([^>]*)>/g;
127
+ var hashRegex = /href={?["']#/;
128
+ var mutatedContentHtmlString = contentHtmlString === null || contentHtmlString === void 0 ? void 0 : contentHtmlString.replace(anchorRegex, function (anchorString, attrs) {
129
+ // Here we insert a custom attribute 'ARTICLE_ANCHOR_SCROLL' in order to apply
130
+ // smooth scroll effect on <a href="#..."> anchors inside infobox
131
+ return hashRegex.exec(anchorString) ? "<a ".concat(_anchor.ARTICLE_ANCHOR_SCROLL, "=\"true\" ").concat(attrs, ">") : anchorString;
132
+ });
133
+ return mutatedContentHtmlString ? /*#__PURE__*/_react["default"].createElement(Container, {
125
134
  className: className
126
135
  }, title ? /*#__PURE__*/_react["default"].createElement(Title, null, title) : null, /*#__PURE__*/_react["default"].createElement(Content, {
127
136
  dangerouslySetInnerHTML: {
128
- __html: contentHtmlString
137
+ __html: mutatedContentHtmlString
129
138
  }
130
139
  })) : null;
131
140
  }
@@ -17,6 +17,8 @@ var _tableOfContents = _interopRequireDefault(require("@twreporter/react-compone
17
17
 
18
18
  var _map = _interopRequireDefault(require("lodash/map"));
19
19
 
20
+ var _anchor = require("../../constants/anchor");
21
+
20
22
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
21
23
 
22
24
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -136,7 +138,8 @@ var TableOfContents = /*#__PURE__*/function (_React$PureComponent) {
136
138
 
137
139
  var _this$props = this.props,
138
140
  className = _this$props.className,
139
- manager = _this$props.manager;
141
+ manager = _this$props.manager,
142
+ onStartScrollingToAnchor = _this$props.onStartScrollingToAnchor;
140
143
  var isExpanded = this.state.isExpanded;
141
144
  return /*#__PURE__*/_react["default"].createElement("div", {
142
145
  className: "hidden-print",
@@ -144,13 +147,21 @@ var TableOfContents = /*#__PURE__*/function (_React$PureComponent) {
144
147
  }, /*#__PURE__*/_react["default"].createElement(_tableOfContents["default"].React.TableOfContents, {
145
148
  className: className,
146
149
  manager: manager,
150
+ scrollDuration: _anchor.ANCHOR_SCROLL_DURATION,
147
151
  render: function render(anchors, highlightAnchor, handleAnchorClick) {
148
152
  var anchorsJSX = _.map(anchors, function (anchor) {
149
153
  var toHighlight = anchor === highlightAnchor;
150
154
  return /*#__PURE__*/_react["default"].createElement(Styled.TOCRow, {
151
155
  key: anchor.anchorID,
152
156
  onClick: function onClick() {
153
- return handleAnchorClick(anchor.anchorID);
157
+ onStartScrollingToAnchor(true, function () {
158
+ return handleAnchorClick(anchor.anchorID, function () {
159
+ // Wait for a short time to avoid trigger waypoint's onEnter() of infogram embed close to the anchor
160
+ setTimeout(function () {
161
+ return onStartScrollingToAnchor(false);
162
+ }, _anchor.WAIT_AFTER_REACH_ANCHOR);
163
+ });
164
+ });
154
165
  }
155
166
  }, /*#__PURE__*/_react["default"].createElement(Styled.TOCIndicator, {
156
167
  toHighlight: toHighlight,
@@ -178,7 +189,8 @@ var TableOfContents = /*#__PURE__*/function (_React$PureComponent) {
178
189
 
179
190
  _defineProperty(TableOfContents, "propTypes", {
180
191
  className: _propTypes["default"].string,
181
- manager: _tableOfContents["default"].React.TableOfContents.propTypes.manager
192
+ manager: _tableOfContents["default"].React.TableOfContents.propTypes.manager,
193
+ onStartScrollingToAnchor: _propTypes["default"].func
182
194
  });
183
195
 
184
196
  _defineProperty(TableOfContents, "defaultProps", {
@@ -3,6 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.relatedPostAnchor = void 0;
7
- var relatedPostAnchor = 'related-post-anchor';
8
- exports.relatedPostAnchor = relatedPostAnchor;
6
+ exports.ANCHOR_SCROLL_DURATION = exports.WAIT_AFTER_REACH_ANCHOR = exports.ARTICLE_ANCHOR_SCROLL = exports.RELATED_POST_ANCHOR_ID = void 0;
7
+ var RELATED_POST_ANCHOR_ID = 'related-post-anchor';
8
+ exports.RELATED_POST_ANCHOR_ID = RELATED_POST_ANCHOR_ID;
9
+ var ARTICLE_ANCHOR_SCROLL = 'article-anchor-scroll';
10
+ exports.ARTICLE_ANCHOR_SCROLL = ARTICLE_ANCHOR_SCROLL;
11
+ var WAIT_AFTER_REACH_ANCHOR = 20;
12
+ exports.WAIT_AFTER_REACH_ANCHOR = WAIT_AFTER_REACH_ANCHOR;
13
+ var ANCHOR_SCROLL_DURATION = 500;
14
+ exports.ANCHOR_SCROLL_DURATION = ANCHOR_SCROLL_DURATION;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twreporter/react-article-components",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "The Reporter react article components, which are used in article page",
5
5
  "main": "lib/components/article-page.js",
6
6
  "repository": "https://github.com/twreporter/twreporter-npm-packages.git",
@@ -24,21 +24,21 @@
24
24
  "webpack-dev-server": "^3.7.2"
25
25
  },
26
26
  "dependencies": {
27
- "@twreporter/core": "^1.4.1",
28
- "@twreporter/react-components": "^8.11.0",
27
+ "@twreporter/core": "^1.4.2",
28
+ "@twreporter/react-components": "^8.11.1",
29
29
  "@twreporter/redux": "^7.2.2",
30
- "@twreporter/universal-header": "^2.2.11",
30
+ "@twreporter/universal-header": "^2.2.12",
31
31
  "howler": "^2.2.3",
32
32
  "lodash": "^4.17.11",
33
33
  "memoize-one": "^5.0.5",
34
34
  "prop-types": "^15.0.0",
35
35
  "react": "^16.3.0",
36
- "react-scroll": "^1.8.7",
37
36
  "react-waypoint": "^9.0.2",
37
+ "smooth-scroll": "16.1.3",
38
38
  "styled-components": "^4.0.0"
39
39
  },
40
40
  "files": [
41
41
  "lib"
42
42
  ],
43
- "gitHead": "244cb67b289aa61d0c847dc2f07036d2472269c6"
43
+ "gitHead": "18008e631123e851e1a31dc054918e2a1973514b"
44
44
  }