From bff38c409c48eefecb597dc415fa105045337786 Mon Sep 17 00:00:00 2001 From: Gabriel Aszalos Date: Thu, 20 Apr 2017 12:41:42 +0200 Subject: [PATCH] fix(editor3): enable multiple editors (#1522) * fix(editor3): documented attributes and added find-replace-target When the Editor3 directive is being used, it registers itself with a service that allows external components to run find & replace operations on it. Only one editor can be registered at a time, and this flag enables that operation. The `data-find-replace-target` attribute registers the editor with the find & replace service and de-registers it when its scope is destroyed. Only one editor can be registered at a time. * feat(editor3): allow editor3 services to override editor2 - Added missing interface methods to the editor3 service, so that it can replace editor2 when needed. Assures SendItem & Macros work correctly. - Added config `features.onlyEditor3` which completely disables editor2. - Added single-line option to the editor, enabled by adding `data-single-line` as an attribute (no value needed). - Added editor3 version for all fields on items. - Fixed problem with hovering toolbar, so that when an editor goes out of view, the toolbar goes away (instead of staying sticky when no editor is in view anymore). - Added several CSS fixes to acommodate editor3. - Made formatting options for special content work. Editor formats supported: picture, anchor, embed, table * feat(editor3): new set of icons Updates the new set of icons provided by the design team. * feat(editor3): added editorResolver service --- fonts/sd_icons.eot | Bin 14208 -> 16216 bytes fonts/sd_icons.svg | 61 +++++++++----- fonts/sd_icons.ttf | Bin 14040 -> 16048 bytes fonts/sd_icons.woff | Bin 14116 -> 16124 bytes .../directives/ArticleEditDirective.js | 1 + .../authoring/directives/SendItem.js | 6 +- scripts/apps/authoring/editor/find-replace.js | 8 +- scripts/apps/authoring/macros/macros.js | 6 +- .../apps/authoring/tests/authoring.spec.js | 8 +- .../apps/authoring/views/article-edit.html | 22 ++--- .../controllers/ContentProfilesController.js | 6 +- .../directives/ContentProfileSchemaEditor.js | 6 +- .../content/views/profile-settings.html | 6 ++ .../content/views/schema-editor.html | 4 - scripts/core/editor3/components/Editor3.jsx | 15 +++- .../editor3/components/embeds/EmbedButton.jsx | 2 +- .../editor3/components/images/ImageButton.jsx | 2 +- .../editor3/components/links/LinkButton.jsx | 4 +- .../editor3/components/links/LinkPopover.jsx | 4 +- .../editor3/components/tables/TableButton.jsx | 2 +- .../editor3/components/tests/editor3.spec.jsx | 9 +- .../editor3/components/tests/links.spec.jsx | 2 - .../editor3/components/tests/styles.spec.jsx | 4 +- .../components/toolbar/StyleButton.jsx | 30 +++++-- .../core/editor3/components/toolbar/index.js | 25 ++++-- scripts/core/editor3/directive.js | 79 ++++++++++++++++-- scripts/core/editor3/index.js | 11 +++ .../core/editor3/reducers/find-replace.jsx | 4 +- scripts/core/editor3/service.js | 77 +++++++++++++++-- scripts/core/editor3/styles.scss | 71 +++++++++++++--- .../core/editor3/tests/editor3core.spec.jsx | 4 +- styles/sass/sd-icon-font.scss | 51 ++++++++++- webpack.config.js | 5 +- 33 files changed, 416 insertions(+), 119 deletions(-) diff --git a/fonts/sd_icons.eot b/fonts/sd_icons.eot index e0fa57380902e51f7bd61b1841646b4c5b7e8598..4eb6f19db2ba591cdb0dbe086a729d726f494974 100755 GIT binary patch delta 4460 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 delta 2460 zcmaJ@duUtN8UMa>^sr>>aeXaIxpp02OR{7)Z4~QeY(+gbZ7EskLJ3|erIpjPP1D%% zwvZZ{MTaz$F|v_VEQB()5JG8SG|EEhtKW;*m8yLRrb(E@RF}a>s4B3_ADG&{s`};^~5moe7bM{ zQ}b{BSbYvq>Y?)+3-ilM@G>+3%05~TFU~JK{qw&)xR_$T@keS!4I zXt`8DyHIDviT>h*9)?(A5ZQ9jjEpRdKmUDabh2I-eAc-*kj)Mdu>nVn*Je|N;X-Pv zHkC?E7KWL$(*Ec@Y24`AANHqydCZYaO7A+cxU%+8P3&mw@Ys&G?(w}m83h9%g!V`v zVf6JE?TJd+2-*ghZE%}bHfyn2D=XT-Btq6&b-k?_fh+&?Ek>GQkN{kANoWM28)9HW z5(Z!+q#*}jl7rkfY`>kbbHzkDd52MQkS;KA6+#=N5Txu|M=n=MnFCCC52tVIAH7d* z8bF(?>X;+Os?{;3#;VRMht6Mj3a6R3n9Q2)3EXbbBRCf&)w#-znnzKSa?Lb zhpkM;nq^9@&6v;1v_WxZWn{)$U9~do@@18=oSMGJW`$A!twQpvCL=HefkB866kw9$ zShf@Cgk7dh2T-|+CQOj4Q!yi&T1aRw9A5nA}&HjQWOO$C4?(b zNRLl>x`YQhU^Q*LvEg5=c{h%XZpBh%8Y!>*eS!bGMlqQe`AP3{$NA~?3%9Oz&d3z< z;is^ZH^~S8N@MUdMBe)DP;%tYI-*i1pe8QHl8#WL7OLc!tGyRu<=D`NjQC`SD;uar z2(YwKBp8YYQpT($r)`u*Ik-SrkW_`zl}LFxkVTngLQ0p25SEftp1N(M6K$H7-Q>cn zK*;7WjX^tCEhd9D4q%!$Yfbmx_qkUI)>GSVi04tb!2^%IC+=q#93f^dU7DeSw%osK zKRE&k+epGvV8b{WTBwvu_p-D$l%h;ktCzf3&?UVkZITn-lr7Jhn|IXOAOsHAo|sLS z?kl||8V#{)SK~-i;>QnuFEulj7mgGgs&iYXw{Pmj)NrFQ>>k1T*b6UEB>|!!f#k)OHGu;|=$H`Us&D%aL@S>BQp7yPwdQR*O{EIir{?`-{0C8Jw&VuTV{a zAe3-G8>ETrl_F7eC<#}PS{@0cfwEg0KmyGUfjm*oQOttLvLaqj90fx&YFRqiNC&qx zfNwqII_q0oGG^1DFze_ErX2E87r3J+F@&TxpfkRcW3ADEPK_Xu<~50sOeCF}Ta(nQ zP7!yKLCtBK&2I`(+6roSWQWjV)Hf|5D89uQZGiW-W-=+A(!zV`a#x41x3@^ogLO8K znNy2QbPx)yypxn%fBvU|savWbuoiS(insc)MbXznNri?=6Q}TVt*V-1KxLzAiQHaq zx=QQlK#qYUQ0m;csoc9v}N%}ER`9bHQ!94W<-FsJh#+E~QK!EEnoxAJFSnWU1?IYj;{+QbQ#m%wZ zZvWN?T($b9Hrp?*oqLxkYji|)7xu57A!&dcx2L zL$DW)!9|SYc3i|Wc!dFblpSH``4F%1MSh%r;0yT1eFuD}eDC-^_CM}F - + - + @@ -47,9 +47,9 @@ - + - + @@ -58,19 +58,19 @@ - + - - + + - + @@ -79,7 +79,7 @@ - + @@ -90,26 +90,26 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + - + @@ -126,4 +126,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/sd_icons.ttf b/fonts/sd_icons.ttf index ede7e3c1c0443e550f87895a0983d3f0d96dbb48..c5f33407ac5e865bd00bc6f8a65258df5e2a1151 100755 GIT binary patch delta 4499 zcmZu!eQX=$8GoO99XpQWkMq|#cADnm*m3%m*q5`@uW(BXbOd!N1=?<+ZGeV~(c-CH z>8ipV)j(YZooU9jA=oOkRVw3;NI}yyp>i-ZA;j7uZPO+J%An()A;dI>m=vG)d(RF{ zOV9V-`~7^szvuPe4$MAkGGdGcn8i37{?_)PQaH+ggOOKJ_TG13s+zw0Po0cWH~M!z zFmPLS+zJ;;%Z-Pkjz|_$y`w24{+wdMX^d6i#uJgKRrvM!J0DW+tID;i%z**>q2RHLswK&0{e78ZihXpnpGtw(HY_VT~?U7F?* zKle)Yamgn=81#hE;g3HR4c0G+wbFPn>M)YoEJd8NQf-pBpyhlr^DKXIG$^d)r=|S4 zDs#)Ro!8R2Tv{4W=ZprE3^H=*1;-sCxMXg-=U~wE!!L|m++txCV+zZ##K*_3n&-}*;J>ml2*$XZ&sl}#;tfkojI??`3qR%@j* zvnje~Rg6?_=hJnT&gat{-+IlOC037Ek|l=f;!C$ZL$XtwyBMwVt=FnAtGYt0KTYG0 z!vy{P>2b+!Fz143N}hCogV|h4_gnqxg@ts#^oHYz)%6YTyky%mPbL3n5;K^=ZEi3b zh8Vcc5?RTBfit;WhBNV%Y%7L?XU^(*|dCW?6K^IR()E%2%1zbrn93xj&X|_=jp`C$&Gm$b6%*fHnove)mp8#qC7&EyKh^u z=8lcoI#Wk)UwO-J>KyCq8*^-1RN&zRnq6}Yd<ysLEvg@DT$>*>~f`O z5oyEbX7II$G~=t`3es#}LuV1MY`mh4vh*@Ndx7olkG~j0&$j+G;VH`qGf~u|#ktw^L!AJDT7B)FM&#eUu zX~Q)!G0T{DjCoiJaU%>Y&h-rm;!w&y;VW|KA#e;qgRJ>^LC>Y>QiamJWX~f4e8OoE zeeT8nDj!<{-nvG*0$ps|Le2nYzu8?VbklJgClzsyQK2ERLU(;jcfls~=OznB{bu8R zX!t&Cwgm*bRZk-p4pEy*B{^R8gzT#Apz1$TOvNp!5ykY>0EemlVNCQ`W9OvJTP zrCd?GJ}+)0*}j3iK0H#~KzjKmLRx|PngGlaFh}Vjya^Erij0@GamNGV#~u?bfu!SD?NE%G!RIKfa;ye4u81K+vYXB80+fojQjmz zbbUT4w?EGUZ0!|%F$4}PnLU0YLhnpa-KGK#=O3EueUAa_s0Vi%5(-|zOb*& z8}|DX=y%~l2u%$p@c;ri$|?XV~5KD=ey;H^`m7b>UY5KLSRY;H?7D?%)_Z;fQ?BV!V-ej@o~)n+Eb ziGakd8=*+~t)7I)*6+EcP`KqDo$SeKbrOB#TyaAZ_Xl!&jv;0c;9{9rUY(q*iiB(A z)0v!ThyhoaZ7gc?r}^t_v$zL_$aSRfAe>(6ksy)}qXONmtp_;l&EA1T552()h#Cmnpo~|VASnHe*1cz05S%7vVyLNBtGuJ1b z@2UCx_|8&kr%F5M5zxRh3_60wfSA#Csm@F8@^vj5%jCYHV1VvUuJ7BlyDRBbRqWsO zje&t)LTE3=8<7(PFtV7e%084qYJwy1Q8u_K-~^Wo)_`+R&}t$NhQ&aug1Nk3fmi_f zZ}GOvDnQqSTaW=Y1eKnx;6>_6XmF&9lRB`{und7thYpDsO#|=`I5rpY*zPrYF-Gjk zbp~EUie=$~V2ccCAnyQAp@{%u@Bt_H7t;Wr8i%tjybV)=z{G_fOr%ZR-GNsUa2WD8 z>3D$Ypi^3;V>Sr^&0B{9M+D~>mm_hqp+Let;o<-lWwpgf5{40xCm5kY5N;_?R{?zSGsGu>*aJRJ)V9n{bKrqOkd`<%!$k&Ggn|0;iyyWQRbfu I>2K5j0iycdqW}N^ delta 2507 zcmaJ?4~QJq8UMcbc6V;?ZvV{9?Vs$O<|ey)yLU#jxxKx)+^w6JtF@xR1{*Fue&N-s`!wYjunc25p030NK z=c98^Ezue$Uawt`&j0w4@BZkue&Uw_O56ACo!b+>_w;Un%@^tW#6H?EE(EH?UnM@b zZ(;xPhjHHH9Z<+hu^XdzL@m@M`Wnpf437&%nKqWx@(Bj;}-d}vQYk~M2 z2^?E`;;H>)C|wh0e}49Rc;Fb`g9lpM@l|XDfwdL?BmWhIp_gnNgfSASp)-+8l{v;R z<(Re^97`tpL)i_(mGKF*OEp#=OP0s8HWHEA9-F9`hHY}$2CtFH=QC_3lNW7Z5+R?t>iJvS1h&2tSd28f zDNn#9*V!^CYca4O1p}}ivQPl9$U$zKcF<1Pg>oXBy1^s$_2QV6*W9+uvrOfHw1VM=+2n)PJbd{gBzZdGrx8KD$F^ZDd|8jL&_X4Bw996Vpgl6zmUuE01EbD2kK*g9j8ILRBa|iBy*Zd6ao3r1Xd==u&d3 zQ?HHmMH{QJ8(jDu2>AkL(Xb0nIc3;5fLY$CHoVWbdT&#tPuzV)JdVOM_TT@GxPzT@ zh1hoC!ZvE?jQ3CNIj6}a_<90MgAJo(XlbG{ejCebLn-mpwVLJSl5XkES&N+TU)l1k zwP{DS4MN~h^}(6!_}1~WqFxufch?U$Bz}D757XPGio%s*oils;^!AN?m>#OvhrGjB z8+rO^>LfrEq#Q^&Ra>3{WcdN&Srk~RAFX?TrH2r@)?7*FnXWai{PPJNQ>RRQT`6Q*ZIC9eIYm(PQBqt%YCHm@g$iC9K!8>UAWt|2%9)X>D3bNz zC>dH%W9eigeYmCp0;?(4jnG(`8`MDw|2FBVNheb<<&x97-(5v@A|y=(-4mSvtMa;1 zj8$2Ug=8_AprtiQ&2>VoVdzR;8(yG6^swSYK8Vs*(DWlagoaUHw}c||4aTfFv*Hpt zvu4J%m7LMH{%`5(2*>qP)Hi4iM6aBtXrhBqXoEUQ$<@nuR;yrYO&1v(TCI!!Io+U< zouLBJprl|e{G2L;YgJBx0dH9@0;MjW|5vbrZ+#6P3E$Vbx!{QF z@flFTz>PJuexF<7HM};!D`l*txR6Ro8UV^WGFU7Q;z890g`hVTWnUEs-Ma>hG&uAO zZ|x~Ahl_v!mw)o=O%KJYUqMfR!UOQP^rkP@)^@x7hX8QZoZqqupJ~3lWj(Uyxh;P| z*?eK@KsQqAQIMEu#-~%w?DXE|#c8t<$L+X?r|=>Jwu>ERXZX#$$`|=j{$3yy7!B+X zoCy3W@Nw{g;HlvG;HRNXNQ7oXuY^7`qDIcR(|E*?#!JR~UAeBux{h@Hy6Z}KWB7sa zk?^_jrS3rYeceCnKHdFJPpD_8r`mIW&r7|b-n)Ab_Wri_-;qqD7TMW|JQg_}c|G#y z$cNE*R77W^^U+h$^U+UY>DWwcUrff%#NLhf#Yf}!#h2qJ;;+a55%Kyr|t) KuGW{;H}Jpp+e*j) diff --git a/fonts/sd_icons.woff b/fonts/sd_icons.woff index 118bae4b7d6fa82671d859eee4bf732fe6cd33d2..d8448f0c18e3c47c3e4ac0bf547e3cfaaded4930 100755 GIT binary patch delta 4442 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 delta 2508 zcmaJ@4QO0d7Cz_RnR%H>=I7;Qemcpvd7YWeBvw0{$z)q|{rtEXyL@T~?%s2-PA*L_|?4^u5`0UlWoR z>rBqO@1D8mJAdD~r(Q?T12!)-QZKEuoYx+lo8Aq8Go;_H zB|G_{`{?vjT6c=}9gC89boS{-NOu;Xyj9B`kNvG@=Hcmw0EQEEW>ib~?h`v_NJ+Xa zw9jiX&IIZ+b9)v@H%s>btq=1#(>7M*DtWo&#ro zdhTJ;>409=^2!%G=5{~v)E>$w@Y0ogaBp)f-i3QxNAV4Gg238}|DFE@!q5c?=!0Rh zX<%(Mm9B7%W4dPAW^g!_>_fI-Skbj}Lyte{4vaM# zf={@odkckL64vXAq54Fo++WU&*T*xNv2s6?*<3Joa}L*d_JvgD&(FJ(N$DRa6HnH+ z)Wx=`ZNAyz=70HfV=*uR!f3AzCC#2x#U385nucw1*#^(a77AH5ku8WeFo{seUh#a@ zCV{ON1M^WQ0uq2rE`ftV9gTwpY3PMDkb@$CMG11-w1aliE>@Dc^bJTt(K;Jhw&h4sQBhg>og;hnxWTrImiSC`$RMW!6VA>UGAd4zJ=;_pjXq8uA zV1y+7nhBpNt7saiS~;^qHK>?K+_*|Z+Z-9--~knaP!&o~A~CSHfU>}ZlpYC{S4vKd z^x8;0<=~D@?@<@Ju}i(MYcX1 zHA5R}EZuCRAC^5pV6o+z^3K&8f@US@e@%L7(ajW0xs-Aafh49(=FYYH@ozLq$N52ADwbk9*7Lc^%*BcaNy%;vgeuBJrE zEQPVWQZo8WzM-ReF0;zamDAmrTp=j5^Od6H;>S-+!!fmlOM!(JIk23d1zF;P z#w7fuN^>Fu&pG6(R%Ad^q(_U=Uc7CU_OYUJ3|xUyQ{_eutm_;gIq;npYvNjX2E+~U z9VCGwzuMjv9$Kuhn>TRzTCP^Gn&v_(C281|cc`ya>cjo24GKXk6?Iyb`rIvjCAuW^ z&D?scv=A-<0$lvv8^3xWUjG3)15_S>|7F&HzqGd7?e_z~RrB3j*W)wIOSi5;*6bMn zH_GPu@x2kGHlrXj(cH0NzvG)npa)jNE_fbJV*5Kke%eKd7aPm!~Ekw zC@>V*6F3_9N8nQM{^0T8>EPF)Y)FJAL$8OvF=9sE*lavvNaI!GxgyU`mSfJ=WKF5`Fc_%FQ#r!9Y}qZE~mGr|CJfgWrSxU@GM09 P8Zi1l&1&7X@ooG+KHpA* diff --git a/scripts/apps/authoring/authoring/directives/ArticleEditDirective.js b/scripts/apps/authoring/authoring/directives/ArticleEditDirective.js index f207463db0..1811df74b0 100644 --- a/scripts/apps/authoring/authoring/directives/ArticleEditDirective.js +++ b/scripts/apps/authoring/authoring/directives/ArticleEditDirective.js @@ -62,6 +62,7 @@ export function ArticleEditDirective( scope.editSignOff = false; scope.mediaLoading = false; scope.validator = config.validatorMediaMetadata; + scope.features = config.features; var mainEditScope = scope.$parent.$parent; var autopopulateByline = config.features && config.features.autopopulateByline; diff --git a/scripts/apps/authoring/authoring/directives/SendItem.js b/scripts/apps/authoring/authoring/directives/SendItem.js index 21c90a2094..1339ef2c0b 100644 --- a/scripts/apps/authoring/authoring/directives/SendItem.js +++ b/scripts/apps/authoring/authoring/directives/SendItem.js @@ -2,11 +2,11 @@ import _ from 'lodash'; SendItem.$inject = ['$q', 'api', 'desks', 'notify', 'authoringWorkspace', 'superdeskFlags', '$location', 'macros', '$rootScope', - 'authoring', 'send', 'editor', 'confirm', 'archiveService', + 'authoring', 'send', 'editorResolver', 'confirm', 'archiveService', 'preferencesService', 'multi', 'datetimeHelper', 'config', 'privileges', 'storage']; export function SendItem($q, api, desks, notify, authoringWorkspace, superdeskFlags, $location, macros, $rootScope, - authoring, send, editor, confirm, archiveService, + authoring, send, editorResolver, confirm, archiveService, preferencesService, multi, datetimeHelper, config, privileges, storage) { return { scope: { @@ -26,6 +26,8 @@ export function SendItem($q, api, desks, notify, authoringWorkspace, controllerAs: 'vm', templateUrl: 'scripts/apps/authoring/views/send-item.html', link: function sendItemLink(scope, elem, attrs, ctrl) { + const editor = editorResolver.get(); + scope.mode = scope.mode || 'authoring'; scope.desks = null; scope.stages = null; diff --git a/scripts/apps/authoring/editor/find-replace.js b/scripts/apps/authoring/editor/find-replace.js index 97b27fc219..0bd327ced5 100644 --- a/scripts/apps/authoring/editor/find-replace.js +++ b/scripts/apps/authoring/editor/find-replace.js @@ -7,16 +7,14 @@ * AUTHORS and LICENSE files distributed with this source code, or * at https://www.sourcefabric.org/apps/license */ -FindReplaceDirective.$inject = ['editor', 'editor3', 'macros', 'authoring']; +FindReplaceDirective.$inject = ['editorResolver', 'macros']; /** * using directive here so that it can return focus */ -function FindReplaceDirective(editor2, editor3, macros, authoring) { +function FindReplaceDirective(editorResolver, macros) { return { link: function(scope, elem) { - // use the editor service of editor3, if it's active - const isEditor3 = authoring.editor.body_html.editor3; - const editor = isEditor3 ? editor3 : editor2; + const editor = editorResolver.get(); scope.to = ''; scope.from = ''; diff --git a/scripts/apps/authoring/macros/macros.js b/scripts/apps/authoring/macros/macros.js index ace385cd9b..0cbb7e3ec0 100644 --- a/scripts/apps/authoring/macros/macros.js +++ b/scripts/apps/authoring/macros/macros.js @@ -234,12 +234,14 @@ function MacrosController($scope, macros, desks, autosave, $rootScope, storage) * apply the results of triggered macro with the use of available set of methods such that next, * prev and replace */ -MacrosReplaceDirective.$inject = ['editor']; -function MacrosReplaceDirective(editor) { +MacrosReplaceDirective.$inject = ['editorResolver']; +function MacrosReplaceDirective(editorResolver) { return { scope: true, templateUrl: 'scripts/apps/authoring/macros/views/macros-replace.html', link: function(scope) { + const editor = editorResolver.get(); + scope.diff = null; scope.$on('macro:diff', (evt, diff) => { diff --git a/scripts/apps/authoring/tests/authoring.spec.js b/scripts/apps/authoring/tests/authoring.spec.js index e8579be658..cc69c5cfdd 100644 --- a/scripts/apps/authoring/tests/authoring.spec.js +++ b/scripts/apps/authoring/tests/authoring.spec.js @@ -2274,9 +2274,15 @@ describe('authoring themes', () => { describe('send item directive', () => { beforeEach(window.module(($provide) => { - $provide.constant('config', {server: {url: undefined}, iframely: {key: '123'}, editor: {}}); + $provide.constant('config', { + server: {url: undefined}, + iframely: {key: '123'}, + editor: {}, + features: {onlyEditor3: false} + }); })); + beforeEach(window.module('superdesk.core.editor3')); beforeEach(window.module('superdesk.apps.editor2')); beforeEach(window.module('superdesk.core.preferences')); beforeEach(window.module('superdesk.apps.authoring')); diff --git a/scripts/apps/authoring/views/article-edit.html b/scripts/apps/authoring/views/article-edit.html index 9aff3d820f..eb5b212677 100644 --- a/scripts/apps/authoring/views/article-edit.html +++ b/scripts/apps/authoring/views/article-edit.html @@ -1,4 +1,4 @@ -
+
{{item.headline}}
-
+
-
+
-
+
-
+
@@ -198,12 +199,13 @@
-
+
-
{{item.description_text}}
-
+
{{ :: 'Editing' | translate }} "{{ editing.form.label }}" + +
diff --git a/scripts/apps/workspace/content/views/schema-editor.html b/scripts/apps/workspace/content/views/schema-editor.html index 17400b80be..5ec0ef4e5f 100644 --- a/scripts/apps/workspace/content/views/schema-editor.html +++ b/scripts/apps/workspace/content/views/schema-editor.html @@ -17,10 +17,6 @@

Schema Configuration

-
- -
-
diff --git a/scripts/core/editor3/components/Editor3.jsx b/scripts/core/editor3/components/Editor3.jsx index 2edef56713..1c4f72bff7 100644 --- a/scripts/core/editor3/components/Editor3.jsx +++ b/scripts/core/editor3/components/Editor3.jsx @@ -69,7 +69,9 @@ export class Editor3Component extends React.Component { return; } - const toolbarStyle = editorRect.top < pageRect.top + 50 ? 'fixed' : 'relative'; + const isToolbarOut = editorRect.top < pageRect.top + 50; + const isBottomOut = editorRect.bottom < pageRect.top + 60; + const toolbarStyle = isToolbarOut && !isBottomOut ? 'fixed' : 'relative'; if (toolbarStyle !== this.state.toolbarStyle) { this.setState({toolbarStyle}); @@ -99,7 +101,12 @@ export class Editor3Component extends React.Component { * @description Handles key commands in the editor. */ handleKeyCommand(command) { - const {editorState, onChange} = this.props; + const {editorState, onChange, singleLine} = this.props; + + if (singleLine && command === 'split-block') { + return 'handled'; + } + const newState = RichUtils.handleKeyCommand(editorState, command); if (newState) { @@ -142,6 +149,7 @@ export class Editor3Component extends React.Component { let cx = classNames({ 'Editor3-root Editor3-editor': true, 'floating-toolbar': toolbarStyle === 'fixed', + 'no-toolbar': !showToolbar, 'read-only': readOnly }); @@ -174,7 +182,8 @@ Editor3Component.propTypes = { unsetReadOnly: React.PropTypes.func, onTab: React.PropTypes.func, dragDrop: React.PropTypes.func, - scrollContainer: React.PropTypes.object + scrollContainer: React.PropTypes.string, + singleLine: React.PropTypes.bool }; const mapStateToProps = (state) => ({ diff --git a/scripts/core/editor3/components/embeds/EmbedButton.jsx b/scripts/core/editor3/components/embeds/EmbedButton.jsx index c72b80454a..6574116993 100644 --- a/scripts/core/editor3/components/embeds/EmbedButton.jsx +++ b/scripts/core/editor3/components/embeds/EmbedButton.jsx @@ -45,7 +45,7 @@ export class EmbedButton extends Component { return (
- embed + {dialogOpen ? : null}
); diff --git a/scripts/core/editor3/components/images/ImageButton.jsx b/scripts/core/editor3/components/images/ImageButton.jsx index f2f24b565b..c2cadea9ca 100644 --- a/scripts/core/editor3/components/images/ImageButton.jsx +++ b/scripts/core/editor3/components/images/ImageButton.jsx @@ -11,7 +11,7 @@ import * as actions from '../../actions'; */ const ImageButtonComponent = ({insertImages}) =>
- img +
; ImageButtonComponent.propTypes = { diff --git a/scripts/core/editor3/components/links/LinkButton.jsx b/scripts/core/editor3/components/links/LinkButton.jsx index f45eb51b04..eb7b05cde2 100644 --- a/scripts/core/editor3/components/links/LinkButton.jsx +++ b/scripts/core/editor3/components/links/LinkButton.jsx @@ -79,7 +79,9 @@ export class LinkButtonComponent extends Component { return (
- link + + + {entityType === 'LINK' ? - tbl +
); } diff --git a/scripts/core/editor3/components/tests/editor3.spec.jsx b/scripts/core/editor3/components/tests/editor3.spec.jsx index ba53db4b6d..126a95c41d 100644 --- a/scripts/core/editor3/components/tests/editor3.spec.jsx +++ b/scripts/core/editor3/components/tests/editor3.spec.jsx @@ -9,14 +9,7 @@ describe('editor3.component', () => { const wrapper = shallow(); expect(wrapper.find('DraftEditor').length).toBe(1); - expect(wrapper.find('Toolbar').length).toBe(0); - }); - - it('should show toolbar when enabled', () => { - const wrapper = shallow(); - - expect(wrapper.find('DraftEditor').length).toBe(1); - expect(wrapper.find('Toolbar').length).toBe(1); + expect(wrapper.find('.Editor3-controls').length).toBe(0); }); it('should mount and put client rect inside attribute on update', () => { diff --git a/scripts/core/editor3/components/tests/links.spec.jsx b/scripts/core/editor3/components/tests/links.spec.jsx index c0b656d3bb..4f69d981be 100644 --- a/scripts/core/editor3/components/tests/links.spec.jsx +++ b/scripts/core/editor3/components/tests/links.spec.jsx @@ -27,9 +27,7 @@ function getWrapper(es = null, shallowMount = false) { describe('editor3.components.link-button', () => { it('should render button text', () => { const {wrapper} = getShallowWrapper(); - const button = wrapper.find('span').first(); - expect(button.text()).toBe('link'); expect(wrapper.find('LinkPopover').length).toBe(0); }); diff --git a/scripts/core/editor3/components/tests/styles.spec.jsx b/scripts/core/editor3/components/tests/styles.spec.jsx index 674b13eedd..d25e2cbd9c 100644 --- a/scripts/core/editor3/components/tests/styles.spec.jsx +++ b/scripts/core/editor3/components/tests/styles.spec.jsx @@ -7,10 +7,10 @@ import {InlineStyleControlsComponent as InlineStyleControls} from '../toolbar/In describe('editor3.components.toolbar', () => { it('(StyleButton) should render label', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.hasClass('Editor3-styleButton')).toBe(true); - expect(wrapper.text()).toBe('button_label'); + expect(wrapper.find('i.icon-heading-1').length).toBe(1); }); it('(StyleButton) should become active when prop is set to true', () => { diff --git a/scripts/core/editor3/components/toolbar/StyleButton.jsx b/scripts/core/editor3/components/toolbar/StyleButton.jsx index c782100049..4dc968cba1 100644 --- a/scripts/core/editor3/components/toolbar/StyleButton.jsx +++ b/scripts/core/editor3/components/toolbar/StyleButton.jsx @@ -1,4 +1,21 @@ import React from 'react'; +import classNames from 'classnames'; + +const StyleIcons = { + bold: 'icon-bold', + italic: 'icon-italic', + underline: 'icon-underline', + strikethrough: 'icon-strikethrough', + h1: 'icon-heading-1', + h2: 'icon-heading-2', + h3: 'icon-heading-3', + h4: 'icon-heading-4', + h5: 'icon-heading-5', + h6: 'icon-heading-6', + quote: 'icon-quote', + ul: 'icon-unordered-list', + ol: 'icon-ordered-list' +}; /** * @ngdoc React @@ -18,15 +35,16 @@ export default class StyleButton extends React.Component { } render() { - let className = 'Editor3-styleButton'; + const {active, label} = this.props; - if (this.props.active) { - className += ' Editor3-activeButton'; - } + const cx = classNames({ + 'Editor3-styleButton': true, + 'Editor3-activeButton': active + }); return ( - - {this.props.label} + + ); } diff --git a/scripts/core/editor3/components/toolbar/index.js b/scripts/core/editor3/components/toolbar/index.js index 55f1ae71e1..48c3080c1f 100644 --- a/scripts/core/editor3/components/toolbar/index.js +++ b/scripts/core/editor3/components/toolbar/index.js @@ -5,6 +5,7 @@ import {LinkButton} from '../links'; import {ImageButton} from '../images'; import {EmbedButton} from '../embeds'; import {TableButton} from '../tables'; +import {connect} from 'react-redux'; import classNames from 'classnames'; /** @@ -15,9 +16,10 @@ import classNames from 'classnames'; * @param {boolean} disabled Disables clicking on the toolbar, if true. * @description Holds the editor's toolbar. */ -export default class Toolbar extends Component { +class ToolbarComponent extends Component { render() { - const {editorRect, disabled} = this.props; + const {editorRect, disabled, editorFormat} = this.props; + const has = (opt) => editorFormat.indexOf(opt) > -1; const cx = classNames({ 'Editor3-controls': true, disabled: disabled @@ -27,16 +29,23 @@ export default class Toolbar extends Component {
- - - - + {has('anchor') ? : null} + {has('picture') ? : null} + {has('embed') ? : null} + {has('table') ? : null}
); } } -Toolbar.propTypes = { +ToolbarComponent.propTypes = { editorRect: React.PropTypes.object.isRequired, - disabled: React.PropTypes.bool + disabled: React.PropTypes.bool, + editorFormat: React.PropTypes.array }; + +const mapStateToProps = (state) => ({editorFormat: state.editorFormat}); + +const Toolbar = connect(mapStateToProps, null)(ToolbarComponent); + +export default Toolbar; diff --git a/scripts/core/editor3/directive.js b/scripts/core/editor3/directive.js index d9fb250b28..8352df883b 100644 --- a/scripts/core/editor3/directive.js +++ b/scripts/core/editor3/directive.js @@ -24,25 +24,88 @@ class Editor3Directive { this.controller = ['$element', 'editor3', '$scope', this.initialize]; this.bindToController = { + /** + * @type {String} + * @description If set, it will be used to make sure the toolbar is always + * visible when scrolling. If not set, window object is used as reference. + * Any valid jQuery selector will do. + */ scrollContainer: '@', - config: '=', - editorFormat: '=', - language: '=', - onChange: '&', + + /** + * @type {Boolean} + * @description Whether this editor is the target for find & replace + * operations. The Find & Replace service can only have one editor as + * target. + */ + findReplaceTarget: '@', + + /** + * @type {Object} + * @description Editor format options that are enabled and should be displayed + * in the toolbar. + */ + editorFormat: '=?', + + /** + * @type {Object} + * @description A JSON object representing the Content State of the Draft + * editor. When available, it is used to show content, using `convertFromRaw`. + * Either this, or value have to be set. Use this for most accurate behavior. + */ + editorState: '=?', + + /** + * @type {String} + * @description HTML value of editor. Used by the outside world. + */ value: '=', - editorState: '=', - readOnly: '=' + + /** + * @type {Boolean} + * @description If true, editor is read-only. + */ + readOnly: '=?', + + /** + * @type {Function} + * @description Function that gets called on every content change. + */ + onChange: '&', + + /** + * @type {String} + * @description Spellchecker's language. + */ + language: '=?', + + /** + * @type {Boolean} + * @description Disables the Enter key if the attribute is set. + */ + singleLine: '@' }; } initialize($element, editor3, $scope) { + // defaults + this.language = this.language || 'en'; + this.readOnly = this.readOnly || false; + this.findReplaceTarget = typeof this.findReplaceTarget !== 'undefined'; + this.singleLine = typeof this.singleLine !== 'undefined'; + const store = createStore(this); - editor3.setStore(store); + if (this.findReplaceTarget) { + editor3.setStore(store); + $scope.$on('$destroy', editor3.unsetStore); + } ReactDOM.render( - + , $element.get(0) ); } diff --git a/scripts/core/editor3/index.js b/scripts/core/editor3/index.js index 2c01b1711e..65620434c6 100644 --- a/scripts/core/editor3/index.js +++ b/scripts/core/editor3/index.js @@ -2,6 +2,7 @@ import './styles.scss'; import {EditorService} from './service'; import {sdEditor3} from './directive'; +import ng from 'core/services/ng'; /** * @ngdoc module @@ -12,4 +13,14 @@ import {sdEditor3} from './directive'; */ export default angular.module('superdesk.core.editor3', ['superdesk.apps.spellcheck']) .service('editor3', EditorService) + .service('editorResolver', ['editor', 'editor3', 'config', function(editor2, editor3, config) { + // Enables the use of editor2 and editor3 in parallel. + // Resolves to old editor in case editor3 is inactive. + this.get = function() { + const authoring = ng.get('authoring'); + const editor3Active = authoring.editor && authoring.editor.body_html && authoring.editor.body_html.editor3; + + return config.features.onlyEditor3 || editor3Active ? editor3 : editor2; + }; + }]) .directive('sdEditor3', sdEditor3); diff --git a/scripts/core/editor3/reducers/find-replace.jsx b/scripts/core/editor3/reducers/find-replace.jsx index 59dc887949..72cb944686 100644 --- a/scripts/core/editor3/reducers/find-replace.jsx +++ b/scripts/core/editor3/reducers/find-replace.jsx @@ -157,7 +157,7 @@ export default findReplace; * @param {Object} state * @description Returns the number of occurences of the search criteria inside the current editor content. */ -const countOccurrences = (state) => { +export const countOccurrences = (state) => { const content = state.editorState.getCurrentContent(); const {pattern, caseSensitive} = state.searchTerm; const re = new RegExp(pattern, 'g' + (caseSensitive ? '' : 'i')); @@ -233,7 +233,7 @@ const createSelection = (key, start, end) => * @description Searches the content for the given pattern and calls the given callback * for each occurrence, passing it the index of the match and its SelectionState. */ -const forEachMatch = (content, pattern, caseSensitive, cb) => { +export const forEachMatch = (content, pattern, caseSensitive, cb) => { if (!pattern) { return false; } diff --git a/scripts/core/editor3/service.js b/scripts/core/editor3/service.js index 1086291438..e59c171853 100644 --- a/scripts/core/editor3/service.js +++ b/scripts/core/editor3/service.js @@ -1,4 +1,6 @@ import * as action from './actions/find-replace'; +import ng from 'core/services/ng'; +import {countOccurrences, forEachMatch} from './reducers/find-replace'; /** * @type {Object} Redux store @@ -23,9 +25,22 @@ export class EditorService { * @description Registers the passed redux store with the service. */ setStore(s) { + if (store !== null) { + console.warn('You\'ve overwritten the find & replace target.'); + } + store = s; } + /** + * @ngdoc method + * @name editor3#unsetStore + * @description Clears the store. + */ + unsetStore() { + store = null; + } + /** * @ngdoc method * @name editor3#selectNext @@ -33,7 +48,7 @@ export class EditorService { * criteria in the editor. */ selectNext() { - store.dispatch(action.findNext()); + ok() && store.dispatch(action.findNext()); } /** @@ -43,7 +58,7 @@ export class EditorService { * criteria in the editor. */ selectPrev() { - store.dispatch(action.findPrev()); + ok() && store.dispatch(action.findPrev()); } /** @@ -53,7 +68,7 @@ export class EditorService { * @description Replaces the currently highlighted search criteria with the given text. */ replace(txt) { - store.dispatch(action.replace(txt)); + ok() && store.dispatch(action.replace(txt)); } /** @@ -63,7 +78,7 @@ export class EditorService { * @description Replaces all the search criteria with the given text. */ replaceAll(txt) { - store.dispatch(action.replaceAll(txt)); + ok() && store.dispatch(action.replaceAll(txt)); } /** @@ -74,6 +89,10 @@ export class EditorService { * @description Updates the search criteria. */ setSettings({findreplace}) { + if (!ok()) { + return; + } + if (findreplace === null) { store.dispatch(action.setHighlightCriteria({pattern: ''})); @@ -92,6 +111,54 @@ export class EditorService { * @description Highlights the current search criteria in the editor. */ render() { - store.dispatch(action.renderHighlights()); + ok() && store.dispatch(action.renderHighlights()); + } + + /** + * @ngdoc method + * @name editor3#countErrors + * @description Not sure what this method is supposed to do. It's needed by + * the interface and it's copied from `core/editor2/editor.js#93`. + */ + countErrors() { + return ng.get('$q').when(countOccurrences(store.getState())); } + + /** + * @ngdoc method + * @name editor3#getActiveText + * @description Gets the text under the current selection. + */ + getActiveText() { + if (!ok()) { + return; + } + + const state = store.getState(); + const {editorState, searchTerm} = state; + const {index, pattern, caseSensitive} = searchTerm; + const content = editorState.getCurrentContent(); + + let txt = pattern; + + // find the active match + forEachMatch(content, pattern, caseSensitive, (i, selection, block) => { + if (i === index) { + const start = selection.getStartOffset(); + const end = selection.getEndOffset(); + + txt = block.getText().slice(start, end); + } + }); + + return txt; + } +} + +function ok() { + if (store === null) { + console.error('No editor set as target in service.'); + } + + return store !== null; } diff --git a/scripts/core/editor3/styles.scss b/scripts/core/editor3/styles.scss index 5288bb34ea..2231e34ee3 100644 --- a/scripts/core/editor3/styles.scss +++ b/scripts/core/editor3/styles.scss @@ -8,6 +8,10 @@ padding: 40px 15px 15px; position: relative; + &.no-toolbar { + padding-top: 5px; + } + &.floating-toolbar .Editor3-controls { position: fixed; top: 132px; @@ -158,6 +162,7 @@ left: 0; padding: 15px; background-color: #fff; + line-height: 100% !important; &.disabled { * { @@ -182,15 +187,15 @@ } input { - height: 33px; - padding: 8px 47px 8px 8px; + height: 53px; + padding: 8px 47px 8px 15px; } } .input-controls { position: absolute; - top: 0; - right: 0; + top: 10px; + right: 5px; margin: 8px; i { @@ -216,17 +221,13 @@ top: 0; input[type="url"] { - height: 33px; - padding: 8px 47px 8px 8px; + height: 53px; + padding: 8px 47px 8px 15px; } } } .Editor3-styleButton { - .inactive { - color: #ccc; - } - .link-button + .link-editor { a { margin-right: 5px; @@ -250,8 +251,54 @@ margin-right: 16px; padding: 2px 0; display: inline-block; + + font-size: 13px; + font-style: normal; + + span { + font-size: 13px; + font-style: normal; + } + + [class*="icon-"] { + color: #999; + + &:hover { + color: #555; + } + } + + .inactive [class*="icon-"] { + color: #ddd; + + &:hover { + color: #ddd; + } + } + + &.Editor3-activeButton [class*="icon-"] { + color: #5890ff; + } } -.Editor3-activeButton { - color: #5890ff; +/** Editor2 CSS Fix for headline **/ +.main-article .headline div.Editor3-root div { + font-size: 14px !important; + line-height: 100% !important; + + .DraftEditor-editorContainer div { + font-size: 28px !important; + line-height: 120% !important; + } +} + +/** Editor2 CSS Fix for abstract **/ +.main-article .abstract div.Editor3-root div { + font-size: 14px !important; + line-height: 100% !important; + + .DraftEditor-editorContainer div { + font-size: 16px !important; + line-height: 150% !important; + } } diff --git a/scripts/core/editor3/tests/editor3core.spec.jsx b/scripts/core/editor3/tests/editor3core.spec.jsx index 78680314ac..93632fb08b 100644 --- a/scripts/core/editor3/tests/editor3core.spec.jsx +++ b/scripts/core/editor3/tests/editor3core.spec.jsx @@ -8,7 +8,9 @@ describe('editor3.service', () => { 'replace', 'replaceAll', 'setSettings', - 'render' + 'render', + 'getActiveText', + 'countErrors' ].forEach((fn) => { expect(typeof editor3[fn]).toBe('function'); }); diff --git a/styles/sass/sd-icon-font.scss b/styles/sass/sd-icon-font.scss index 3412add26f..71affff7cd 100644 --- a/styles/sass/sd-icon-font.scss +++ b/styles/sass/sd-icon-font.scss @@ -344,10 +344,10 @@ .icon-italic:before { content: "\e656"; } -.icon-text-height:before { +.icon-underline:before { content: "\e657"; } -.icon-text-width:before { +.icon-striketrough:before { content: "\e658"; } .icon-align-left:before { @@ -440,3 +440,50 @@ .icon-paste:before { content: "\e676"; } +.icon-arrow-left:before { + content: "\e677"; +} +.icon-arrow-right:before { + content: "\e678"; +} +.icon-info-large:before { + content: "\e679"; +} +.icon-help-large:before { + content: "\e67a"; +} +.icon-attachment:before { + content: "\e67b"; +} +.icon-attachment-large:before { + content: "\e67c"; +} +.icon-table:before { + content: "\e67d"; +} +.icon-ordered-list:before { + content: "\e67e"; +} +.icon-heading-1:before { + content: "\e67f"; +} +.icon-heading-2:before { + content: "\e680"; +} +.icon-heading-3:before { + content: "\e681"; +} +.icon-heading-4:before { + content: "\e682"; +} +.icon-heading-5:before { + content: "\e683"; +} +.icon-heading-6:before { + content: "\e684"; +} +.icon-quote:before { + content: "\e685"; +} + + diff --git a/webpack.config.js b/webpack.config.js index ffb0a5a6b4..0bc5595961 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -216,7 +216,10 @@ function getDefaults(grunt) { // app features features: { // tansa spellchecker - useTansaProofing: false + useTansaProofing: false, + + // replace editor2 + onlyEditor3: false }, // workspace defaults