Refactoring spell&gramma windows

This commit is contained in:
Oleg Korshul
2025-11-18 17:39:15 +03:00
parent 40217a4eb5
commit 86d3e6813c
6 changed files with 117 additions and 288 deletions

View File

@ -36,268 +36,17 @@
<link rel="stylesheet" href="https://onlyoffice.github.io/sdkjs-plugins/v1/plugins.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grammar Suggestion</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: white;
padding: 0;
margin: 0;
color:rgb(61,61,61)
}
.popup {
background: white;
border-radius: 4px;
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.2);
border: 1px solid #d0d0d0;
overflow: hidden;
max-width: 320px;
min-width: 280px;
}
.popup-header {
background: rgb(241,241,241);
color: #333;
padding: 10px 12px;
font-size: 12px;
font-weight: bold;
display: flex;
text-align: center;
letter-spacing: 0.01em;
align-items: center;
border-bottom: 1px solid #d0d0d0;
position: relative;
justify-content: center;
align-items: center;
}
.close-icon {
cursor: pointer;
color: #666;
font-size: 18px;
line-height: 1;
padding: 2px 4px;
transition: color 0.2s;
position: absolute;
right: 8px;
}
.close-icon:hover {
color: #000;
}
.popup-body {
padding: 16px 16px 0 16px;
}
.section {
margin-bottom: 12px;
}
.section:last-of-type {
margin-bottom: 16px;
}
.label {
font-size: 11px;
font-weight: 700;
color: #333;
margin-bottom: 6px;
}
.correction {
font-size: 12px;
color: #000;
line-height: 1.5;
background: #f5f5f5;
border: 1px solid #d0d0d0;
border-radius: 3px;
padding: 10px;
}
.correction-text {
display: flex;
align-items: center;
gap: 8px;
}
.original {
color: #000;
font-weight: normal;
}
.arrow {
color: #666;
font-weight: bold;
}
.suggested {
color: #000;
font-weight: normal;
}
.suggested strong {
color: #F62211;
font-weight: bold;
}
.explanation {
font-size: 12px;
color: #000;
line-height: 1.5;
background: #f5f5f5;
border: 1px solid #d0d0d0;
border-radius: 3px;
padding: 10px;
}
.button-container {
display: flex;
gap: 8px;
padding: 16px;
border-top: 1px solid #d0d0d0;
margin: 0 -16px;
}
.btn {
flex: 1;
padding: 0px 0px;
border: 1px solid #d0d0d0;
border-radius: 2px;
font-size: 11px;
letter-spacing: 0.01em;
line-height: var(--line-height-base, 1.5);
font-weight: bold;
vertical-align: middle;
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
background: white;
color: #333;
display: inline-block;
width: 86px;
height: 22px;
}
.btn-accept {
background: #2c2c2c;
color: white;
border-color: #2c2c2c;
}
.btn-accept:hover {
background: #1a1a1a;
border-color: #1a1a1a;
}
.btn-reject {
background: white;
color: #333;
}
.btn-reject:hover {
background: #f5f5f5;
}
.btn:active {
transform: scale(0.98);
}
</style>
<title>Annotation popup</title>
</head>
<body>
<div class="popup" id="popup">
<div class="popup-header">
<span id="title">Grammar suggestion</span>
<span class="close-icon" id="closeIcon">×</span>
</div>
<div class="popup-body">
<div class="section">
<div class="label">Suggested correction</div>
<div class="correction">
<div class="correction-text">
<span class="original" id="original"></span>
<span class="arrow"></span>
<span class="suggested" id="suggested"></span>
</div>
</div>
</div>
<div class="section" id="explanation-section">
<div class="label">Explanation</div>
<div class="explanation" id="explanation"></div>
</div>
<div class="button-container">
<button class="btn btn-accept" id="acceptBtn">Accept</button>
<button class="btn btn-reject" id="rejectBtn">Reject</button>
</div>
</div>
</div>
<body style="margin:0; padding:0;">
<script>
let errorData = {
"title" : "Grammar suggestion",
"original": "",
"suggested": "",
"explanation": ""
};
function displayError(error)
{
document.getElementById('title').textContent = error.title;
document.getElementById('original').textContent = error.original;
document.getElementById('suggested').innerHTML = error.suggested;
if (!error.explanation) {
document.getElementById('explanation-section').style.display = "none";
} else {
document.getElementById('explanation-section').style.display = "block";
document.getElementById('explanation').textContent = error.explanation;
}
errorData = error;
}
displayError(errorData);
window.updateError = function (newError)
{
displayError(newError);
};
document.getElementById('acceptBtn').addEventListener('click', function ()
{
window.Asc.plugin.sendToPlugin("onAccept");
});
document.getElementById('rejectBtn').addEventListener('click', function ()
{
window.Asc.plugin.sendToPlugin("onReject");
});
window.Asc.plugin.init = function() {
window.Asc.plugin.sendToPlugin("onWindowReady", {});
window.Asc.plugin.attachEvent("onUpdateSuggestion", function(obj) {
displayError(obj);
window.Asc.plugin.sendToPlugin("onUpdateSize", [document.body.scrollWidth, document.body.scrollHeight]);
window.Asc.plugin.attachEvent("onUpdateContent", function(content) {
document.body.innerHTML = content;
});
};
window.Asc.plugin.attachEvent("onThemeChanged", function(){
});
document.getElementById('closeIcon').addEventListener('click', function ()
{
window.Asc.plugin.sendToPlugin("onClose");
});
</script>
</body>
</html>

View File

@ -691,7 +691,7 @@ window.Asc.plugin.onTranslate = async function() {
await initWithTranslate(1);
};
window.Asc.plugin.button = function(id, windowId) {
window.Asc.plugin.button = async function(id, windowId) {
if (!windowId) {
return
}
@ -702,6 +702,22 @@ window.Asc.plugin.button = function(id, windowId) {
delete window.chatWindow;
}
if (textAnnotatorPopup && textAnnotatorPopup.popup && textAnnotatorPopup.popup.id === windowId)
{
switch (id) {
case 0:
await textAnnotatorPopup.popup.onAccept();
break;
case 1:
await textAnnotatorPopup.popup.onReject();
break;
default:
textAnnotatorPopup.close();
break;
}
return;
}
if (settingsWindow && windowId === settingsWindow.id) {
settingsWindow.close();
settingsWindow = null;

View File

@ -36,15 +36,20 @@ function TextAnnotationPopup()
this.type = 0; // 0 - spelling, 1 - grammar
this.paraId = -1;
this.rangeId = -1;
this.content = "";
this.width = 318;
this.height = 500;
this.open = function(type, paraId, rangeId)
this.open = function(type, paraId, rangeId, data)
{
if (this.popup && 0 === this.type && 1 === type)
return null;
this._calculateWindowSize(data);
return this._open(type, paraId, rangeId);
};
this._open = function(type, paraId, rangeId)
{
if (this.type === type
@ -62,24 +67,21 @@ function TextAnnotationPopup()
let variation = {
url : 'annotationPopup.html',
isVisual : true,
buttons : [],
buttons : this._getButtons(),
isModal : false,
isCustomWindow : true,
description: this._getTitle(),
EditorsSupport : ["word", "slide", "cell", "pdf"],
size : [318, 500],
isTargeted : true,
transparent : true
size : [this.width, this.height],
fixedSize : true,
isTargeted : true
};
let _t = this;
let popup = new window.Asc.PluginWindow();
popup.attachEvent("onClose", function() {
_t.close();
});
popup.attachEvent("onUpdateSize", function(size) {
if (size[0] !== variation.size[0] || size[1] !== variation.size[1]) {
Asc.Editor.callMethod("ResizeWindow", [popup.id, [size[0], size[1]]]);
}
let _t = this;
popup.attachEvent("onWindowReady", function() {
popup.command("onUpdateContent", _t.content);
});
popup.show(variation);
this.popup = popup;
return popup;
@ -100,7 +102,74 @@ function TextAnnotationPopup()
this.popup.close();
this.popup = null;
Asc.Editor.callMethod("FocusEditor");
}
};
this._getTitle = function()
{
return window.Asc.plugin.tr(this.type === 0 ? "Spelling suggestion" : "Grammar suggestion");
};
this._getButtons = function()
{
return [
{ text: window.Asc.plugin.tr('Accept'), primary: true },
{ text: window.Asc.plugin.tr('Reject'), primary: false }
];
};
this._calculateWindowSize = function(data)
{
let backColor = window.Asc.plugin.theme ? window.Asc.plugin.theme["background-normal"] : "#FFFFFF";
let textColor = window.Asc.plugin.theme ? window.Asc.plugin.theme["text-normal"] : "#3D3D3D";
let borderColor = window.Asc.plugin.theme ? window.Asc.plugin.theme["border-divider"] : "#666666";
let ballonColor = window.Asc.plugin.theme ? window.Asc.plugin.theme["canvas-background"] : "#F5F5F5";
this.content = `<div style="background:${backColor}; overflow:hidden; max-width:320px; min-width:280px;color:${textColor}; user-select:none;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;">
<div style="padding:16px 16px 0px 16px;">
<div style="margin-bottom:12px;">
<div style="font-size:11px; font-weight:700; color:${textColor}; margin-bottom:6px;">
${window.Asc.plugin.tr("Suggested correction")}
</div>
<div style="font-size:12px; color:${textColor}; line-height:1.5; background:${ballonColor}; border:1px solid ${borderColor}; border-radius:3px; padding:10px;">
<div style="display:flex; align-items:center; gap:8px;">
<span style="color:${textColor}; font-weight:normal;">${data.original}</span>
<span style="color:${borderColor}; font-weight:bold;">→</span>
<span style="color:${textColor}; font-weight:normal;">${data.suggested}</span>
</div>
</div>
</div>`;
if (data.explanation) {
this.content += `<div style="margin-bottom:16px;">
<div style="font-size:11px; font-weight:700; color:${textColor}; margin-bottom:6px;">
${window.Asc.plugin.tr("Explanation")}
</div>
<div style="font-size:12px; color:${textColor}; line-height:1.5; background:${ballonColor}; border:1px solid ${borderColor}; border-radius:3px; padding:10px;">${data.explanation}</div>
</div>`;
}
this.content += "</div></div>";
let measureDiv = document.createElement("div");
measureDiv.style.position = "absolute";
measureDiv.style.left = "-9999px";
measureDiv.style.top = "-9999px";
measureDiv.style.width = this.width + "px";
measureDiv.style.visibility = "hidden";
measureDiv.style.pointerEvents = "none";
measureDiv.style.opacity = "0";
measureDiv.style.margin = "0";
measureDiv.style.padding = "0";
measureDiv.innerHTML = this.content;
document.body.appendChild(measureDiv);
this.height = measureDiv.scrollHeight;
document.body.removeChild(measureDiv);
};
}
var textAnnotatorPopup = new TextAnnotationPopup();

View File

@ -219,10 +219,9 @@ GrammarChecker.prototype.getInfoForPopup = function(paraId, rangeId)
{
let _s = this.getAnnotation(paraId, rangeId);
return {
"title" : "Grammar suggestion",
"suggested" : _s["difference"],
"original" : _s["original"],
"explanation" : _s["description"]
suggested : _s["difference"],
original : _s["original"],
explanation : _s["description"]
};
};
GrammarChecker.prototype.onAccept = async function(paraId, rangeId)

View File

@ -210,9 +210,8 @@ SpellChecker.prototype.getInfoForPopup = function(paraId, rangeId)
{
let anot = this.getAnnotation(paraId, rangeId);
return {
"title" : "Spelling suggestion",
"suggested" : anot["suggested"],
"original" : anot["original"]
suggested : anot["suggested"],
original : anot["original"]
};
};
SpellChecker.prototype.onAccept = async function(paraId, rangeId)

View File

@ -91,22 +91,19 @@ TextAnnotator.prototype.openPopup = async function(paraId, rangeId)
if (!textAnnotatorPopup)
return;
let popup = textAnnotatorPopup.open(this.type, paraId, rangeId);
let popup = textAnnotatorPopup.open(this.type, paraId, rangeId, this.getInfoForPopup(paraId, rangeId));
if (!popup)
return;
let _t = this;
popup.attachEvent("onWindowReady", function() {
popup.command("onUpdateSuggestion", _t.getInfoForPopup(paraId, rangeId));
});
popup.attachEvent("onAccept", async function() {
popup.onAccept = async function() {
await _t.onAccept(paraId, rangeId);
_t.closePopup();
});
popup.attachEvent("onReject", async function() {
};
popup.onReject = async function() {
await _t.onReject(paraId, rangeId);
_t.closePopup();
});
};
};
TextAnnotator.prototype.closePopup = function()
{