瀏覽代碼

完善wizard表4

herj 2 年之前
父節點
當前提交
5e719da0f9

+ 1 - 0
packages/jldbq-extenison/package.json

@@ -44,6 +44,7 @@
     "react": "^17.0.1",
     "react-beautiful-dnd": "^13.1.0",
     "react-select": "^5.4.0",
+    "react-syntax-highlighter": "^15.5.0",
     "swr": "^1.3.0"
   },
   "devDependencies": {

+ 2 - 0
packages/jldbq-extenison/src/index.tsx

@@ -53,6 +53,8 @@ const plugin: JupyterFrontEndPlugin<void> = {
         content: ReactWidget.create(
           <SyncWizard
             onFinish={data => {
+              // TODO:
+              alert(JSON.stringify(data, null, 2));
               console.log(data);
             }}
           />

+ 92 - 28
packages/jldbq-extenison/src/wizard/StepFourForm.tsx

@@ -1,18 +1,79 @@
-import React, { forwardRef, useImperativeHandle, useState } from 'react';
+import React, {
+  forwardRef,
+  useImperativeHandle,
+  useRef,
+  useState
+} from 'react';
 import { JsonSchemaDrawer } from './drawer';
 
+const FormNumberInput: React.FunctionComponent<{
+  id?: string;
+  value?: number;
+  onChange: (value: number) => void;
+}> = ({ id, value, onChange }) => {
+  const valStr = value !== undefined ? value.toString() : '';
+  const [draft, setDraft] = useState(valStr);
+  const [isEditing, setIsEditing] = useState(false);
+  const inputRef = useRef<HTMLInputElement>(null);
+
+  // getDerivedProps
+  if (!isEditing && valStr !== draft) {
+    setDraft(valStr);
+  }
+
+  return (
+    <div className="form-input-number">
+      <button type="button" onClick={() => onChange((value || 0) - 1)} />
+      <input
+        className="form-input"
+        type="text"
+        autoComplete="off"
+        ref={inputRef}
+        id={id}
+        value={draft}
+        onFocus={() => {
+          inputRef.current?.select();
+          setIsEditing(true);
+        }}
+        onBlur={() => {
+          const newVal = parseInt(draft, 10);
+          if (!isNaN(newVal)) {
+            onChange(newVal);
+          }
+          setIsEditing(false);
+        }}
+        onChange={evt => setDraft(evt.target.value)}
+      />
+      <button type="button" onClick={() => onChange((value || 0) + 1)} />
+    </div>
+  );
+};
+
+interface IForm {
+  route: string;
+  block: string;
+  timeout: number;
+  retry: number;
+  startTime: string;
+  timeField: string;
+  shardField: string;
+  timeRule: string;
+  taskName: string;
+}
+
+// TODO:
 const checkForm = (formData: any): any => {
   return null;
 };
 
 const StepFourForm: React.ForwardRefRenderFunction<
   {
-    getData: () => any;
+    getData: () => Partial<IForm> | null;
   },
   { wizardData: any[] }
 > = ({ wizardData }, ref) => {
   const [showSideBar, setShowSideBar] = useState(false);
-  const [formData, setFormData] = useState<any>({});
+  const [formData, setFormData] = useState<Partial<IForm>>({});
   const [, setFormError] = useState<any>({});
 
   useImperativeHandle(ref, () => {
@@ -31,8 +92,10 @@ const StepFourForm: React.ForwardRefRenderFunction<
   return (
     <form>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-2-datasource">
-          <span className="field-required" />
+        <label
+          className="form-label field-required"
+          htmlFor="step-2-datasource"
+        >
           路由策略
         </label>
         <select
@@ -48,8 +111,7 @@ const StepFourForm: React.ForwardRefRenderFunction<
         </select>
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-4-block">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-4-block">
           阻塞处理方式
         </label>
         <select
@@ -65,44 +127,39 @@ const StepFourForm: React.ForwardRefRenderFunction<
         </select>
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-4-timeout">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-4-timeout">
           超时时间
         </label>
-        <input
-          className="form-input"
-          type="number"
+        <FormNumberInput
           id="step-4-timeout"
           value={formData.timeout}
-          onChange={evt =>
-            setFormData({ ...formData, timeout: evt.target.value })
+          onChange={value =>
+            setFormData({ ...formData, timeout: Math.max(value, 0) })
           }
         />
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-4-retry">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-4-retry">
           失败重试次数
         </label>
-        <input
-          className="form-input"
-          type="number"
+        <FormNumberInput
           id="step-4-retry"
           value={formData.retry}
-          onChange={evt =>
-            setFormData({ ...formData, retry: evt.target.value })
+          onChange={value =>
+            setFormData({ ...formData, retry: Math.max(value, 0) })
           }
         />
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-4-starttime">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-4-starttime">
           增量开始时间
         </label>
         <input
           className="form-input"
           type="text"
           id="step-4-starttime"
+          autoComplete="off"
+          placeholder="2022.8.6 17:22:14"
           value={formData.startTime}
           onChange={evt =>
             setFormData({ ...formData, startTime: evt.target.value })
@@ -117,6 +174,8 @@ const StepFourForm: React.ForwardRefRenderFunction<
           className="form-input"
           type="text"
           id="step-4-timefield"
+          autoComplete="off"
+          placeholder="-DlastTime='%s' -DcurrentTime='%s'"
           value={formData.timeField}
           onChange={evt =>
             setFormData({ ...formData, timeField: evt.target.value })
@@ -131,6 +190,8 @@ const StepFourForm: React.ForwardRefRenderFunction<
           className="form-input"
           type="text"
           id="step-4-shardfield"
+          autoComplete="off"
+          placeholder="手动输入分区字段"
           value={formData.shardField}
           onChange={evt =>
             setFormData({ ...formData, shardField: evt.target.value })
@@ -138,14 +199,15 @@ const StepFourForm: React.ForwardRefRenderFunction<
         />
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-4-timerule">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-4-timerule">
           定时规则
         </label>
         <input
           className="form-input"
           type="text"
           id="step-4-timerule"
+          autoComplete="off"
+          placeholder="0 0/2***?"
           value={formData.timeRule}
           onChange={evt =>
             setFormData({ ...formData, timeRule: evt.target.value })
@@ -153,14 +215,15 @@ const StepFourForm: React.ForwardRefRenderFunction<
         />
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-4-taskname">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-4-taskname">
           同步任务名称
         </label>
         <input
           className="form-input"
           type="text"
           id="step-4-taskname"
+          autoComplete="off"
+          placeholder="同步任务名称"
           value={formData.taskName}
           onChange={evt =>
             setFormData({ ...formData, taskName: evt.target.value })
@@ -169,10 +232,11 @@ const StepFourForm: React.ForwardRefRenderFunction<
       </div>
       <button
         type="button"
+        tabIndex={-1}
         className="drawer-button"
         onClick={() => setShowSideBar(true)}
       >
-        构建
+        构建预览
       </button>
       <JsonSchemaDrawer
         jsonSchema={wizardData.slice(0, 3).concat(formData)}

+ 13 - 10
packages/jldbq-extenison/src/wizard/StepOneForm.tsx

@@ -28,6 +28,7 @@ const MysqlDetail: React.FunctionComponent<{
           className="form-input"
           type="text"
           id="step-1-pkey"
+          autoComplete="off"
           value={value.pkey}
           onChange={evt => onChange({ pkey: evt.target.value })}
         />
@@ -55,8 +56,7 @@ const HiveDetail: React.FunctionComponent<{
   return (
     <>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-1-path">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-1-path">
           选择路径
         </label>
         <textarea
@@ -68,21 +68,20 @@ const HiveDetail: React.FunctionComponent<{
         />
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-1-hdfs">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-1-hdfs">
           HDFS
         </label>
         <input
           className="form-input"
           type="text"
           id="step-1-hdfs"
+          autoComplete="off"
           value={value.hdfs}
           onChange={evt => onChange({ hdfs: evt.target.value })}
         />
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-1-filetype">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-1-filetype">
           文件类型
         </label>
         <select
@@ -104,6 +103,7 @@ const HiveDetail: React.FunctionComponent<{
           className="form-input"
           type="text"
           id="step-1-delim"
+          autoComplete="off"
           value={value.delim}
           onChange={evt => onChange({ delim: evt.target.value })}
         />
@@ -112,6 +112,7 @@ const HiveDetail: React.FunctionComponent<{
   );
 };
 
+// TODO:
 const checkForm = (formData: any): any => {
   return null;
 };
@@ -162,8 +163,10 @@ const StepOneForm: React.ForwardRefRenderFunction<
   return (
     <form>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-1-datasource">
-          <span className="field-required" />
+        <label
+          className="form-label field-required"
+          htmlFor="step-1-datasource"
+        >
           选择数据源
         </label>
         <select
@@ -180,8 +183,7 @@ const StepOneForm: React.ForwardRefRenderFunction<
         </select>
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-1-datatable">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-1-datatable">
           选择表
         </label>
         <select
@@ -204,6 +206,7 @@ const StepOneForm: React.ForwardRefRenderFunction<
       {detail}
       <button
         type="button"
+        tabIndex={-1}
         className="drawer-button"
         onClick={() => setShowSideBar(true)}
       >

+ 3 - 4
packages/jldbq-extenison/src/wizard/StepThreeForm.tsx

@@ -22,7 +22,7 @@ interface IForm {
   mode: string;
 }
 
-// TODO
+// TODO:
 const checkForm = (formData: any): any => {
   return null;
 };
@@ -74,7 +74,7 @@ const useSchemaList = (
 
 const StepThreeForm: React.ForwardRefRenderFunction<
   {
-    getData: () => any;
+    getData: () => Partial<IForm> | null;
   },
   { wizardData: any[] }
 > = ({ wizardData }, ref) => {
@@ -421,8 +421,7 @@ const StepThreeForm: React.ForwardRefRenderFunction<
         </div>
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-3-mode">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-3-mode">
           写入模式
         </label>
         <select

+ 12 - 10
packages/jldbq-extenison/src/wizard/StepTwoForm.tsx

@@ -9,8 +9,7 @@ const HiveDetail: React.FunctionComponent<{
   return (
     <>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-1-path">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-1-path">
           选择路径
         </label>
         <textarea
@@ -22,21 +21,20 @@ const HiveDetail: React.FunctionComponent<{
         />
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-1-hdfs">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-1-hdfs">
           HDFS
         </label>
         <input
           className="form-input"
           type="text"
           id="step-1-hdfs"
+          autoComplete="off"
           value={value.hdfs}
           onChange={evt => onChange({ hdfs: evt.target.value })}
         />
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-1-filetype">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-1-filetype">
           文件类型
         </label>
         <select
@@ -58,6 +56,7 @@ const HiveDetail: React.FunctionComponent<{
           className="form-input"
           type="text"
           id="step-1-delim"
+          autoComplete="off"
           value={value.delim}
           onChange={evt => onChange({ delim: evt.target.value })}
         />
@@ -66,6 +65,7 @@ const HiveDetail: React.FunctionComponent<{
   );
 };
 
+// TODO:
 const checkForm = (formData: any): any => {
   return null;
 };
@@ -125,8 +125,10 @@ const StepTwoForm: React.ForwardRefRenderFunction<
   return (
     <form>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-2-datasource">
-          <span className="field-required" />
+        <label
+          className="form-label field-required"
+          htmlFor="step-2-datasource"
+        >
           选择数据源
         </label>
         <select
@@ -143,8 +145,7 @@ const StepTwoForm: React.ForwardRefRenderFunction<
         </select>
       </div>
       <div className="form-group">
-        <label className="form-label" htmlFor="step-2-datatable">
-          <span className="field-required" />
+        <label className="form-label field-required" htmlFor="step-2-datatable">
           选择表
         </label>
         <select
@@ -167,6 +168,7 @@ const StepTwoForm: React.ForwardRefRenderFunction<
       {detail}
       <button
         type="button"
+        tabIndex={-1}
         className="drawer-button"
         onClick={() => setShowSideBar(true)}
       >

+ 1 - 1
packages/jldbq-extenison/src/wizard/SyncWizard.tsx

@@ -22,7 +22,7 @@ const StepController: React.FunctionComponent<{
         上一步
       </button>
       <button className="step-control" onClick={onNext}>
-        {currentStep === 3 ? '完成' : '下一步'}
+        {currentStep === 3 ? '构建' : '下一步'}
       </button>
     </div>
   );

+ 22 - 4
packages/jldbq-extenison/src/wizard/drawer.tsx

@@ -1,4 +1,6 @@
 import React from 'react';
+import SyntaxHighlighter from 'react-syntax-highlighter';
+import { solarizedDark } from 'react-syntax-highlighter/dist/esm/styles/hljs';
 import { useTableSchema } from './service';
 
 export const TableShemaDrawer: React.FunctionComponent<{
@@ -12,7 +14,12 @@ export const TableShemaDrawer: React.FunctionComponent<{
     <div className={'drawer-container ' + (show ? '' : 'drawer-hidden')}>
       <div className="drawer-titlebar">
         <div className="drawer-title">表结构预览</div>
-        <button type="button" className="drawer-close-btn" onClick={onClose} />
+        <button
+          type="button"
+          tabIndex={-1}
+          className="drawer-close-btn"
+          onClick={onClose}
+        />
       </div>
       <div className="drawer-table-header">
         <span className="drawer-table-cell" style={{ width: '20%' }}>
@@ -57,12 +64,23 @@ export const JsonSchemaDrawer: React.FunctionComponent<{
   onClose: () => void;
 }> = ({ jsonSchema, show, onClose }) => {
   return (
-    <div className={'drawer-container ' + (show ? '' : 'drawer-hidden')}>
+    <div
+      className={
+        'drawer-container drawer-dark ' + (show ? '' : 'drawer-hidden')
+      }
+    >
       <div className="drawer-titlebar">
         <div className="drawer-title" />
-        <button type="button" className="drawer-close-btn" onClick={onClose} />
+        <button
+          type="button"
+          tabIndex={-1}
+          className="drawer-close-btn"
+          onClick={onClose}
+        />
       </div>
-      <pre className="json-schema">{JSON.stringify(jsonSchema, null, 2)}</pre>
+      <SyntaxHighlighter showLineNumbers language="json" style={solarizedDark}>
+        {JSON.stringify(jsonSchema, null, 2)}
+      </SyntaxHighlighter>
     </div>
   );
 };

二進制
packages/jldbq-extenison/style/img/collapse-white.png


+ 68 - 12
packages/jldbq-extenison/style/wizard.css

@@ -4,7 +4,7 @@
   overflow-x: hidden;
   display: flex;
   flex-direction: column;
-  padding: 30px;
+  padding: 15px 60px;
   position: relative;
   font-size: 14px;
 }
@@ -20,9 +20,9 @@
   display: flex;
   margin: 0;
   padding: 0;
-  width: 800px;
+  min-width: 800px;
   color: #7d7d7d;
-  margin-bottom: 30px;
+  margin-bottom: 60px;
 }
 
 .sync-wizard .step-index-list li {
@@ -142,15 +142,69 @@
   padding: 10px;
 }
 
+.sync-wizard .form-input-number {
+  position: relative;
+}
+
+.sync-wizard .form-input-number button {
+  position: absolute;
+  top: 1px;
+  width: 32px;
+  height: 30px;
+  border: none;
+  border-radius: 0;
+  background-color: #efeff1;
+  padding: 0;
+  margin: 0;
+  cursor: pointer;
+}
+
+.sync-wizard .form-input-number button:first-child {
+  left: 1px;
+  border-right: 1px solid #c8d3e9;
+  border-top-left-radius: 2px;
+  border-bottom-left-radius: 2px;
+  background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iIzdmN2M3ZiIgdmlld0JveD0iMCAwIDE2IDE2Ij48cGF0aCBkPSJNNCA4YS41LjUgMCAwIDEgLjUtLjVoN2EuNS41IDAgMCAxIDAgMWgtN0EuNS41IDAgMCAxIDQgOHoiLz48L3N2Zz4');
+  background-repeat: no-repeat;
+  background-position: left 50% top 50%;
+  background-size: 20px auto;
+}
+
+.sync-wizard .form-input-number button:last-child {
+  right: 1px;
+  border-left: 1px solid #c8d3e9;
+  border-top-right-radius: 2px;
+  border-bottom-right-radius: 2px;
+  background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iIzdmN2M3ZiIgdmlld0JveD0iMCAwIDE2IDE2Ij48cGF0aCBkPSJNOCA0YS41LjUgMCAwIDEgLjUuNXYzaDNhLjUuNSAwIDAgMSAwIDFoLTN2M2EuNS41IDAgMCAxLTEgMHYtM2gtM2EuNS41IDAgMCAxIDAtMWgzdi0zQS41LjUgMCAwIDEgOCA0eiIvPjwvc3ZnPg==');
+  background-repeat: no-repeat;
+  background-position: left 50% top 50%;
+  background-size: 20px auto;
+}
+
+.sync-wizard .form-input-number .form-input {
+  text-align: center;
+}
+
 .sync-wizard .form-label {
   color: #4a4a4a;
   height: 32px;
-  width: 100px;
+  width: 104px;
+  padding-left: 12px;
+  position: relative;
   display: flex;
   align-items: center;
   margin-right: 15px;
 }
 
+.sync-wizard .form-label.field-required::before {
+  content: '*';
+  color: #ed5555;
+  position: absolute;
+  left: 0;
+  top: 0;
+  line-height: 32px;
+}
+
 .sync-wizard .form-group {
   display: flex;
   margin-bottom: 25px;
@@ -160,12 +214,6 @@
   margin-bottom: 0;
 }
 
-.sync-wizard .field-required::after {
-  content: '*';
-  color: #ed5555;
-  margin-right: 6px;
-}
-
 .sync-wizard p.form-hint {
   color: #bbbbbb;
   margin: 0;
@@ -428,7 +476,7 @@
 .sync-wizard .step-control:disabled {
   border-color: #d6d6d6;
   color: #d6d6d6;
-  cursor: not-allowed;
+  cursor: unset;
 }
 
 /* drawer */
@@ -464,7 +512,7 @@
 
 .sync-wizard .drawer-close-btn {
   border: none;
-  background-color: white;
+  background-color: transparent;
   padding: 0;
   width: 24px;
   height: 24px;
@@ -504,3 +552,11 @@
   text-overflow: ellipsis;
   padding: 0 5px;
 }
+
+.sync-wizard .drawer-container.drawer-dark {
+  background-color: #002b36;
+}
+
+.sync-wizard .drawer-dark .drawer-close-btn {
+  background-image: url('./img/collapse-white.png');
+}

+ 79 - 1
yarn.lock

@@ -3747,6 +3747,13 @@
   dependencies:
     "@types/node" "*"
 
+"@types/hast@^2.0.0":
+  version "2.3.4"
+  resolved "https://registry.npmmirror.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc"
+  integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==
+  dependencies:
+    "@types/unist" "*"
+
 "@types/hoist-non-react-statics@^3.3.0":
   version "3.3.1"
   resolved "https://registry.npmmirror.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
@@ -4076,6 +4083,11 @@
   dependencies:
     source-map "^0.6.1"
 
+"@types/unist@*":
+  version "2.0.6"
+  resolved "https://registry.npmmirror.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
+  integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
+
 "@types/url-parse@^1.4.3":
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/@types/url-parse/-/url-parse-1.4.3.tgz#fba49d90f834951cb000a674efee3d6f20968329"
@@ -8625,7 +8637,7 @@ fastq@^1.6.0:
   dependencies:
     reusify "^1.0.4"
 
-fault@^1.0.2:
+fault@^1.0.0, fault@^1.0.2:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13"
   integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==
@@ -9506,11 +9518,27 @@ hastscript@^5.0.0:
     property-information "^5.0.0"
     space-separated-tokens "^1.0.0"
 
+hastscript@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.npmmirror.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640"
+  integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==
+  dependencies:
+    "@types/hast" "^2.0.0"
+    comma-separated-tokens "^1.0.0"
+    hast-util-parse-selector "^2.0.0"
+    property-information "^5.0.0"
+    space-separated-tokens "^1.0.0"
+
 he@1.2.x, he@^1.1.0, he@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
   integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
 
+highlight.js@^10.4.1, highlight.js@~10.7.0:
+  version "10.7.3"
+  resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
+  integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
+
 highlight.js@~9.15.0, highlight.js@~9.15.1:
   version "9.15.10"
   resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2"
@@ -11878,6 +11906,14 @@ lowlight@1.12.1:
     fault "^1.0.2"
     highlight.js "~9.15.0"
 
+lowlight@^1.17.0:
+  version "1.20.0"
+  resolved "https://registry.npmmirror.com/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888"
+  integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==
+  dependencies:
+    fault "^1.0.0"
+    highlight.js "~10.7.0"
+
 lru-cache@6.0.0, lru-cache@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@@ -13387,6 +13423,18 @@ parse-entities@^1.1.2:
     is-decimal "^1.0.0"
     is-hexadecimal "^1.0.0"
 
+parse-entities@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8"
+  integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==
+  dependencies:
+    character-entities "^1.0.0"
+    character-entities-legacy "^1.0.0"
+    character-reference-invalid "^1.0.0"
+    is-alphanumerical "^1.0.0"
+    is-decimal "^1.0.0"
+    is-hexadecimal "^1.0.0"
+
 parse-json@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@@ -13859,6 +13907,11 @@ pretty-ms@^5.0.0:
   dependencies:
     parse-ms "^2.1.0"
 
+prismjs@^1.27.0:
+  version "1.28.0"
+  resolved "https://registry.npmmirror.com/prismjs/-/prismjs-1.28.0.tgz#0d8f561fa0f7cf6ebca901747828b149147044b6"
+  integrity sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==
+
 prismjs@^1.8.4, prismjs@~1.17.0:
   version "1.17.1"
   resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.17.1.tgz#e669fcbd4cdd873c35102881c33b14d0d68519be"
@@ -13866,6 +13919,11 @@ prismjs@^1.8.4, prismjs@~1.17.0:
   optionalDependencies:
     clipboard "^2.0.0"
 
+prismjs@~1.27.0:
+  version "1.27.0"
+  resolved "https://registry.npmmirror.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057"
+  integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==
+
 private@~0.1.5:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@@ -14841,6 +14899,17 @@ react-syntax-highlighter@^12.2.1:
     prismjs "^1.8.4"
     refractor "^2.4.1"
 
+react-syntax-highlighter@^15.5.0:
+  version "15.5.0"
+  resolved "https://registry.npmmirror.com/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz#4b3eccc2325fa2ec8eff1e2d6c18fa4a9e07ab20"
+  integrity sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==
+  dependencies:
+    "@babel/runtime" "^7.3.1"
+    highlight.js "^10.4.1"
+    lowlight "^1.17.0"
+    prismjs "^1.27.0"
+    refractor "^3.6.0"
+
 react-textarea-autosize@^8.1.1:
   version "8.3.3"
   resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz#f70913945369da453fd554c168f6baacd1fa04d8"
@@ -15092,6 +15161,15 @@ refractor@^2.4.1:
     parse-entities "^1.1.2"
     prismjs "~1.17.0"
 
+refractor@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.npmmirror.com/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a"
+  integrity sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==
+  dependencies:
+    hastscript "^6.0.0"
+    parse-entities "^2.0.0"
+    prismjs "~1.27.0"
+
 regenerate-unicode-properties@^8.2.0:
   version "8.2.0"
   resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"