show format bar when text is selected

This commit is contained in:
Bruno Windels 2019-09-03 16:02:37 +02:00
parent 0d02ab59d6
commit 65ddfc0a50
7 changed files with 150 additions and 0 deletions

View file

@ -16,6 +16,8 @@ limitations under the License.
*/ */
.mx_BasicMessageComposer { .mx_BasicMessageComposer {
position: relative;
.mx_BasicMessageComposer_inputEmpty > :first-child::before { .mx_BasicMessageComposer_inputEmpty > :first-child::before {
content: var(--placeholder); content: var(--placeholder);
opacity: 0.333; opacity: 0.333;
@ -71,4 +73,70 @@ limitations under the License.
position: relative; position: relative;
height: 0; height: 0;
} }
.mx_BasicMessageComposer_formatBar {
display: none;
background-color: red;
width: calc(26px * 4);
height: 24px;
position: absolute;
cursor: pointer;
border-radius: 4px;
background: $message-action-bar-bg-color;
&.mx_BasicMessageComposer_formatBar_shown {
display: block;
}
> * {
white-space: nowrap;
display: inline-block;
position: relative;
border: 1px solid $message-action-bar-border-color;
margin-left: -1px;
&:hover {
border-color: $message-action-bar-hover-border-color;
}
}
.mx_BasicMessageComposer_formatButton {
width: 27px;
height: 24px;
box-sizing: border-box;
}
.mx_BasicMessageComposer_formatButton::after {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
mask-repeat: no-repeat;
mask-position: center;
background-color: $message-action-bar-fg-color;
}
.mx_BasicMessageComposer_formatBold::after {
mask-image: url('$(res)/img/format/bold.svg');
}
.mx_BasicMessageComposer_formatItalic::after {
mask-image: url('$(res)/img/format/italics.svg');
}
.mx_BasicMessageComposer_formatStrikethrough::after {
mask-image: url('$(res)/img/format/strikethrough.svg');
}
.mx_BasicMessageComposer_formatQuote::after {
mask-image: url('$(res)/img/format/quote.svg');
}
.mx_BasicMessageComposer_formatCode::after {
mask-image: url('$(res)/img/format/code.svg');
}
}
} }

3
res/img/format/bold.svg Normal file
View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="13" viewBox="0 0 10 13">
<path fill="#212121" fill-rule="nonzero" d="M7.47 6.156c.732.204 1.299.57 1.701 1.098.402.528.603 1.188.603 1.98 0 1.092-.375 1.941-1.125 2.547-.75.606-1.797.909-3.141.909H.918c-.288 0-.513-.081-.675-.243C.081 12.285 0 12.066 0 11.79V.9C0 .624.081.405.243.243.405.081.63 0 .918 0H5.31c1.308 0 2.331.291 3.069.873.738.582 1.107 1.395 1.107 2.439 0 .672-.177 1.254-.531 1.746-.354.492-.849.858-1.485 1.098zM1.818 5.49h3.204c1.776 0 2.664-.672 2.664-2.016 0-.672-.219-1.17-.657-1.494-.438-.324-1.107-.486-2.007-.486H1.818V5.49zm3.492 5.706c.924 0 1.602-.168 2.034-.504.432-.336.648-.858.648-1.566 0-.72-.219-1.257-.657-1.611-.438-.354-1.113-.531-2.025-.531H1.818v4.212H5.31z"/>
</svg>

After

Width:  |  Height:  |  Size: 770 B

7
res/img/format/code.svg Normal file
View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="19" height="12" viewBox="0 0 19 12">
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round">
<path stroke-linejoin="round" d="M14.1 9.8L18 5.9 14.1 2"/>
<path d="M7.5 11.5l4-11"/>
<path stroke-linejoin="round" d="M4.9 2L1 5.9l3.9 3.9"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 348 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="3" height="13" viewBox="0 0 3 13">
<path fill="#212121" fill-rule="nonzero" d="M.542 12.87a.539.539 0 0 1-.396-.162.506.506 0 0 1-.144-.414L1.92.522A.551.551 0 0 1 2.478 0c.168 0 .303.051.405.153.102.102.147.243.135.423L1.1 12.348a.544.544 0 0 1-.171.387.563.563 0 0 1-.387.135z"/>
</svg>

After

Width:  |  Height:  |  Size: 340 B

5
res/img/format/quote.svg Normal file
View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="11" viewBox="0 0 13 11">
<g fill="#212121" fill-rule="nonzero">
<path d="M1.05 2.375c.25.017.458.112.625.288.167.175.25.404.25.687 0 .3-.087.542-.262.725A.877.877 0 0 1 1 4.35c-.667 0-1-.492-1-1.475C0 1.858.317.975.95.225 1.1.075 1.25 0 1.4 0a.44.44 0 0 1 .325.125.44.44 0 0 1 .125.325c0 .15-.05.275-.15.375a2.26 2.26 0 0 0-.462.7 3.215 3.215 0 0 0-.188.85zm3.575 0c.25.017.458.112.625.288.167.175.25.404.25.687 0 .3-.087.542-.263.725a.877.877 0 0 1-.662.275c-.667 0-1-.492-1-1.475 0-1.017.317-1.9.95-2.65.15-.15.3-.225.45-.225A.44.44 0 0 1 5.3.125a.44.44 0 0 1 .125.325c0 .15-.05.275-.15.375a2.26 2.26 0 0 0-.463.7 3.215 3.215 0 0 0-.187.85zM11.95 7.975a.916.916 0 0 1-.625-.287c-.167-.176-.25-.405-.25-.688 0-.3.087-.542.262-.725A.877.877 0 0 1 12 6c.667 0 1 .492 1 1.475 0 1.017-.317 1.9-.95 2.65-.15.15-.3.225-.45.225a.44.44 0 0 1-.325-.125.44.44 0 0 1-.125-.325c0-.15.05-.275.15-.375a2.26 2.26 0 0 0 .462-.7c.092-.233.155-.517.188-.85zm-3.575 0a.916.916 0 0 1-.625-.287C7.583 7.512 7.5 7.283 7.5 7c0-.3.087-.542.262-.725A.877.877 0 0 1 8.425 6c.667 0 1 .492 1 1.475 0 1.017-.317 1.9-.95 2.65-.15.15-.3.225-.45.225a.44.44 0 0 1-.325-.125.44.44 0 0 1-.125-.325c0-.15.05-.275.15-.375a2.26 2.26 0 0 0 .462-.7c.092-.233.155-.517.188-.85z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="13" viewBox="0 0 12 13">
<g fill="none" fill-rule="evenodd">
<path fill="#212121" fill-rule="nonzero" d="M9.566 7.834l-.071-.085c.39.426.585.999.585 1.719 0 .684-.189 1.293-.567 1.827-.378.534-.912.948-1.602 1.242-.69.294-1.479.441-2.367.441a7.85 7.85 0 0 1-2.565-.423c-.822-.282-1.467-.663-1.935-1.143a.65.65 0 0 1-.234-.522c0-.132.039-.246.117-.342.078-.096.165-.144.261-.144.12 0 .258.06.414.18 1.128.936 2.442 1.404 3.942 1.404 1.092 0 1.935-.219 2.529-.657.594-.438.891-1.059.891-1.863 0-.468-.144-.846-.432-1.134a2.753 2.753 0 0 0-.696-.5h1.73zM5.616 0c.828 0 1.608.135 2.34.405.732.27 1.338.657 1.818 1.161.156.156.234.33.234.522a.526.526 0 0 1-.117.342c-.078.096-.165.144-.261.144-.12 0-.258-.06-.414-.18-.648-.528-1.233-.894-1.755-1.098C6.939 1.092 6.324.99 5.616.99c-1.068 0-1.902.228-2.502.684-.6.456-.9 1.092-.9 1.908 0 .492.132.891.396 1.197l.052.055H1.326c-.152-.354-.228-.765-.228-1.234 0-.708.189-1.335.567-1.881.378-.546.909-.969 1.593-1.269C3.942.15 4.728 0 5.616 0z"/>
<rect width="12" height="1" y="5.834" fill="#000" rx=".5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -74,8 +74,10 @@ export default class BasicMessageEditor extends React.Component {
}; };
this._editorRef = null; this._editorRef = null;
this._autocompleteRef = null; this._autocompleteRef = null;
this._formatBarRef = null;
this._modifiedFlag = false; this._modifiedFlag = false;
this._isIMEComposing = false; this._isIMEComposing = false;
this._hasTextSelected = false;
} }
_replaceEmoticon = (caretPosition, inputType, diff) => { _replaceEmoticon = (caretPosition, inputType, diff) => {
@ -239,6 +241,36 @@ export default class BasicMessageEditor extends React.Component {
_onSelectionChange = () => { _onSelectionChange = () => {
this._refreshLastCaretIfNeeded(); this._refreshLastCaretIfNeeded();
const selection = document.getSelection();
if (this._hasTextSelected && selection.isCollapsed) {
this._hasTextSelected = false;
if (this._formatBarRef) {
this._formatBarRef.classList.remove("mx_BasicMessageComposer_formatBar_shown");
}
} else if (!selection.isCollapsed) {
this._hasTextSelected = true;
if (this._formatBarRef) {
this._formatBarRef.classList.add("mx_BasicMessageComposer_formatBar_shown");
const selectionRect = selection.getRangeAt(0).getBoundingClientRect();
let leftOffset = 0;
let node = this._formatBarRef;
while (node.offsetParent) {
node = node.offsetParent;
leftOffset += node.offsetLeft;
}
let topOffset = 0;
node = this._formatBarRef;
while (node.offsetParent) {
node = node.offsetParent;
topOffset += node.offsetTop;
}
this._formatBarRef.style.left = `${selectionRect.left - leftOffset}px`;
this._formatBarRef.style.top = `${selectionRect.top - topOffset - 16 - 12}px`;
}
}
} }
_onKeyDown = (event) => { _onKeyDown = (event) => {
@ -392,6 +424,25 @@ export default class BasicMessageEditor extends React.Component {
return caretPosition; return caretPosition;
} }
_formatBold = () => {
}
_formatItalic = () => {
}
_formatStrikethrough = () => {
}
_formatQuote = () => {
}
_formatCodeBlock = () => {
}
render() { render() {
let autoComplete; let autoComplete;
if (this.state.autoComplete) { if (this.state.autoComplete) {
@ -413,6 +464,13 @@ export default class BasicMessageEditor extends React.Component {
}); });
return (<div className={classes}> return (<div className={classes}>
{ autoComplete } { autoComplete }
<div className="mx_BasicMessageComposer_formatBar" ref={ref => this._formatBarRef = ref}>
<span onClick={this._formatBold} className="mx_BasicMessageComposer_formatButton mx_BasicMessageComposer_formatBold"></span>
<span onClick={this._formatItalic} className="mx_BasicMessageComposer_formatButton mx_BasicMessageComposer_formatItalic"></span>
<span onClick={this._formatStrikethrough} className="mx_BasicMessageComposer_formatButton mx_BasicMessageComposer_formatStrikethrough"></span>
<span onClick={this._formatCode} className="mx_BasicMessageComposer_formatButton mx_BasicMessageComposer_formatCode"></span>
<span onClick={this._formatQuote} className="mx_BasicMessageComposer_formatButton mx_BasicMessageComposer_formatQuote"></span>
</div>
<div <div
className="mx_BasicMessageComposer_input" className="mx_BasicMessageComposer_input"
contentEditable="true" contentEditable="true"