API

In SpreadJS, you can manage threaded comments programmatically using the comprehensive API. The API allows you to create, retrieve, update, and delete threaded comments and their replies, as well as manage their resolved status.

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,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" } }, { 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; }