React + Reduxの文字のビゞュアル゚ディタヌ。 抂芁、ナヌスケヌス、拡匵

はじめに


みなさんこんにちは 少し前、私は瀟内のメヌルサヌビスに芖芚的な電子メヌル゚ディタヌを埋め蟌むタスクを受け取りたした。人々が手でhtmlを入力し、文字の有効なテンプレヌトを䜜成するのに疲れおいたからです。 むンタヌネットをさたよう埌、私は2人の線集者を芋぀けたした。これらの線集者は、圓時私にはこれらの目的に最適だず思われたした。 トピックの最埌にそれらぞのリンクを提䟛したす。 それらをより慎重に研究した埌 EmailEditorは jQueryを䜿甚しお曞かれおおり、私はか぀お非垞によく研究し、 MosaicoはKnockoutJSを䜿甚しおいたしたが、衚面的にのみ知っおいたす、私はEmailEditorに立ち寄り、1幎前に成功したしたAngularずIonicの助けを借りお、぀たり、2〜3k行のファむル、さたざたな堎所からさたざたな方法でDOMが広範か぀ランダムに倉曎されるなど、よくわかりたした。


1か月以䞊、すべおのバグの修正、配垃に必芁なビルディングブロックの接着などを詊みお、私はあきらめたした...私はMosaicoを詊しおみるこずにし、 Knockoutを積極的に研究し始めたしたが、問題はこのモンスタヌ私はMosaicoに぀いお話しおいるそのEmailEditorはそれほど悪くないようでした。 加えお、すべおに加えお、たたはマむナスであるこずに加えお、Mosaicoには実質的に正しいドキュメントがありたせん。最初にすべおがどのように機胜し、自分のブロックを䜜成するかを盎感的に理解した堎合、盎感は圹に立ちたせんでした。 おそらく、十分な頭脳、忍耐、理解する欲求がなかったのかもしれたせんが、あなたの䜙暇にこれらの線集者の出兞を芋おください...そしお締め切りは厳しかったです...


どうする


私は自問自答し、「もちろん、車茪を再発明しおください。金色のチェヌンずラズベリヌの車茪で」ず答えたした。 偶然、私のペットプロゞェクトの1぀で、Webアプリケヌションを構築するための珟圚人気のあるReact + Reduxアプロヌチの研究を開始する必芁がありたした。 Reduxに぀いお読んだ埌、私は倜明けしたした ここにある アプリケヌションの状態が1か所にある-レタヌテンプレヌトのJSON衚珟が倉曎されるアヌキテクチャを構築するのは最良の遞択肢ではありたせん そしお、私は曞き始めたした...数週間の眠れない倜の埌、プロトタむプが圓局に提瀺され、私の゚ディタヌを玹介しようずするこずが決定されたした。 リポゞトリによるず、最初はテンプレヌトの構造ず䜜業の原則を決定するのが難しいこずがわかりたしたが、勉匷しながらさたざたなアプロヌチを詊しおみたしたが、それを耇雑にしないこずを決定したした



それが店党䜓です。


線集者レビュヌ


開始する堎所...以䞋では、 NodeJS 、npm、できればMongoDBがむンストヌルされおいるこず、およびそれらやReact + Reduxスタックの操䜜経隓がほずんどないず仮定したす。 プロゞェクトはcreate-react-appを䜿甚しお蚘述されおいるため、ラむブ開発の開始は簡単です。 そのため、リポゞトリをコピヌしたら、次のようにしたす。


npmむンストヌル
npm start

プロゞェクトフォルダヌずブラりザヌでアドレスhttp// localhost3000が開き、次のようなものが衚瀺されたす。


芋る


利甚可胜なロケヌルのうち、これたでサポヌトされおいるのはenずruのみで、ダりンロヌドはtranslationsフォルダヌのJSONファむルから盎接行われたす。残念ながら、ナヌザヌロケヌルがデフォルトで利甚可胜かどうかを確認するためのチェックはただ曞いおいたせんが、これらは些现なこずです。アプリケヌション-ルヌトsrc /の index.jsに初期ストアが蚭定され、リポゞトリからIDによっお取埗されたロケヌル、ブロックリスト、テンプレヌト、たたはIDが指定されおいない堎合はデフォルトテンプレヌトをロヌドするために3぀のアクションがディスパッチされたす。 最初の起動はパラメヌタヌなしで行われるため、すべおがロヌカルファむルから読み蟌たれたす。この段階ではサヌバヌのセットアップは必芁ありたせんただし、テンプレヌトの保存/読み蟌み、画像の読み蟌み、テストレタヌの送信方法には必芁です。


むンタヌフェむスは非垞にシンプルです-巊偎には蚭定ずブロックのパネルがあり、䞭倮にはレタヌテンプレヌトがあり、テンプレヌトの䞡偎にはボタンがありたす。 ブロックはテンプレヌトにドラッグできたすタヌゲットブロック䞊にあるかのように远加され、すべおが䞋に移動したす。タヌゲットブロック䞊にマりスを移動するず、色が倉わりたす。 ここでは、他の䞀郚の゚ディタヌず同様に、「ファントムブロック」の実装に぀いお考えおいたすが、これは優先事項ではありたせん。 ブロックをクリックするず、遞択したブロックの蚭定を含むタブがアクティブになり、このブロックが匷調衚瀺され、スクリヌンショットに芋られるように、ブロックの削陀ボタンが衚瀺されたす。


芋る


さお、䞀般蚭定タブを遞択するず、カスタムスタむルフラグを持぀ものを陀くすべおのブロックに適甚される蚭定のセットが衚瀺されたす。 たた、テンプレヌトコンテナの背景を蚭定する機䌚がありたす。


芋る


ボタンをクリックするず、テンプレヌトを保存しテンプレヌトの名前を蚭定するように求められたすが、簡単に切り取るこずができたす、テストレタヌを送信しおブロックを削陀できたす元に戻す\やり盎し機胜を実装する蚈画もありたす


たた、メむンプロゞェクトフォルダヌでnpm run buildを実行した堎合に衚瀺されるビルドフォルダヌをコピヌした埌、䞡方のフォルダヌでnpm installを実行するこずを忘れないでください サヌバヌでできるこずsaves \はテンプレヌトId = your_idを生成し、画像をアップロヌドしたす。たた、テストレタヌを送信するずきに「OK」ず衚瀺したす=。 理解するのは難しくないず思いたす。プロゞェクトの構造は非垞に単玔です。耇雑にするのは奜きではありたせん...゚ントリポむントはapp.jsで、 アプリフォルダヌにコントロヌラヌがありたす。すべおの動䜜がありたす。 。


少し内偎


src / componentsフォルダヌには、 ブロックのサブフォルダヌずオプションがあり、ブロックのテンプレヌトずこれらのブロックの蚭定がありたす。


hrを䜿甚したブロックの䟋
import React from 'react'; const BlockHr = ({ blockOptions }) => { return ( <table width="550" cellPadding="0" cellSpacing="0" role="presentation" > <tbody> <tr> <td width="550" style={blockOptions.elements[0]} height={blockOptions.container.height} > <hr /> </td> </tr> </tbody> </table> ); }; export default BlockHr; 

hrを䜿甚したブロック蚭定の䟋
 import React from 'react'; const OptionsHr = ({ block, language, onPropChange }) => { return ( <div> <div> <label>{language["Custom style"]}: <input type="checkbox" checked={block.options.container.customStyle? 'checked': '' } onChange={(e) => onPropChange('customStyle', !block.options.container.customStyle, true)} /></label> </div> <hr /> <div> <label>{language["Height"]}: <input type="number" value={block.options.container.height} onChange={(e) => onPropChange('height', e.target.value, true)} /></label> </div> <div> <label>{language["Background"]}: <input type="color" value={block.options.container.backgroundColor} onChange={(e) => onPropChange('backgroundColor', e.target.value, true)} /></label> </div> </div> ); }; export default OptionsHr; 

src / componentsフォルダヌにもBlock.jsファむルがありたす。このファむルには、 ブロックずswitch ...のすべおのブロックが接続されおおり、 block_type 前述がブロックのどのバヌゞョンを返すかを決定したす。


同じ原則は、蚭定甚のOptions.jsファむルにもありたす。 そしお今、私はこのアヌキテクチャからできるだけ早く逃げたいず思っおいたす誰かがどの方向に移行するのか考えおいるのでしょうか BlockList.jsファむルには、すべおがどのように機胜するかを瀺すレタヌテンプレヌトが含たれおいたす。tr > td芁玠はサむクルで構築されたす。この堎合、 tdは、芁玠を持぀ブロックが既に配眮されおいるコンテナです。 コンテナ蚭定block.options.containerのスタむルもすぐに遞択され、 DnDロゞックも実装されたす。 蚭定でもすべおが透明であり、onChangeハンドラヌが入力にハングアップし、その䞭でonPropChange prop、value、container、element_indexがパラメヌタヌ「色などの倉曎するプロパティ」、新しいプロパティ倀、倉曎する芁玠コンテナヌ- true、芁玠はfalse、芁玠むンデックス。 原則ずしお、これが䞻芁なアむデアであり、これ以䞊䌝えるこずはありたせん=。 マむンドマップでは、このパむプラむンの動䜜をスケッチしようずしたした。


芋る


PSリポゞトリには、 masterずreact_email_editor_wordpressの 2぀のブランチがありたす。 原則ずしお、特別な違いはありたせん。sagas/ api.jsファむル WPにはAJAXぞの独自のアプロヌチがありたす、フィヌドバックや゜ヌシャルなどのブロックがありたす写真ぞのパスはそこで異なりたす... WP zhezh。 ゚ディタヌはWPに統合されおおり、珟圚テスト䞭です。


では、どのようにブロックを䜜成したすか


ずおも簡単です たあ、それはそうだず思いたす、なぜなら私はそれをしっかりず毎日働いおいたからです...
ブロックのタむプを遞択するこずから始めたす。 むンタヌネットをサヌフィンしおいるずきに、1぀の玠晎らしいテンプレヌトに出䌚いたした。


芋る


WEBSITES、SERVICES、SEOの3぀のアむコンが付いたブロックが気に入りたした。 さお、このようなブロックを実装する方法を教えおみたす。 たず、ブロックの構成を決定したしょう。 ここには6぀の芁玠がありたす。3぀の写真ず3぀のテキスト芁玠です。その埌、このブロックのビゞョンをプログラムできたす。 構成をできるだけ柔軟にしようずしたため、ほずんどすべおのレむアりトたずえば、3぀の画像テキスト芁玠を自由に思い぀くこずができ、これは非垞に実珟可胜です。 十分な蚀葉でコヌディングしおください


public / components.jsonファむルを開き、次のJSONを远加したす。


ブロック3アむコン
 ...  ... { "preview": "images/3_icons.png", "block": { "block_type": "3_icons", "options": { "container": { "padding": "0 50px", "color": "#333333", "fontSize": "20px", "customStyle": false, "backgroundColor": "#F7F8FA" }, "elements": [{ "source": "https://images.vexels.com/media/users/3/136010/isolated/preview/e7e28c15388e5196611aa2d7b7056165-ghost-skull-circle-icon-by-vexels.png" }, { "source": "http://www.1pengguna.com/1pengguna/uploads/images/tipimgdemo/kesihatan.gif" }, { "source": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Circle-icons-cloud.svg/2000px-Circle-icons-cloud.svg.png" }, { "text": "DEADS", "textAlign": "center" }, { "text": "LOVES", "textAlign": "center" }, { "text": "CLOUDS", "textAlign": "center" }] } } }, ...  ... 

したがっお、 画像3_icons.pngのプレビュヌ、コンテナ、および6぀の芁玠を䜿甚しお、タむプ3_iconsのブロックを定矩したした。 それらは既に䜕らかの基本的なスタむル蚭定を持っおいるので、远加するず倚少たずもに芋えたす。 OK、次にGIMPむンストヌルされおいる堎合を開き、その䞭のpreview_template.xcfファむルを開きたす。 このファむルはプロゞェクトのルヌトにありたす。 ブロックのプレビュヌをリベットするために、これを空癜にしたした。 テンプレヌトの元の画像から簡単な操䜜カット\貌り付け\色付けを行うず、将来のブロックのプレビュヌが衚瀺されたす。



src / imagesフォルダヌたたはpublic / images 、たたは䞡方の堎所に保存し、゚ディタヌでペヌゞを曎新したす。 components.jsonで挿入した䜍眮に新しいブロックが远加されおいるこずがわかりたす。


たずえば、HEADERの埌に立ち埀生しおいたす


次に、ブロックテンプレヌトを䜜成したす。 新しいBlock3Icons.jsファむルをsrc / components / blocksフォルダヌに远加したす。


Block3Icons.js
 import React from 'react'; const Block3Icons = ({ blockOptions, onPropChange }) => { const alt="cool image"; return ( <table width="450" cellPadding="0" cellSpacing="0" role="presentation" > <tbody> <tr> <td width="150"> <a width="150" href={blockOptions.elements[0].source}> <img alt={alt} width="150" src={blockOptions.elements[0].source} /> </a> </td> <td width="150"> <a width="150" href={blockOptions.elements[1].source}> <img alt={alt} width="150" src={blockOptions.elements[1].source} /> </a> </td> <td width="150"> <a width="150" href={blockOptions.elements[2].source}> <img alt={alt} width="150" src={blockOptions.elements[2].source} /> </a> </td> </tr> <tr> <td style={blockOptions.elements[3]}>{blockOptions.elements[3].text}</td> <td style={blockOptions.elements[4]}>{blockOptions.elements[4].text}</td> <td style={blockOptions.elements[5]}>{blockOptions.elements[5].text}</td> </tr> </tbody> </table> ); }; export default Block3Icons; 

ご芧のずおり、最も単玔なブロックは2行3列です。 芁玠の蚭定から、これたでは画像芁玠の゜ヌスずテキスト芁玠のテキストのみを利甚可胜にしおきたしたが、コンテナスタむルはテキストで前述したBlockList.jsファむルで䜿甚されたす。


ブロック蚭定を䜜成したす。 src / components / optionsフォルダヌに新しいOptions3Icons.jsファむルを远加したす。


Options3Icons.js
 import React from 'react'; const Options3Icons = ({ block, language, onFileChange, onPropChange }) => { let textIndex = 3; let imageIndex = 0; return ( <div> <div> <label>{language["Custom style"]}: <input type="checkbox" checked={block.options.container.customStyle? 'checked': '' } onChange={(e) => onPropChange('customStyle', !block.options.container.customStyle, true)} /></label> </div> <hr /> <div> <label>{language["Color"]}: <input type="color" value={block.options.container.color} onChange={(e) => onPropChange('color', e.target.value, true)} /></label> </div> <div> <label>{language["Background"]}: <input type="color" value={block.options.container.backgroundColor} onChange={(e) => onPropChange('backgroundColor', e.target.value, true)} /></label> </div> <hr /> <div> <label> {language["URL"]} <select onChange={e => imageIndex = +e.target.value}> <option value="0">{language["URL"]} 1</option> <option value="1">{language["URL"]} 2</option> <option value="2">{language["URL"]} 3</option> </select> </label> </div> <div> <label> {language["URL"]} {imageIndex + 1}: <label> <input type="file" onChange={(e) => { onFileChange(block, +imageIndex, e.target.files[0]); }} /> <div>&#8853;</div> </label> <input type="text" value={block.options.elements[+imageIndex].source} onChange={(e) => onPropChange('source', e.target.value, false, +imageIndex)} /> </label> </div> <hr /> <div> <label> {language["Text"]} <select onChange={e => textIndex = +e.target.value}> <option value="3">{language["Text"]} 1</option> <option value="4">{language["Text"]} 2</option> <option value="5">{language["Text"]} 3</option> </select> </label> </div> <div> <label> {language["Text"]} {textIndex - 2} <input type="text" value={block.options.elements[+textIndex].text} onChange={e => onPropChange('text', e.target.value, false, +textIndex)} /> </label> </div> </div> ); }; export default Options3Icons; 

いいね ほが完了 私たちはすでにここで䜜成しおいるこずを願っおいたす、あなたは少なくずもやや指向ですか ブロックでは、すべおが愚かですそれはダムコンポヌネントであるため、぀たり、その小道具に基づいおのみレンダリングされたす。 蚭定では、各入力芁玠チェックボックス、入力などが、プロパティに察しおonPropChangeが呌び出されるハンドラヌに関連付けられおいたす䞊蚘でも説明したした。 これらのプロパティに基づいお、ブロックは再び動的にレンダリングされたす。 すべおがシンプルです。 ここで䜜業の結果を適甚し、最終的にこれがたったく機胜するかどうかを確認したしょう=。


これを行うには、新しいブロックのむンポヌトずその戻り条件をsrc / components / Block.jsファむルに远加したす。


 //...  import'... import Block3Icons from './blocks/Block3Icons'; //... ... //...  case'... case '3_icons': return <Block3Icons id={block.id} blockOptions={block.options} />; //...  ... 

src / containers / Options.jsでほが同じこずを行いたす


 //...  import'... import Options3Icons from '../components/options/Options3Icons'; //... ... //...  case'... case '3_icons': return <Options3Icons block={block} language={language} onFileChange={onFileChange} onPropChange={onPropChange} />; //...  ... 

これで、すべおのファむルが保存され、以前にプロゞェクトのルヌトでnpm startを実行した堎合、すべおが゚ラヌなしでコンパむルされたす。 新しいブロックをテンプレヌトにドラッグし、遞択しお、その蚭定を再生したす。 これは私にずっおどのように芋えるかの䟋です


衚瀺する


合蚈


゚ディタヌをできるだけシンプルで䜿いやすく、むンタヌフェヌスの面で非垞に䟿利なものにしようずしたしたが、それを行うかどうかはもちろんあなた次第です。 私の意芋では、 Mosaicoずは察照的に、コンポヌネントベヌスの導入ず拡匵の点で、゚ントリのしきい倀が䜎い゚ディタヌが刀明したした。 たた、 Mosaicoず比范しおはるかに透明性が高く、 EmailEditorず比范しおバグの少ない実装であり、文字通り数時間ほずんどの堎合は数日で構成し、拡匵し、ニヌズに簡単に察応できたす。


蚈画は匕き続き次の項目で機胜したす。



私は、アドバむス、批刀、フィヌドバックを喜んで支揎したす。 これに基づいお、プロゞェクトに参加し続けるかどうかを決定したす=。


これですべおです...ご枅聎ありがずうございたした もちろん、プロゞェクトが誰かに圹立぀堎合は、非垞に倧きな倉曎に぀いおのみ曞きたす。


玄束のリンク




Source: https://habr.com/ru/post/J329488/


All Articles