mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-07-30 03:09:50 +08:00
317 lines
8.0 KiB
JavaScript
317 lines
8.0 KiB
JavaScript
|
|
/**
|
||
|
|
* Workspace API Component Template
|
||
|
|
*
|
||
|
|
* Demonstrates tab management in Salesforce Console (Service/Sales Console) using:
|
||
|
|
* - IsConsoleNavigation wire to detect console context
|
||
|
|
* - Tab opening, closing, and focusing
|
||
|
|
* - Subtab management
|
||
|
|
* - Tab label and icon customization
|
||
|
|
* - Tab refresh
|
||
|
|
*
|
||
|
|
* @see https://developer.salesforce.com/docs/component-library/bundle/lightning-workspace-api
|
||
|
|
*/
|
||
|
|
import { LightningElement, wire, api } from 'lwc';
|
||
|
|
import {
|
||
|
|
IsConsoleNavigation,
|
||
|
|
getFocusedTabInfo,
|
||
|
|
getTabInfo,
|
||
|
|
openTab,
|
||
|
|
closeTab,
|
||
|
|
focusTab,
|
||
|
|
refreshTab,
|
||
|
|
setTabLabel,
|
||
|
|
setTabIcon,
|
||
|
|
setTabHighlighted,
|
||
|
|
disableTabClose,
|
||
|
|
getAllTabInfo,
|
||
|
|
openSubtab,
|
||
|
|
MINIMUM_SUBTAB_SIZE
|
||
|
|
} from 'lightning/platformWorkspaceApi';
|
||
|
|
|
||
|
|
export default class WorkspaceComponent extends LightningElement {
|
||
|
|
// Wire to check if we're in a console app
|
||
|
|
@wire(IsConsoleNavigation) isConsoleNavigation;
|
||
|
|
|
||
|
|
// Current tab information
|
||
|
|
currentTabId;
|
||
|
|
currentTabLabel;
|
||
|
|
allTabs = [];
|
||
|
|
|
||
|
|
// Error tracking
|
||
|
|
error;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if the component is running in a console context
|
||
|
|
*/
|
||
|
|
get isConsole() {
|
||
|
|
return this.isConsoleNavigation?.data === true;
|
||
|
|
}
|
||
|
|
|
||
|
|
get isNotConsole() {
|
||
|
|
return this.isConsoleNavigation?.data === false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Connected callback - get current tab info
|
||
|
|
*/
|
||
|
|
async connectedCallback() {
|
||
|
|
if (this.isConsole) {
|
||
|
|
await this._refreshCurrentTabInfo();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==================
|
||
|
|
// Public API Methods
|
||
|
|
// ==================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Open a record in a new tab
|
||
|
|
* @param {String} recordId - The record ID to open
|
||
|
|
* @param {String} objectApiName - The object API name (for icon)
|
||
|
|
* @param {Boolean} focus - Whether to focus the new tab
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async openRecordTab(recordId, objectApiName = 'Account', focus = true) {
|
||
|
|
if (!this.isConsole) {
|
||
|
|
console.warn('openRecordTab: Not in console context');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
const tabId = await openTab({
|
||
|
|
recordId,
|
||
|
|
focus,
|
||
|
|
icon: `standard:${objectApiName.toLowerCase()}`,
|
||
|
|
label: 'Loading...'
|
||
|
|
});
|
||
|
|
return tabId;
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error opening tab', error);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Open a URL in a new tab
|
||
|
|
* @param {String} url - The URL to open
|
||
|
|
* @param {String} label - The tab label
|
||
|
|
* @param {String} icon - The tab icon
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async openUrlTab(url, label = 'New Tab', icon = 'utility:new_window') {
|
||
|
|
if (!this.isConsole) return null;
|
||
|
|
|
||
|
|
try {
|
||
|
|
const tabId = await openTab({
|
||
|
|
url,
|
||
|
|
focus: true,
|
||
|
|
label,
|
||
|
|
icon
|
||
|
|
});
|
||
|
|
return tabId;
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error opening URL tab', error);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Open a subtab under the current (or specified) parent tab
|
||
|
|
* @param {String} recordId - The record ID for the subtab
|
||
|
|
* @param {String} parentTabId - Optional parent tab ID (defaults to current tab)
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async openRecordSubtab(recordId, parentTabId = null) {
|
||
|
|
if (!this.isConsole) return null;
|
||
|
|
|
||
|
|
try {
|
||
|
|
const parent = parentTabId || (await this._getCurrentTabId());
|
||
|
|
const subtabId = await openSubtab({
|
||
|
|
parentTabId: parent,
|
||
|
|
recordId,
|
||
|
|
focus: true
|
||
|
|
});
|
||
|
|
return subtabId;
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error opening subtab', error);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Close a specific tab
|
||
|
|
* @param {String} tabId - The tab ID to close
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async closeTabById(tabId) {
|
||
|
|
if (!this.isConsole) return;
|
||
|
|
|
||
|
|
try {
|
||
|
|
await closeTab(tabId);
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error closing tab', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Close the current tab
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async closeCurrentTab() {
|
||
|
|
if (!this.isConsole) return;
|
||
|
|
|
||
|
|
try {
|
||
|
|
const tabId = await this._getCurrentTabId();
|
||
|
|
await closeTab(tabId);
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error closing current tab', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Focus a specific tab
|
||
|
|
* @param {String} tabId - The tab ID to focus
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async focusTabById(tabId) {
|
||
|
|
if (!this.isConsole) return;
|
||
|
|
|
||
|
|
try {
|
||
|
|
await focusTab(tabId);
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error focusing tab', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update the current tab's label
|
||
|
|
* @param {String} label - The new label
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async updateCurrentTabLabel(label) {
|
||
|
|
if (!this.isConsole) return;
|
||
|
|
|
||
|
|
try {
|
||
|
|
const tabId = await this._getCurrentTabId();
|
||
|
|
await setTabLabel(tabId, label);
|
||
|
|
this.currentTabLabel = label;
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error updating tab label', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update the current tab's icon
|
||
|
|
* @param {String} iconName - The icon name (e.g., 'standard:account')
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async updateCurrentTabIcon(iconName) {
|
||
|
|
if (!this.isConsole) return;
|
||
|
|
|
||
|
|
try {
|
||
|
|
const tabId = await this._getCurrentTabId();
|
||
|
|
await setTabIcon(tabId, iconName);
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error updating tab icon', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Highlight a tab (shows visual indicator)
|
||
|
|
* @param {String} tabId - The tab ID to highlight
|
||
|
|
* @param {Boolean} highlighted - Whether to highlight or remove highlight
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async highlightTab(tabId, highlighted = true) {
|
||
|
|
if (!this.isConsole) return;
|
||
|
|
|
||
|
|
try {
|
||
|
|
await setTabHighlighted(tabId, highlighted);
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error highlighting tab', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Prevent a tab from being closed
|
||
|
|
* @param {String} tabId - The tab ID
|
||
|
|
* @param {Boolean} disabled - Whether to disable close
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async preventTabClose(tabId, disabled = true) {
|
||
|
|
if (!this.isConsole) return;
|
||
|
|
|
||
|
|
try {
|
||
|
|
await disableTabClose(tabId, disabled);
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error disabling tab close', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Refresh the current tab
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async refreshCurrentTab() {
|
||
|
|
if (!this.isConsole) return;
|
||
|
|
|
||
|
|
try {
|
||
|
|
const tabId = await this._getCurrentTabId();
|
||
|
|
await refreshTab(tabId);
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error refreshing tab', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get information about all open tabs
|
||
|
|
*/
|
||
|
|
@api
|
||
|
|
async getAllTabs() {
|
||
|
|
if (!this.isConsole) return [];
|
||
|
|
|
||
|
|
try {
|
||
|
|
const tabInfo = await getAllTabInfo();
|
||
|
|
this.allTabs = tabInfo;
|
||
|
|
return tabInfo;
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error getting all tabs', error);
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// =================
|
||
|
|
// Private Methods
|
||
|
|
// =================
|
||
|
|
|
||
|
|
async _getCurrentTabId() {
|
||
|
|
const tabInfo = await getFocusedTabInfo();
|
||
|
|
this.currentTabId = tabInfo.tabId;
|
||
|
|
return tabInfo.tabId;
|
||
|
|
}
|
||
|
|
|
||
|
|
async _refreshCurrentTabInfo() {
|
||
|
|
try {
|
||
|
|
const tabInfo = await getFocusedTabInfo();
|
||
|
|
this.currentTabId = tabInfo.tabId;
|
||
|
|
this.currentTabLabel = tabInfo.title;
|
||
|
|
} catch (error) {
|
||
|
|
this._handleError('Error getting tab info', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
_handleError(message, error) {
|
||
|
|
console.error(message, error);
|
||
|
|
this.error = `${message}: ${error?.body?.message || error?.message || 'Unknown error'}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Event handlers for UI (if template is used)
|
||
|
|
handleRefresh() {
|
||
|
|
this.refreshCurrentTab();
|
||
|
|
}
|
||
|
|
|
||
|
|
handleGetAllTabs() {
|
||
|
|
this.getAllTabs();
|
||
|
|
}
|
||
|
|
}
|