Просмотр исходного кода

Merge pull request #3218 from weseek/imprv/improve-link-edit-modal

Imprv/improve link edit modal
Yuki Takei 5 лет назад
Родитель
Сommit
fbafa16f9f

+ 1 - 1
package.json

@@ -231,7 +231,7 @@
     "react-codemirror2": "^6.0.0",
     "react-copy-to-clipboard": "^5.0.1",
     "react-dom": "^16.8.3",
-    "react-dropzone": "^10.1.3",
+    "react-dropzone": "^11.2.4",
     "react-frame-component": "^4.0.0",
     "react-hotkeys": "^2.0.0",
     "react-i18next": "^11.1.0",

+ 2 - 6
src/client/js/components/PageEditor/CodeMirrorEditor.jsx

@@ -56,9 +56,7 @@ require('../../util/codemirror/autorefresh.ext');
 
 
 const MARKDOWN_TABLE_ACTIVATED_CLASS = 'markdown-table-activated';
-// TODO: activate by GW-3443
-// const MARKDOWN_LINK_ACTIVATED_CLASS = 'markdown-link-activated';
-const MARKDOWN_LINK_ACTIVATED_CLASS = '';
+const MARKDOWN_LINK_ACTIVATED_CLASS = 'markdown-link-activated';
 
 export default class CodeMirrorEditor extends AbstractEditor {
 
@@ -768,9 +766,7 @@ export default class CodeMirrorEditor extends AbstractEditor {
         color={null}
         size="sm"
         title="Link"
-        // TODO: activate by GW-3443
-        // onClick={this.showLinkEditHandler}
-        onClick={this.createReplaceSelectionHandler('[', ']()')}
+        onClick={this.showLinkEditHandler}
       >
         <EditorIcon icon="Link" />
       </Button>,

+ 99 - 102
src/client/js/components/PageEditor/LinkEditModal.jsx

@@ -9,11 +9,9 @@ import {
   PopoverBody,
 } from 'reactstrap';
 
-import { debounce } from 'throttle-debounce';
-
 import path from 'path';
 import validator from 'validator';
-import Preview from './Preview';
+import PreviewWithSuspense from './PreviewWithSuspense';
 import PagePreviewIcon from '../Icons/PagePreviewIcon';
 
 import AppContainer from '../../services/AppContainer';
@@ -39,7 +37,6 @@ class LinkEditModal extends React.PureComponent {
       markdown: '',
       previewError: '',
       permalink: '',
-      linkText: '',
       isPreviewOpen: false,
     };
 
@@ -56,18 +53,9 @@ class LinkEditModal extends React.PureComponent {
     this.toggleIsUsePamanentLink = this.toggleIsUsePamanentLink.bind(this);
     this.save = this.save.bind(this);
     this.generateLink = this.generateLink.bind(this);
-    this.renderPreview = this.renderPreview.bind(this);
     this.getRootPath = this.getRootPath.bind(this);
     this.toggleIsPreviewOpen = this.toggleIsPreviewOpen.bind(this);
-    this.generateAndSetPreviewDebounced = debounce(200, this.generateAndSetPreview.bind(this));
-  }
-
-  componentDidUpdate(prevProps, prevState) {
-    const { linkInputValue: prevLinkInputValue } = prevState;
-    const { linkInputValue } = this.state;
-    if (linkInputValue !== prevLinkInputValue) {
-      this.generateAndSetPreviewDebounced(linkInputValue);
-    }
+    this.setMarkdown = this.setMarkdown.bind(this);
   }
 
   // defaultMarkdownLink is an instance of Linker
@@ -158,21 +146,8 @@ class LinkEditModal extends React.PureComponent {
     this.setState({ isUsePermanentLink: !this.state.isUsePermanentLink, isUseRelativePath: false });
   }
 
-  renderPreview() {
-    if (this.state.markdown !== '') {
-      return (
-        <div className="linkedit-preview">
-          <Preview markdown={this.state.markdown} />
-        </div>
-      );
-    }
-    if (this.state.previewError !== '') {
-      return this.state.previewError;
-    }
-    return 'Page preview here.';
-  }
-
-  async generateAndSetPreview(path) {
+  async setMarkdown() {
+    const path = this.state.linkInputValue;
     let markdown = '';
     let previewError = '';
     let permalink = '';
@@ -192,10 +167,13 @@ class LinkEditModal extends React.PureComponent {
         previewError = err.message;
       }
     }
+    else {
+      previewError = `'${path}' is not a GROWI page.`;
+    }
     this.setState({ markdown, previewError, permalink });
   }
 
-  renderLinkPreview() {
+  getLinkForPreview() {
     const linker = this.generateLink();
 
     if (this.isUsePermanentLink && this.permalink != null) {
@@ -206,16 +184,21 @@ class LinkEditModal extends React.PureComponent {
       linker.label = linker.link;
     }
 
-    const linkText = linker.generateMarkdownText();
+    return linker;
+  }
+
+  renderLinkPreview() {
+    const linker = this.getLinkForPreview();
     return (
-      <div className="d-flex justify-content-between mb-3">
+      <div className="d-flex justify-content-between mb-3 flex-column flex-sm-row">
         <div className="card card-disabled w-100 p-1 mb-0">
           <p className="text-left text-muted mb-1 small">Markdown</p>
-          <p className="text-center text-truncate text-muted">{linkText}</p>
+          <p className="text-center text-truncate text-muted">{linker.generateMarkdownText()}</p>
         </div>
-        <div className="d-flex align-items-center">
+        <div className="d-flex align-items-center justify-content-center">
           <span className="lead mx-3">
-            <i className="fa fa-caret-right"></i>
+            <i className="d-none d-sm-block fa fa-caret-right"></i>
+            <i className="d-sm-none fa fa-caret-down"></i>
           </span>
         </div>
         <div className="card w-100 p-1 mb-0">
@@ -257,8 +240,10 @@ class LinkEditModal extends React.PureComponent {
   }
 
   save() {
+    const linker = this.getLinkForPreview();
+
     if (this.props.onSave != null) {
-      this.props.onSave(this.state.linkText);
+      this.props.onSave(linker.generateMarkdownText());
     }
 
     this.hide();
@@ -289,7 +274,11 @@ class LinkEditModal extends React.PureComponent {
     return type === Linker.types.markdownLink ? path.dirname(pagePath) : pagePath;
   }
 
-  toggleIsPreviewOpen() {
+  async toggleIsPreviewOpen() {
+    // open popover
+    if (this.state.isPreviewOpen === false) {
+      this.setMarkdown();
+    }
     this.setState({ isPreviewOpen: !this.state.isPreviewOpen });
   }
 
@@ -309,14 +298,15 @@ class LinkEditModal extends React.PureComponent {
                 inputName="link"
                 placeholder="Input page path or URL"
                 keywordOnInit={this.state.linkInputValue}
+                behaviorOfResetBtn="clear"
               />
-              <div className="input-group-append">
+              <div className="d-none d-sm-block input-group-append">
                 <button type="button" id="preview-btn" className="btn btn-info btn-page-preview">
                   <PagePreviewIcon />
                 </button>
                 <Popover trigger="focus" placement="right" isOpen={this.state.isPreviewOpen} target="preview-btn" toggle={this.toggleIsPreviewOpen}>
                   <PopoverBody>
-                    {this.renderPreview()}
+                    <PreviewWithSuspense setMarkdown={this.setMarkdown} markdown={this.state.markdown} error={this.state.previewError} />
                   </PopoverBody>
                 </Popover>
               </div>
@@ -334,6 +324,7 @@ class LinkEditModal extends React.PureComponent {
                 value={this.state.labelInputValue}
                 onChange={e => this.handleChangeLabelInput(e.target.value)}
                 disabled={this.state.linkerType === Linker.types.growiLink}
+                placeholder={this.state.linkInputValue}
               />
             </div>
           </div>
@@ -348,73 +339,79 @@ class LinkEditModal extends React.PureComponent {
         <form className="form-group mb-0">
           <div className="form-group row">
             <label className="col-sm-3">Path format</label>
-            <div className="custom-control custom-checkbox custom-checkbox-info custom-control-inline">
-              <input
-                className="custom-control-input"
-                id="relativePath"
-                type="checkbox"
-                checked={this.state.isUseRelativePath}
-                onChange={this.toggleIsUseRelativePath}
-                disabled={!this.state.linkInputValue.startsWith('/') || this.state.linkerType === Linker.types.growiLink}
-              />
-              <label className="custom-control-label" htmlFor="relativePath">
-                Use relative path
-              </label>
-            </div>
-            <div className="custom-control custom-checkbox custom-checkbox-info custom-control-inline">
-              <input
-                className="custom-control-input"
-                id="permanentLink"
-                type="checkbox"
-                checked={this.state.isUsePermanentLink}
-                onChange={this.toggleIsUsePamanentLink}
-                disabled={this.state.permalink === '' || this.state.linkerType === Linker.types.growiLink}
-              />
-              <label className="custom-control-label" htmlFor="permanentLink">
-                Use permanent link
-              </label>
+            <div className="col-sm-9">
+              <div className="custom-control custom-checkbox custom-checkbox-info custom-control-inline">
+                <input
+                  className="custom-control-input"
+                  id="relativePath"
+                  type="checkbox"
+                  checked={this.state.isUseRelativePath}
+                  onChange={this.toggleIsUseRelativePath}
+                  disabled={!this.state.linkInputValue.startsWith('/') || this.state.linkerType === Linker.types.growiLink}
+                />
+                <label className="custom-control-label" htmlFor="relativePath">
+                  Use relative path
+                </label>
+              </div>
+              <div className="custom-control custom-checkbox custom-checkbox-info custom-control-inline">
+                <input
+                  className="custom-control-input"
+                  id="permanentLink"
+                  type="checkbox"
+                  checked={this.state.isUsePermanentLink}
+                  onChange={this.toggleIsUsePamanentLink}
+                  disabled={this.state.permalink === '' || this.state.linkerType === Linker.types.growiLink}
+                />
+                <label className="custom-control-label" htmlFor="permanentLink">
+                  Use permanent link
+                </label>
+              </div>
             </div>
           </div>
           <div className="form-group row mb-0">
             <label className="col-sm-3">Notation</label>
-            <div className="custom-control custom-radio custom-control-inline">
-              <input
-                type="radio"
-                className="custom-control-input"
-                id="markdownType"
-                value={Linker.types.markdownLink}
-                checked={this.state.linkerType === Linker.types.markdownLink}
-                onChange={e => this.handleSelecteLinkerType(e.target.value)}
-              />
-              <label className="custom-control-label" htmlFor="markdownType">
-                Markdown
-              </label>
-            </div>
-            <div className="custom-control custom-radio custom-control-inline">
-              <input
-                type="radio"
-                className="custom-control-input"
-                id="growiType"
-                value={Linker.types.growiLink}
-                checked={this.state.linkerType === Linker.types.growiLink}
-                onChange={e => this.handleSelecteLinkerType(e.target.value)}
-              />
-              <label className="custom-control-label" htmlFor="growiType">
-                Growi original
-              </label>
-            </div>
-            <div className="custom-control custom-radio custom-control-inline">
-              <input
-                type="radio"
-                className="custom-control-input"
-                id="pukiwikiType"
-                value={Linker.types.pukiwikiLink}
-                checked={this.state.linkerType === Linker.types.pukiwikiLink}
-                onChange={e => this.handleSelecteLinkerType(e.target.value)}
-              />
-              <label className="custom-control-label" htmlFor="pukiwikiType">
-                Pukiwiki
-              </label>
+            <div className="col-sm-9">
+              <div className="custom-control custom-radio custom-control-inline">
+                <input
+                  type="radio"
+                  className="custom-control-input"
+                  id="markdownType"
+                  value={Linker.types.markdownLink}
+                  checked={this.state.linkerType === Linker.types.markdownLink}
+                  onChange={e => this.handleSelecteLinkerType(e.target.value)}
+                />
+                <label className="custom-control-label" htmlFor="markdownType">
+                  Markdown
+                </label>
+              </div>
+              <div className="custom-control custom-radio custom-control-inline">
+                <input
+                  type="radio"
+                  className="custom-control-input"
+                  id="growiType"
+                  value={Linker.types.growiLink}
+                  checked={this.state.linkerType === Linker.types.growiLink}
+                  onChange={e => this.handleSelecteLinkerType(e.target.value)}
+                />
+                <label className="custom-control-label" htmlFor="growiType">
+                  Growi original
+                </label>
+              </div>
+              {this.isApplyPukiwikiLikeLinkerPlugin && (
+                <div className="custom-control custom-radio custom-control-inline">
+                  <input
+                    type="radio"
+                    className="custom-control-input"
+                    id="pukiwikiType"
+                    value={Linker.types.pukiwikiLink}
+                    checked={this.state.linkerType === Linker.types.pukiwikiLink}
+                    onChange={e => this.handleSelecteLinkerType(e.target.value)}
+                  />
+                  <label className="custom-control-label" htmlFor="pukiwikiType">
+                    Pukiwiki
+                  </label>
+                </div>
+              )}
             </div>
           </div>
         </form>

+ 30 - 0
src/client/js/components/PageEditor/PreviewWithSuspense.jsx

@@ -0,0 +1,30 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Preview from './Preview';
+
+import { withLoadingSppiner } from '../SuspenseUtils';
+
+function PagePreview(props) {
+  if (props.markdown === '') {
+    if (props.error !== '') {
+      return props.error;
+    }
+    throw (async() => {
+      await props.setMarkdown();
+    })();
+  }
+
+  return (
+    <div className="linkedit-preview">
+      <Preview markdown={props.markdown} />
+    </div>
+  );
+}
+
+PagePreview.propTypes = {
+  setMarkdown: PropTypes.func,
+  markdown: PropTypes.string,
+  error: PropTypes.string,
+};
+
+export default withLoadingSppiner(PagePreview);

+ 32 - 10
src/client/js/components/SearchTypeahead.jsx

@@ -24,12 +24,14 @@ class SearchTypeahead extends React.Component {
     };
 
     this.restoreInitialData = this.restoreInitialData.bind(this);
+    this.clearKeyword = this.clearKeyword.bind(this);
+    this.changeKeyword = this.changeKeyword.bind(this);
     this.search = this.search.bind(this);
     this.onInputChange = this.onInputChange.bind(this);
     this.onKeyDown = this.onKeyDown.bind(this);
     this.dispatchSubmit = this.dispatchSubmit.bind(this);
     this.getEmptyLabel = this.getEmptyLabel.bind(this);
-    this.getRestoreFormButton = this.getRestoreFormButton.bind(this);
+    this.getResetFormButton = this.getResetFormButton.bind(this);
     this.renderMenuItemChildren = this.renderMenuItemChildren.bind(this);
     this.getTypeahead = this.getTypeahead.bind(this);
   }
@@ -51,11 +53,24 @@ class SearchTypeahead extends React.Component {
   }
 
   /**
-   * Initialize keyword
+   * Initialize keywordyword
    */
   restoreInitialData() {
+    this.changeKeyword(this.props.keywordOnInit);
+  }
+
+  /**
+   * clear keyword
+   */
+  clearKeyword(text) {
+    this.changeKeyword('');
+  }
+
+  /**
+   * change keyword
+   */
+  changeKeyword(text) {
     // see https://github.com/ericgio/react-bootstrap-typeahead/issues/266#issuecomment-414987723
-    const text = this.props.keywordOnInit;
     const instance = this.typeahead.getInstance();
     instance.clear();
     instance.setState({ text });
@@ -149,11 +164,16 @@ class SearchTypeahead extends React.Component {
   /**
    * Get restore form button to initialize button
    */
-  getRestoreFormButton() {
-    const isHidden = (this.state.input === this.props.keywordOnInit);
-
-    return isHidden ? <span /> : (
-      <button type="button" className="btn btn-link search-clear" onMouseDown={this.restoreInitialData}>
+  getResetFormButton() {
+    const isClearBtn = this.props.behaviorOfResetBtn === 'clear';
+    const initialKeyword = isClearBtn ? '' : this.props.keywordOnInit;
+    const isHidden = this.state.input === initialKeyword;
+    const resetForm = isClearBtn ? this.clearKeyword : this.restoreInitialData;
+
+    return isHidden ? (
+      <span />
+    ) : (
+      <button type="button" className="btn btn-link search-clear" onMouseDown={resetForm}>
         <i className="icon-close" />
       </button>
     );
@@ -179,7 +199,7 @@ class SearchTypeahead extends React.Component {
       inputProps.name = this.props.inputName;
     }
 
-    const restoreFormButton = this.getRestoreFormButton();
+    const resetFormButton = this.getResetFormButton();
 
     return (
       <div className="search-typeahead">
@@ -203,7 +223,7 @@ class SearchTypeahead extends React.Component {
           caseSensitive={false}
           defaultSelected={defaultSelected}
         />
-        {restoreFormButton}
+        {resetFormButton}
       </div>
     );
   }
@@ -232,6 +252,7 @@ SearchTypeahead.propTypes = {
   placeholder:     PropTypes.string,
   keywordOnInit:   PropTypes.string,
   helpElement:     PropTypes.object,
+  behaviorOfResetBtn: PropTypes.oneOf(['restore', 'clear']),
 };
 
 /**
@@ -243,6 +264,7 @@ SearchTypeahead.defaultProps = {
   onChange:        noop,
   placeholder:     '',
   keywordOnInit:   '',
+  behaviorOfResetBtn: 'restore',
   onInputChange: () => {},
 };
 

+ 1 - 0
src/client/styles/scss/_linkedit-preview.scss

@@ -5,6 +5,7 @@
     margin: 0px -10px 0px -10px;
     .wiki {
       overflow-y: scroll;
+      font-size: 0.5rem;
     }
   }
 }

+ 28 - 22
yarn.lock

@@ -2689,11 +2689,10 @@ atob@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a"
 
-attr-accept@^1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.3.tgz#48230c79f93790ef2775fcec4f0db0f5db41ca52"
-  dependencies:
-    core-js "^2.5.0"
+attr-accept@^2.2.1:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b"
+  integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==
 
 auto-parse@>=1.8.0:
   version "1.8.0"
@@ -4315,10 +4314,6 @@ core-js@^1.0.0:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
 
-core-js@^2.5.0:
-  version "2.5.3"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
-
 core-js@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.2.1.tgz#cd41f38534da6cc59f7db050fe67307de9868b09"
@@ -6166,11 +6161,12 @@ file-loader@^5.0.2:
     loader-utils "^1.2.3"
     schema-utils "^2.5.0"
 
-file-selector@^0.1.11:
-  version "0.1.11"
-  resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.11.tgz#4648d1303fc594afe8638d0f35caed38697d32cf"
+file-selector@^0.2.2:
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.4.tgz#7b98286f9dbb9925f420130ea5ed0a69238d4d80"
+  integrity sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==
   dependencies:
-    tslib "^1.9.0"
+    tslib "^2.0.3"
 
 file-uri-to-path@1.0.0:
   version "1.0.0"
@@ -9056,6 +9052,7 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
 loose-envify@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
   dependencies:
     js-tokens "^3.0.0 || ^4.0.0"
 
@@ -11712,6 +11709,7 @@ prop-types@^15.0.0, prop-types@^15.6.2:
 prop-types@^15.5.0, prop-types@^15.6.0, prop-types@^15.7.2:
   version "15.7.2"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
+  integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
   dependencies:
     loose-envify "^1.4.0"
     object-assign "^4.1.1"
@@ -12019,12 +12017,13 @@ react-dom@^16.8.3:
     prop-types "^15.6.2"
     scheduler "^0.13.3"
 
-react-dropzone@^10.1.3:
-  version "10.1.3"
-  resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-10.1.3.tgz#e45a395b19f440b934484b9b4c416318433e2c90"
+react-dropzone@^11.2.4:
+  version "11.2.4"
+  resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-11.2.4.tgz#391a8d2e41a8a974340f83524d306540192e3313"
+  integrity sha512-EGSvK2CxFTuc28WxwuJCICyuYFX8b+sRumwU6Bs6sTbElV2HtQkT0d6C+HEee6XfbjiLIZ+Th9uji27rvo2wGw==
   dependencies:
-    attr-accept "^1.1.3"
-    file-selector "^0.1.11"
+    attr-accept "^2.2.1"
+    file-selector "^0.2.2"
     prop-types "^15.7.2"
 
 react-fast-compare@^2.0.1:
@@ -12092,8 +12091,9 @@ react-is@^16.7.0, react-is@^16.9.0:
   integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==
 
 react-is@^16.8.1:
-  version "16.8.6"
-  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
 
 react-lifecycles-compat@^3.0.4:
   version "3.0.4"
@@ -14731,14 +14731,20 @@ tslib@^1.8.1:
   integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
 
 tslib@^1.9.0:
-  version "1.9.2"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.2.tgz#8be0cc9a1f6dc7727c38deb16c2ebd1a2892988e"
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 
 tslib@^1.9.3:
   version "1.11.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
   integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
 
+tslib@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
+  integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
+
 tsscmp@1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb"