Managing Threaded Comments
Threaded comments provide a series APIs. You can create, manage, and organize threaded comments and replies programmatically.
Creating a Threaded Comment
To create a threaded comment on a cell, use the add method on the sheet's threadedComments manager. Then add the first reply to the thread:
Getting and Removing Threaded Comments
You can get a threaded comment using the get method and remove it using the remove method:
Managing Resolved Status
You can mark a threaded comment as resolved or unresolved using the resolved method:
Adding Replies
Add replies to a threaded comment using the add method. Each reply can contain text, mentions, and links:
Updating a Reply
Update an existing reply using the set method:
Removing a Reply
Remove a specific reply using the remove method:
User Management
Configuring User Manager
Before using threaded comments, configure the UserManager with user lookup and search functions:
Setting Current User
Set the current user who will author new comments and replies:
window.onload = function () {
var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"));
initSpread(spread);
};
// User IDs in GUID format
var USER_IDS = {
ALICE: "{715CFD19-2BB9-4B94-DE0C-08D0F9A019DB}",
BOB: "{8A3E2F47-5C1D-4E8B-A9F2-3B7C6D8E9F01}",
CAROL: "{C4D5E6F7-8A9B-0C1D-2E3F-4A5B6C7D8E9F}",
DAVID: "{D1E2F3A4-B5C6-7D8E-9F0A-1B2C3D4E5F67}"
};
var activeThreadedComment = null;
var activeReplyIndex = null;
function initSpread(spread) {
// Configure UserManager first
configureUserManager();
spread.suspendPaint();
var sheet = spread.getActiveSheet();
sheet.options.allowCellOverflow = true;
// Create a sample data table
createSampleTable(sheet);
// Add threaded comments to demonstrate various APIs
addThreadedComments(sheet);
spread.resumePaint();
// Bind selection change event
var spreadNS = GC.Spread.Sheets;
spread.bind(spreadNS.Events.SelectionChanged, function (e, info) {
var sheetTmp = info.sheet;
var row = sheetTmp.getActiveRowIndex();
var col = sheetTmp.getActiveColumnIndex();
var threadedComment = sheetTmp.threadedComments.get(row, col);
if (threadedComment) {
_getElementById("commentTip").innerHTML = "Threaded Comment in Cell [ " + row + " : " + col + " ]";
activeThreadedComment = threadedComment;
activeReplyIndex = null;
loadReplies(); // Automatically load replies
} else {
_getElementById("commentTip").innerHTML = "No Threaded Comment";
activeThreadedComment = null;
activeReplyIndex = null;
clearReplies(); // Clear replies list
}
updateLabels(spread);
});
// Bind button events
bindButtonEvents(spread);
}
function configureUserManager() {
// Mock user database with GUID-style IDs and avatars
var users = [
{
id: USER_IDS.ALICE,
name: "Alice Johnson",
email: "alice.johnson@company.com",
avatar: {
kind: "data",
dataUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAJ/0lEQVR42u2beVBURx7HN8Yym9JU7W7VJtbW5o+NuhqT0ogYEW88cDAcCnIoEg4VDwSFgQFEjnhxK+fIpSiHAg7XcA6nMAOogNyXQBBBREEEYe6Z33Y/9aGORXQph0nCH996r997PdOf9zv61z3wFwD4U2kaeBp4GngaeBp4GhhpZHjoH51tzUvLi/N0UuMvH2fERNgzYiKxqMW5TKPm+rsqTweezJVKpZ/8boH5PO7susqKjeF+py/Y7NWpMtry44D2qm9FGsrzQUN5HuAjlqbKIsmujcuGDxtqNJz3oF1mF2TrDQ89/efvBlgsFs2srmBv9bC1TN+1YdkIZfk8AnD7igXw04//fafwPQxPWf4N6KguFtia6lWw0pPMeVzubIUGHno68GXkhXO++mpKQwiUhPxQaaC+2gjcy9nmeldH2/cKCdzV3va9i5UpC0FKSdBJaQHgl2a5S735VmmhpkIBYyvYmulV4AFOHlTW2ns1VveW38zXVgjggf6+fzsc2F1Cwn4E4RxgprWhC2fzKQUWCgWfhXq5h6CEI/0IoDKWdthvxH468HjulAFXlBRo6a5f+pyM2Y8s9D2S2LAADzRfz5A7MHdsbI6rtUU26cryAEZTlzly7QddHQvlDlxzu2yT7rqlo+SA5ActTYwOc5IrMC4BLwV6e+NkIm9gHMtOh4yLeNyxOXID5o6NzrHfZ8SWjzvLVmV71FUe46lQbsC93b/O/3n72h6UneUOjIVrck5h7k65Abc11Snrb1Qa/pjZWUf1W/C0MQRDtR9kSlTczmLEH/zDAFOQ55yw0IQnVQkQ7m75hwLGg8fWJNs4TA7qrIVm1kUQd2RDT1kMHNFdj64vmDrgey0NSgZqy4cmA6yB+mqpLIJz1vpw6rAuBiJgrfQ2QG1GEIjaM0HQloGOWcAMc8HPvt5fmpOSsF9uwI8fPfzaQketc+KkNfHa13y7CiQHOcDTmhsI2gBfJ8A7iy+RsFjCe5kwUJ0IVGN1ePV9O1Z/J6gsK9kmz2npC3sLQ84HTEukBfdsVoIgZzNoL4wkwEbqk8GPagxJAfbwrPYGCfu6xB1ZkBJMI18Y3j3pbGtZItfCI8TTjY52KCayJGlN7VWL4ID2GghzOwBNuXTgtTBJMG5zGjyuvA4CZEnhuGSAM8NcXlZa8+CQAaVpaHDgK7mWlnlMhpnmyoXiN1wVQxIutxiMNi2DvRRVOH3UAAqvnoLe8lgyJkkY0m1fwafDwzuJMFDDIK7xWtLJ+6EuFkBZPh8oSt8A2glJkIjFM+UKfL+j7TvjbaqPMOgO1cXEgFJDHCHW5zi4WxlCoOsh8HKngb8HFR4gWDEJ+m5hi7NiPCHQyw0CTjtAQZwXZESdIrxhCMW57e4thLdsR0qJu2Qr98WDUCic5elknfgqjtPpzgBdufDoznVwPGQIlA0rQVV5KTqqQH1uGIgmBiZeSKSnLdFntfIS2Km+DqK87Qjr1jADQX/9EgJ299aVT9pbGpWmZD1cmp+1C2+2YVejmWqgpMMg3LIu+yIkBJ0AH6cDkHzRDUYbU8fhWpnARYmK25Ai49bdZbEQ8stRCPKwgqzoM6gdg++h2N9PJD2cMzyRO+ONhykBfj4y/HcXKzMWHgiO28KYMzjBYGviI4aQybp8FJe8xhTgIqHzt6HJPrg/VntRFJhprCSqLwM15SG8LJ3SPS08AMPNKwYpSvPAbs9W6L9zjUxCk9UYyuAXaCYYFhcewvjIYFcxSlZTCiyRiD9NvHzRGbk2H8cY/eQ+GGtKezc0usZvZZLnglZ8lAElrZ0ZfpIoO9Fn86ICvXzQGni2QmzTCgT8v+LfjPaor+rTWrkQYj2PIujUV9Ak4LPSK9CCYrQLxfX9cHdoO2MDwzhOx58j+xRfPQ3Gm5Uklnrqjay0JHMBn/+5Qm3E42KktaF2hY8HLU6foioMQdPUQwQjwhBYKK570VTD0VKDMp3NUKa9Ccp3boH+1EB8j3hGhKz9rI4BKWGuYGG6QxwQ7BPb19s9T2F/aunncb+Nqq/KOsaIFR3wPwUeoWeghI0KiRZsNVRIoNKx/dxxqDTWhCpTHbgf6gKCxlQCdhg9U1eHCozsOLCJiwDb3FTpmTvsblZ3xy98sXiOQgIX9fzqeLwkF+w5+UArKwBqaR7QSnPBp5wFSZU5wKnLgYa7TGgqioGW0nhoqc+E23XZkFadA0G3WHCCkwdUdj44oL4O6DPsSlngWl70rG90ZIlCAj/mji1M6WgO9q0ua6Sx88UO7DzAskeivjx35uQhiHxwQ8KANOIeeZ+Uc1kBL7j2Nqekt8tWoKgWxnrwfFg5oOZWJRq0ZBxAVvb4OIGcOPn8pHuNkVyR6G8KG8Mo1r4Ib6jKo5ayyIFPUhIUJjRFBp4T2VCdQy2dLCjpBVKFBsZ6ODqy9Dw7r9HhZjZKXgVIKAlhsSfSOCTt5dGuNFd6rbU++rlQ8KVCAwt53NnX/R1KfM8egbNXL4BbWgy45DDAqSAdnAqZL1TABOf88bZjYQbY3swB6+JsOFiQASa5KWCUlSS+1dezS+H/xqP//r0f6DaaA+GHt8Ilm58g0m4nhDkaoZLTBOiuPyOZQpCzMQQ57cFtQiHu5nAwLgT0s2/AbiTzvFSwKsoCRnsTXQrSGQoNXFucZulvsV4abkUhgAlZbyd1GbX9TNdhEefEtaMU8Ak8Aa+SHY2IXxZ4VrI7Bvnc/ygssFQi+TQn6my0n9laiDhKAMvo8jFN8DVdj0WcE9cQdKjHPnAqypTJ0sU9XVSFBR4dHpwb7WLS5G++DgFrTAxsRgITVg5zMoITuQwiwZFZGlnct6qsaZCHrKyIwD1ttWuDDqs/P79vA0RODIz0JnC4gz64ZCWQwK9DowIkSiiRfK5wwHdyrtn7ma+VXti/ESKt3xsY6U1g2aqrgJ/f3ekikkg+I0IHYAYucsRSyawpAxaLRbNSA53SfE3XAAn8AS4dQTOAk9mJ7wS2f1Fbc1Pam0Nwvf5MwP/Xpca7mUjptx/17EO19my5A48M9n8d5WjUgSxMAEchiPe1MFaEgx6czLwmA/yWe+PM3XmlqSYeTVsjtmhl5sjJF6AXEYoXGHIF7mq4vTXg4BYempL+P2B7XTiZET8hMBaeruwQ6N6cZDj8IqtjaGFFX48lAHwiN+BKFL94OnpvYLO3gKm64MqM+y1gcp4+UpSFKrJk3Cbmb2T1VIlUOlMewHgjb2ZGqGuSt8kqwDFMTEtHtkGkFUVGUSh7e+9dA94mq/E5eT3imBa4MqLB7gXAbwq7M67KbHDNjtrMzlZ/nMzkAywSzqrMuU7Nu+JLz4/xoxfE+tM5CcH0ssSQdyiUnnfFDwufk9c5SXR6RjWHzuhooSe3N7+XkFXpiW0NoZyH3VZokfHV9L8ATAP/yYD/BxmCzyIgvhWGAAAAAElFTkSuQmCC"
}
},
{
id: USER_IDS.BOB,
name: "Bob Smith",
email: "bob.smith@company.com",
avatar: {
kind: "data",
dataUrl: "data:image/png;base64,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"
}
},
{
id: USER_IDS.CAROL,
name: "Carol Manager",
email: "carol.manager@company.com",
avatar: {
kind: "data",
dataUrl: "data:image/png;base64,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"
}
},
{
id: USER_IDS.DAVID,
name: "David Accountant",
email: "david.accountant@company.com",
avatar: {
kind: "data",
dataUrl: "data:image/png;base64,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"
}
}
];
GC.Spread.Common.UserManager.configure({
get: async (userId) => {
if (userId === undefined) {
return;
}
return new Promise((resolve) => {
const user = users.find(u => u.id === userId);
resolve(user);
});
},
search: async (query) => {
return new Promise((resolve) => {
resolve(users.filter(u =>
u.name.toLowerCase().includes(query.toLowerCase()) ||
u.email.toLowerCase().includes(query.toLowerCase())
));
});
}
});
// Set current user
GC.Spread.Common.UserManager.current(USER_IDS.ALICE);
}
function addThreadedComments(sheet) {
var threadedCommentManager = sheet.threadedComments;
// Example 1: Simple threaded comment with one reply on Reserve
var tc1 = threadedCommentManager.add(9, 2);
tc1.add({
message: [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: "Please verify if 10% reserve is sufficient for this quarter." }
],
authorId: USER_IDS.ALICE,
createdAt: new Date()
});
// Example 2: Threaded comment with multiple replies on Total Budget
var tc2 = threadedCommentManager.add(10, 2);
tc2.add({
message: [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: "The total budget looks reasonable." }
],
authorId: USER_IDS.ALICE,
createdAt: new Date(Date.now() - 86400000) // 1 day ago
});
tc2.add({
message: [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: "Confirmed. Ready for approval." }
],
authorId: USER_IDS.BOB,
createdAt: new Date(Date.now() - 43200000) // 12 hours ago
});
tc2.add({
message: [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: "Approved!" }
],
authorId: USER_IDS.CAROL,
createdAt: new Date()
});
// Example 3: Resolved threaded comment on Operations
var tc3 = threadedCommentManager.add(4, 2);
tc3.add({
message: [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: "Should we increase the operations budget?" }
],
authorId: USER_IDS.BOB,
createdAt: new Date(Date.now() - 172800000) // 2 days ago
});
tc3.add({
message: [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: "No, this amount aligns with our Q3 plans." }
],
authorId: USER_IDS.ALICE,
createdAt: new Date(Date.now() - 86400000) // 1 day ago
});
// Mark this thread as resolved
tc3.resolved(true);
// Example 4: Comment with mention and link on Development variance
var tc4 = threadedCommentManager.add(3, 4);
tc4.add({
message: [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: "Hey " },
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.mention, userId: USER_IDS.DAVID },
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: ", please review the budget variance in Development department: " },
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.link, href: "https://example.com/budget-policy", text: "Budget Policy" }
],
authorId: USER_IDS.ALICE,
createdAt: new Date()
});
}
function createSampleTable(sheet) {
// Set column widths
sheet.setColumnWidth(1, 150);
sheet.setColumnWidth(2, 120);
sheet.setColumnWidth(3, 100);
sheet.setColumnWidth(4, 120);
// Headers
sheet.setValue(1, 1, "Department");
sheet.setValue(1, 2, "Q3 Budget");
sheet.setValue(1, 3, "Q3 Actual");
sheet.setValue(1, 4, "Variance");
sheet.getRange(1, 1, 1, 4).font("bold 12pt Arial");
// Department budget data
sheet.setValue(2, 1, "Marketing");
sheet.setValue(2, 2, 15000);
sheet.setValue(2, 3, 14200);
sheet.getCell(2, 4).formula("=C3-D3");
sheet.setValue(3, 1, "Development");
sheet.setValue(3, 2, 25000);
sheet.setValue(3, 3, 26800);
sheet.getCell(3, 4).formula("=C4-D4");
sheet.setValue(4, 1, "Operations");
sheet.setValue(4, 2, 10000);
sheet.setValue(4, 3, 9850);
sheet.getCell(4, 4).formula("=C5-D5");
sheet.setValue(5, 1, "Sales");
sheet.setValue(5, 2, 18000);
sheet.setValue(5, 3, 17500);
sheet.getCell(5, 4).formula("=C6-D6");
sheet.setValue(6, 1, "HR");
sheet.setValue(6, 2, 8000);
sheet.setValue(6, 3, 8200);
sheet.getCell(6, 4).formula("=C7-D7");
sheet.setValue(7, 1, "IT Support");
sheet.setValue(7, 2, 12000);
sheet.setValue(7, 3, 11600);
sheet.getCell(7, 4).formula("=C8-D8");
// Subtotal row
sheet.setValue(8, 1, "Subtotal");
sheet.getCell(8, 2).formula("=SUM(C3:C8)");
sheet.getCell(8, 3).formula("=SUM(D3:D8)");
sheet.getCell(8, 4).formula("=SUM(E3:E8)");
sheet.getRange(8, 1, 1, 4).font("bold 11pt Arial");
// Reserve calculation
sheet.setValue(9, 1, "Reserve (10%)");
sheet.getCell(9, 2).formula("=C8*0.1");
sheet.setValue(9, 3, "");
sheet.setValue(9, 4, "");
sheet.getCell(9, 1).font("bold 11pt Arial");
sheet.getCell(9, 2).font("bold 11pt Arial");
sheet.getCell(9, 2).backColor("#FFEB9C");
sheet.setValue(10, 1, "Total Budget");
sheet.getCell(10, 2).formula("=C8+C9");
sheet.getCell(10, 3).formula("=D8");
sheet.getCell(10, 4).formula("=C11-D11");
sheet.getRange(10, 1, 1, 4).font("bold 12pt Arial");
sheet.getRange(10, 2, 1, 3).backColor("#C6EFCE");
sheet.getRange(2, 2, 9, 3).formatter("$#,##0.00;[Red]-$#,##0.00");
}
async function loadReplies() {
if (activeThreadedComment) {
var replies = activeThreadedComment.all();
var repliesList = _getElementById("repliesList");
repliesList.innerHTML = "";
if (replies && replies.length > 0) {
for (var index = 0; index < replies.length; index++) {
var reply = replies[index];
var option = document.createElement("option");
option.value = index;
var message = "";
if (reply.message && reply.message.length > 0) {
var messageParts = [];
for (var i = 0; i < reply.message.length; i++) {
var part = reply.message[i];
if (part.type === GC.Spread.Sheets.ThreadedComments.ContentType.text) {
messageParts.push(part.value);
} else if (part.type === GC.Spread.Sheets.ThreadedComments.ContentType.mention) {
var user = await GC.Spread.Common.UserManager.get(part.userId);
messageParts.push("@" + (user ? user.name : part.userId));
} else if (part.type === GC.Spread.Sheets.ThreadedComments.ContentType.link) {
messageParts.push(part.text || part.href);
}
}
message = messageParts.join("");
}
option.text = "Reply " + (index + 1) + ": " + message.substring(0, 30) + (message.length > 30 ? "..." : "");
repliesList.appendChild(option);
}
} else {
var option = document.createElement("option");
option.text = "No replies";
repliesList.appendChild(option);
}
}
}
function clearReplies() {
var repliesList = _getElementById("repliesList");
repliesList.innerHTML = "";
var option = document.createElement("option");
option.text = "No replies loaded";
repliesList.appendChild(option);
}
function bindButtonEvents(spread) {
// Select reply
_getElementById("repliesList").addEventListener('change', function () {
if (activeThreadedComment && this.value !== "") {
activeReplyIndex = parseInt(this.value);
updateLabels(spread);
}
});
// Add new reply
_getElementById("btnAddReply").addEventListener('click', function () {
if (activeThreadedComment) {
var message = _getElementById("txtNewReply").value;
if (message) {
activeThreadedComment.add({
message: [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: message }
],
authorId: GC.Spread.Common.UserManager.current(),
createdAt: new Date()
});
_getElementById("txtNewReply").value = "";
loadReplies();
updateLabels(spread);
}
}
});
// Delete reply
_getElementById("btnDeleteReply").addEventListener('click', function () {
if (activeThreadedComment && activeReplyIndex !== null) {
var replies = activeThreadedComment.all();
var replyToDelete = replies[activeReplyIndex];
if (replyToDelete) {
activeThreadedComment.remove(activeReplyIndex);
activeReplyIndex = null;
loadReplies();
updateLabels(spread);
}
}
});
// Toggle resolved status
_getElementById("btnToggleResolved").addEventListener('click', function () {
if (activeThreadedComment) {
var currentStatus = activeThreadedComment.resolved();
activeThreadedComment.resolved(!currentStatus);
updateLabels(spread);
}
});
// Delete entire thread
_getElementById("btnDeleteThread").addEventListener('click', function () {
if (activeThreadedComment) {
var sheet = spread.getActiveSheet();
var row = sheet.getActiveRowIndex();
var col = sheet.getActiveColumnIndex();
sheet.threadedComments.remove(row, col);
activeThreadedComment = null;
activeReplyIndex = null;
updateLabels(spread);
clearReplies();
}
});
// Add new thread
_getElementById("btnAddThread").addEventListener('click', function () {
var sheet = spread.getActiveSheet();
var row = sheet.getActiveRowIndex();
var col = sheet.getActiveColumnIndex();
var message = _getElementById("txtNewThread").value;
if (message) {
var existingComment = sheet.threadedComments.get(row, col);
if (existingComment) {
sheet.threadedComments.remove(row, col);
}
var tc = sheet.threadedComments.add(row, col);
tc.add({
message: [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: message }
],
authorId: GC.Spread.Common.UserManager.current(),
createdAt: new Date()
});
_getElementById("txtNewThread").value = "";
activeThreadedComment = tc;
updateLabels(spread);
}
});
// Update reply message
_getElementById("btnUpdateReply").addEventListener('click', function () {
if (activeThreadedComment && activeReplyIndex !== null) {
var newMessage = _getElementById("txtUpdateMessage").value;
if (newMessage) {
var replies = activeThreadedComment.all();
var replyToUpdate = replies[activeReplyIndex];
if (replyToUpdate) {
replyToUpdate.message = [
{ type: GC.Spread.Sheets.ThreadedComments.ContentType.text, value: newMessage }
];
replyToUpdate.modifiedAt = new Date();
activeThreadedComment.set(activeReplyIndex, replyToUpdate);
_getElementById("txtUpdateMessage").value = "";
loadReplies();
updateLabels(spread);
}
}
}
});
// Change current user
_getElementById("comboCurrentUser").addEventListener('change', function () {
var userId = this.value;
GC.Spread.Common.UserManager.current(userId);
});
}
async function updateLabels(spread) {
// Clear all fields first
_getElementById("lblThreadLocation").innerHTML = "-";
_getElementById("lblThreadResolved").innerHTML = "-";
_getElementById("lblThreadRepliesCount").innerHTML = "-";
_getElementById("lblReplyAuthor").innerHTML = "-";
_getElementById("lblReplyCreatedAt").innerHTML = "-";
_getElementById("lblReplyMessage").innerHTML = "-";
if (activeThreadedComment) {
_getElementById("lblThreadLocation").innerHTML = "Row: " + activeThreadedComment.row() + ", Col: " + activeThreadedComment.col();
_getElementById("lblThreadResolved").innerHTML = activeThreadedComment.resolved() ? "Yes" : "No";
var replies = activeThreadedComment.all();
_getElementById("lblThreadRepliesCount").innerHTML = replies.length.toString();
if (activeReplyIndex !== null) {
var activeReply = replies[activeReplyIndex];
if (activeReply) {
// Get author name from UserManager
var authorUser = await GC.Spread.Common.UserManager.get(activeReply.authorId);
_getElementById("lblReplyAuthor").innerHTML = authorUser ? authorUser.name : (activeReply.authorId || "-");
_getElementById("lblReplyCreatedAt").innerHTML = activeReply.createdAt ?
activeReply.createdAt.toLocaleString() : "-";
if (activeReply.message && activeReply.message.length > 0) {
var messageParts = [];
for (var i = 0; i < activeReply.message.length; i++) {
var part = activeReply.message[i];
if (part.type === GC.Spread.Sheets.ThreadedComments.ContentType.text) {
messageParts.push(part.value);
} else if (part.type === GC.Spread.Sheets.ThreadedComments.ContentType.mention) {
var user = await GC.Spread.Common.UserManager.get(part.userId);
messageParts.push("@" + (user ? user.name : part.userId));
} else if (part.type === GC.Spread.Sheets.ThreadedComments.ContentType.link) {
messageParts.push('<a href="' + part.href + '" target="_blank">' + (part.text || part.href) + '</a>');
}
}
_getElementById("lblReplyMessage").innerHTML = messageParts.join("");
}
}
}
}
spread.refresh();
}
function _getElementById(id) {
return document.getElementById(id);
}
<!doctype html>
<html style="height:100%;font-size:14px;">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script>
<script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div class="tc-sample-tutorial">
<div id="ss" class="tc-sample-spreadsheets"></div>
<div class="tc-options-container">
<div class="tc-option-row">
<h4 class="tc-title">Select a cell with threaded comment:
<span id="commentTip" class="tc-comment-tip">No Threaded Comment</span>
</h4>
</div>
<!-- Current User Selection -->
<div class="tc-section">
<h5 class="tc-section-title">Current User</h5>
<div class="tc-option-row">
<label class="tc-label">Switch User:</label>
<select id="comboCurrentUser" class="tc-select" style="width: 100%;">
<option value="{715CFD19-2BB9-4B94-DE0C-08D0F9A019DB}">Alice Johnson</option>
<option value="{8A3E2F47-5C1D-4E8B-A9F2-3B7C6D8E9F01}">Bob Smith</option>
<option value="{C4D5E6F7-8A9B-0C1D-2E3F-4A5B6C7D8E9F}">Carol Manager</option>
<option value="{D1E2F3A4-B5C6-7D8E-9F0A-1B2C3D4E5F67}">David Accountant</option>
</select>
</div>
</div>
<!-- Thread Information -->
<div class="tc-section">
<h5 class="tc-section-title">Thread Information</h5>
<div class="tc-info-row">
<label class="tc-label">Location:</label>
<span id="lblThreadLocation" class="tc-info-value">-</span>
</div>
<div class="tc-info-row">
<label class="tc-label">Resolved:</label>
<span id="lblThreadResolved" class="tc-info-value">-</span>
</div>
<div class="tc-info-row">
<label class="tc-label">Replies Count:</label>
<span id="lblThreadRepliesCount" class="tc-info-value">-</span>
</div>
</div>
<!-- Thread Operations -->
<div class="tc-section">
<h5 class="tc-section-title">Thread Operations</h5>
<div class="tc-option-row">
<input type="text" id="txtNewThread" class="tc-input" placeholder="Enter thread message..." style="width: 100%; margin-bottom: 6px;" />
<button id="btnAddThread" class="tc-btn tc-btn-primary">Add New Thread</button>
</div>
<div class="tc-option-row">
<button id="btnToggleResolved" class="tc-btn tc-btn-secondary">Toggle Resolved Status</button>
<button id="btnDeleteThread" class="tc-btn tc-btn-danger">Delete Thread</button>
</div>
</div>
<!-- Replies Management -->
<div class="tc-section">
<h5 class="tc-section-title">Replies Management</h5>
<div class="tc-option-row">
<label class="tc-label">Select Reply:</label>
<select id="repliesList" class="tc-select" size="4" style="width: 100%; margin-top: 4px;">
<option>No replies loaded</option>
</select>
</div>
</div>
<!-- Reply Information -->
<div class="tc-section">
<h5 class="tc-section-title">Selected Reply Info</h5>
<div class="tc-info-row">
<label class="tc-label">Author:</label>
<span id="lblReplyAuthor" class="tc-info-value">-</span>
</div>
<div class="tc-info-row">
<label class="tc-label">Created At:</label>
<span id="lblReplyCreatedAt" class="tc-info-value">-</span>
</div>
<div class="tc-info-row">
<label class="tc-label">Message:</label>
<span id="lblReplyMessage" class="tc-info-value">-</span>
</div>
</div>
<!-- Reply Operations -->
<div class="tc-section">
<h5 class="tc-section-title">Reply Operations</h5>
<div class="tc-option-row">
<input type="text" id="txtNewReply" class="tc-input" placeholder="Enter reply message..." style="width: 100%; margin-bottom: 6px;" />
<button id="btnAddReply" class="tc-btn tc-btn-primary">Add Reply</button>
</div>
<div class="tc-option-row">
<input type="text" id="txtUpdateMessage" class="tc-input" placeholder="Enter new message..." style="width: 100%; margin-bottom: 6px;" />
<button id="btnUpdateReply" class="tc-btn tc-btn-secondary">Update Selected Reply</button>
</div>
<div class="tc-option-row">
<button id="btnDeleteReply" class="tc-btn tc-btn-danger">Delete Selected Reply</button>
</div>
</div>
</div>
</div>
</body>
</html>
/* Container styles with specific prefixes */
.tc-sample-tutorial {
position: relative;
height: 100%;
overflow: hidden;
}
.tc-sample-spreadsheets {
width: calc(100% - 320px);
height: 100%;
overflow: hidden;
float: left;
}
.tc-options-container {
float: right;
width: 320px;
overflow: auto;
padding: 12px;
height: 100%;
box-sizing: border-box;
background: #fbfbfb;
}
/* Title styles */
.tc-title {
margin: 0 0 12px 0;
font-size: 15px;
color: #333;
}
.tc-comment-tip {
color: #007acc;
font-weight: bold;
}
/* Section styles */
.tc-section {
margin-bottom: 20px;
padding: 12px;
background: white;
border: 1px solid #ddd;
border-radius: 4px;
}
.tc-section-title {
margin: 0 0 12px 0;
padding-bottom: 8px;
border-bottom: 2px solid #007acc;
color: #333;
font-size: 14px;
font-weight: bold;
}
.tc-option-row {
margin-bottom: 10px;
}
.tc-info-row {
display: flex;
align-items: center;
margin-bottom: 8px;
padding: 6px;
background: #f9f9f9;
border-radius: 3px;
}
.tc-info-row .tc-label {
min-width: 100px;
font-weight: bold;
color: #555;
display: inline-flex;
align-items: center;
margin: 0;
}
.tc-info-value {
flex: 1;
word-wrap: break-word;
color: #333;
display: inline-flex;
align-items: center;
min-height: 24px;
}
/* Form element styles */
.tc-label {
display: inline-block;
min-width: 100px;
margin: 6px 0;
font-weight: 500;
color: #333;
}
.tc-input {
padding: 6px 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 13px;
}
.tc-select {
padding: 6px 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 13px;
}
/* Button styles */
.tc-btn {
padding: 8px 12px;
margin: 4px 4px 4px 0;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: background-color 0.2s;
}
.tc-btn-primary {
background-color: #007acc;
color: white;
}
.tc-btn-primary:hover {
background-color: #005a9e;
}
.tc-btn-secondary {
background-color: #5c6bc0;
color: white;
}
.tc-btn-secondary:hover {
background-color: #3f51b5;
}
.tc-btn-danger {
background-color: #e53935;
color: white;
}
.tc-btn-danger:hover {
background-color: #c62828;
}
.tc-btn-info {
background-color: #26a69a;
color: white;
}
.tc-btn-info:hover {
background-color: #00897b;
}
body {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}