Initial commit
This commit is contained in:
793
references/api-advanced-widgets.md
Normal file
793
references/api-advanced-widgets.md
Normal file
@@ -0,0 +1,793 @@
|
||||
# Advanced Widgets & Layout API Reference
|
||||
|
||||
Complete reference for Container Widgets, Layout APIs, R Visualizations, Custom Widgets, and Navigation in SAP Analytics Cloud.
|
||||
|
||||
**Source**: [Analytics Designer API Reference 2025.14](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Container Widgets](#container-widgets)
|
||||
2. [Layout API](#layout-api)
|
||||
3. [Navigation API](#navigation-api)
|
||||
4. [R Visualization](#r-visualization)
|
||||
5. [Custom Widgets](#custom-widgets)
|
||||
6. [Script Objects](#script-objects)
|
||||
7. [Technical Objects](#technical-objects)
|
||||
|
||||
---
|
||||
|
||||
## Container Widgets
|
||||
|
||||
SAC provides five container widget types for organizing content.
|
||||
|
||||
### Panel
|
||||
|
||||
Basic container for grouping widgets.
|
||||
|
||||
```javascript
|
||||
// Show/hide panel
|
||||
Panel_1.setVisible(true);
|
||||
Panel_1.setVisible(false);
|
||||
|
||||
// Check visibility
|
||||
var isVisible = Panel_1.isVisible();
|
||||
```
|
||||
|
||||
**Characteristics**:
|
||||
- Groups widgets together
|
||||
- Move, copy, delete affects all contained widgets
|
||||
- No navigation functionality
|
||||
- Cannot execute scripts on panel click directly
|
||||
|
||||
### TabStrip
|
||||
|
||||
Container with tabbed navigation.
|
||||
|
||||
```javascript
|
||||
// Get active tab
|
||||
var activeTab = TabStrip_1.getSelectedTab();
|
||||
|
||||
// Set active tab
|
||||
TabStrip_1.setSelectedTab("Tab_Sales");
|
||||
|
||||
// Get all tabs
|
||||
var tabs = TabStrip_1.getTabs();
|
||||
```
|
||||
|
||||
**Events**:
|
||||
```javascript
|
||||
TabStrip_1.onSelect = function() {
|
||||
var selectedTab = TabStrip_1.getSelectedTab();
|
||||
console.log("Switched to tab:", selectedTab);
|
||||
|
||||
// Refresh data for newly visible tab
|
||||
if (selectedTab === "Tab_Details") {
|
||||
Table_Details.getDataSource().refreshData();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### PageBook
|
||||
|
||||
Container for page-based navigation.
|
||||
|
||||
```javascript
|
||||
// Navigate to page
|
||||
PageBook_1.setSelectedPage("Page_Dashboard");
|
||||
|
||||
// Get current page
|
||||
var currentPage = PageBook_1.getSelectedPage();
|
||||
|
||||
// Navigate by index
|
||||
PageBook_1.setSelectedPageIndex(0); // First page
|
||||
```
|
||||
|
||||
**Page Navigation Pattern**:
|
||||
```javascript
|
||||
// Navigation buttons
|
||||
Button_Next.onClick = function() {
|
||||
var pages = PageBook_1.getPages();
|
||||
var currentIndex = pages.indexOf(PageBook_1.getSelectedPage());
|
||||
|
||||
if (currentIndex < pages.length - 1) {
|
||||
PageBook_1.setSelectedPageIndex(currentIndex + 1);
|
||||
}
|
||||
};
|
||||
|
||||
Button_Previous.onClick = function() {
|
||||
var pages = PageBook_1.getPages();
|
||||
var currentIndex = pages.indexOf(PageBook_1.getSelectedPage());
|
||||
|
||||
if (currentIndex > 0) {
|
||||
PageBook_1.setSelectedPageIndex(currentIndex - 1);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Flow Layout Panel
|
||||
|
||||
Responsive container with automatic reflow.
|
||||
|
||||
```javascript
|
||||
// Same visibility controls as Panel
|
||||
FlowLayoutPanel_1.setVisible(true);
|
||||
```
|
||||
|
||||
**Characteristics**:
|
||||
- Widgets placed sequentially (not freely positioned)
|
||||
- Automatic reflow on resize
|
||||
- Responsive behavior (widgets wrap to next row)
|
||||
- Similar to responsive lanes
|
||||
|
||||
**Use Case**: Create responsive layouts that adapt to screen size.
|
||||
|
||||
### Popup
|
||||
|
||||
Overlay container that appears above content.
|
||||
|
||||
```javascript
|
||||
// Open popup
|
||||
Popup_1.open();
|
||||
|
||||
// Close popup
|
||||
Popup_1.close();
|
||||
```
|
||||
|
||||
**Events**:
|
||||
```javascript
|
||||
Popup_1.onOpen = function() {
|
||||
// Initialize popup content
|
||||
refreshPopupData();
|
||||
};
|
||||
|
||||
Popup_1.onClose = function() {
|
||||
// Cleanup
|
||||
clearPopupSelections();
|
||||
};
|
||||
```
|
||||
|
||||
**Dialog Mode**: Enable "Header & Footer" in Builder for dialog style with title bar and buttons.
|
||||
|
||||
---
|
||||
|
||||
## Layout API
|
||||
|
||||
Dynamically control widget size and position.
|
||||
|
||||
### Position Methods
|
||||
|
||||
```javascript
|
||||
// Get position (pixels from edge)
|
||||
var left = Widget_1.getLeft();
|
||||
var top = Widget_1.getTop();
|
||||
var right = Widget_1.getRight();
|
||||
var bottom = Widget_1.getBottom();
|
||||
|
||||
// Set position
|
||||
Widget_1.setLeft(100);
|
||||
Widget_1.setTop(50);
|
||||
Widget_1.setRight(200);
|
||||
Widget_1.setBottom(100);
|
||||
```
|
||||
|
||||
### Size Methods
|
||||
|
||||
```javascript
|
||||
// Get dimensions
|
||||
var width = Widget_1.getWidth();
|
||||
var height = Widget_1.getHeight();
|
||||
|
||||
// Set dimensions (pixels or percentage)
|
||||
Widget_1.setWidth(500); // 500 pixels
|
||||
Widget_1.setHeight(300); // 300 pixels
|
||||
|
||||
// Percentage-based
|
||||
Widget_1.setWidth("50%");
|
||||
Widget_1.setHeight("auto");
|
||||
```
|
||||
|
||||
### Responsive Layout Pattern
|
||||
|
||||
```javascript
|
||||
Application.onResize = function(windowWidth, windowHeight) {
|
||||
// Mobile layout
|
||||
if (windowWidth < 768) {
|
||||
Chart_1.setWidth("100%");
|
||||
Chart_1.setHeight(200);
|
||||
Table_1.setWidth("100%");
|
||||
Panel_Sidebar.setVisible(false);
|
||||
}
|
||||
// Tablet layout
|
||||
else if (windowWidth < 1024) {
|
||||
Chart_1.setWidth("50%");
|
||||
Chart_1.setHeight(300);
|
||||
Table_1.setWidth("50%");
|
||||
Panel_Sidebar.setVisible(true);
|
||||
}
|
||||
// Desktop layout
|
||||
else {
|
||||
Chart_1.setWidth("70%");
|
||||
Chart_1.setHeight(400);
|
||||
Table_1.setWidth("30%");
|
||||
Panel_Sidebar.setVisible(true);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Collapsible Panel Pattern
|
||||
|
||||
```javascript
|
||||
var isPanelExpanded = true;
|
||||
|
||||
Button_TogglePanel.onClick = function() {
|
||||
isPanelExpanded = !isPanelExpanded;
|
||||
|
||||
if (isPanelExpanded) {
|
||||
Panel_Navigation.setWidth(250);
|
||||
Panel_Content.setLeft(260);
|
||||
Button_TogglePanel.setText("◀");
|
||||
} else {
|
||||
Panel_Navigation.setWidth(50);
|
||||
Panel_Content.setLeft(60);
|
||||
Button_TogglePanel.setText("▶");
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Navigation API
|
||||
|
||||
Navigate between applications and stories.
|
||||
|
||||
### NavigationUtils
|
||||
|
||||
```javascript
|
||||
// Open another application
|
||||
NavigationUtils.openApplication("APP_ID", {
|
||||
mode: ApplicationMode.View,
|
||||
newWindow: true
|
||||
});
|
||||
|
||||
// Open a story
|
||||
NavigationUtils.openStory("STORY_ID", {
|
||||
newWindow: false
|
||||
});
|
||||
|
||||
// Open URL
|
||||
NavigationUtils.openUrl("[https://example.com",](https://example.com",) true); // true = new window
|
||||
```
|
||||
|
||||
### Create Application URL with Parameters
|
||||
|
||||
```javascript
|
||||
var url = NavigationUtils.createApplicationUrl("APP_ID", {
|
||||
p_year: "2024",
|
||||
p_region: "EMEA",
|
||||
bookmarkId: "DEFAULT"
|
||||
});
|
||||
|
||||
console.log("Share URL:", url);
|
||||
```
|
||||
|
||||
### Page Navigation (Within Application)
|
||||
|
||||
For applications with multiple pages:
|
||||
|
||||
```javascript
|
||||
// Switch to page
|
||||
Application.setActivePage("Page_Details");
|
||||
|
||||
// Get current page
|
||||
var currentPage = Application.getActivePage();
|
||||
```
|
||||
|
||||
### Custom Navigation Menu Pattern
|
||||
|
||||
```javascript
|
||||
// Navigation sidebar buttons
|
||||
Button_Dashboard.onClick = function() {
|
||||
highlightNavButton(Button_Dashboard);
|
||||
PageBook_1.setSelectedPage("Page_Dashboard");
|
||||
};
|
||||
|
||||
Button_Analysis.onClick = function() {
|
||||
highlightNavButton(Button_Analysis);
|
||||
PageBook_1.setSelectedPage("Page_Analysis");
|
||||
};
|
||||
|
||||
Button_Settings.onClick = function() {
|
||||
highlightNavButton(Button_Settings);
|
||||
PageBook_1.setSelectedPage("Page_Settings");
|
||||
};
|
||||
|
||||
// Helper function
|
||||
function highlightNavButton(activeButton) {
|
||||
var navButtons = [Button_Dashboard, Button_Analysis, Button_Settings];
|
||||
navButtons.forEach(function(btn) {
|
||||
if (btn === activeButton) {
|
||||
btn.setCssClass("nav-button-active");
|
||||
} else {
|
||||
btn.setCssClass("nav-button");
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Cross-Story Navigation
|
||||
|
||||
```javascript
|
||||
// Navigate to story with filters
|
||||
Button_DrillToStory.onClick = function() {
|
||||
var selections = Table_1.getSelections();
|
||||
|
||||
if (selections.length > 0) {
|
||||
var productId = selections[0]["Product"];
|
||||
|
||||
NavigationUtils.openStory("DETAIL_STORY_ID", {
|
||||
newWindow: false,
|
||||
urlParameters: {
|
||||
p_product: productId,
|
||||
p_year: Dropdown_Year.getSelectedKey()
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## R Visualization
|
||||
|
||||
R widgets allow custom visualizations using R scripts.
|
||||
|
||||
### Adding R Visualization
|
||||
|
||||
1. Insert → R Visualization in Story
|
||||
2. Configure Input Data (data source binding)
|
||||
3. Write R script in Script Editor
|
||||
|
||||
### R Widget Configuration
|
||||
|
||||
**Input Data**: Binds SAC data to R variables
|
||||
|
||||
**R Script**: Executes R code on the input data
|
||||
|
||||
### Common R Libraries
|
||||
|
||||
```r
|
||||
# Available libraries include:
|
||||
library(ggplot2) # Data visualization
|
||||
library(plotly) # Interactive charts
|
||||
library(dplyr) # Data manipulation
|
||||
library(tidyr) # Data tidying
|
||||
library(scales) # Scale functions
|
||||
```
|
||||
|
||||
### Basic R Chart Example
|
||||
|
||||
```r
|
||||
# Data comes from SAC as data frame
|
||||
# 'data' is the input data variable
|
||||
|
||||
library(ggplot2)
|
||||
|
||||
# Create bar chart
|
||||
ggplot(data, aes(x=Category, y=Value, fill=Region)) +
|
||||
geom_bar(stat="identity", position="dodge") +
|
||||
theme_minimal() +
|
||||
labs(title="Sales by Category and Region")
|
||||
```
|
||||
|
||||
### Interactive Plotly Example
|
||||
|
||||
```r
|
||||
library(plotly)
|
||||
|
||||
# Create interactive chart
|
||||
plot_ly(data,
|
||||
x = ~Month,
|
||||
y = ~Revenue,
|
||||
type = 'scatter',
|
||||
mode = 'lines+markers',
|
||||
color = ~Product) %>%
|
||||
layout(title = "Monthly Revenue Trend")
|
||||
```
|
||||
|
||||
### Animated R Visualization
|
||||
|
||||
```r
|
||||
library(gganimate)
|
||||
library(ggplot2)
|
||||
|
||||
# Animated chart
|
||||
p <- ggplot(data, aes(x=Year, y=Value, color=Category)) +
|
||||
geom_point(size=5) +
|
||||
transition_time(Year) +
|
||||
labs(title = "Year: {frame_time}")
|
||||
|
||||
animate(p, nframes=50)
|
||||
```
|
||||
|
||||
### R Visualization Limitations
|
||||
|
||||
- No direct script interaction with SAC widgets
|
||||
- Input data must be pre-filtered via data source binding
|
||||
- Cannot call R scripts programmatically from SAC script
|
||||
|
||||
### Use Cases
|
||||
|
||||
- Specialized statistical visualizations
|
||||
- Animated charts
|
||||
- Complex data science visualizations
|
||||
- Charts not available in standard SAC library
|
||||
|
||||
---
|
||||
|
||||
## Custom Widgets
|
||||
|
||||
Extend SAC with custom web components.
|
||||
|
||||
### Custom Widget Structure
|
||||
|
||||
```
|
||||
my-widget/
|
||||
├── widget.json # Metadata and configuration
|
||||
├── widget.js # JavaScript implementation
|
||||
├── widget.css # Optional styling
|
||||
└── assets/ # Optional images/resources
|
||||
```
|
||||
|
||||
### widget.json Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "com.company.mywidget",
|
||||
"version": "1.0.0",
|
||||
"name": "My Custom Widget",
|
||||
"description": "A custom widget for SAC",
|
||||
"icon": "data:image/png;base64,...",
|
||||
"webComponent": {
|
||||
"tagName": "my-custom-widget",
|
||||
"entryPoint": "widget.js"
|
||||
},
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"default": "Widget Title"
|
||||
},
|
||||
"threshold": {
|
||||
"type": "number",
|
||||
"default": 100
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"refresh": {
|
||||
"returnType": "void",
|
||||
"description": "Refreshes the widget"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"onClick": {
|
||||
"description": "Fires when widget is clicked"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### widget.js Structure
|
||||
|
||||
```javascript
|
||||
(function() {
|
||||
let template = document.createElement("template");
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
.container { padding: 10px; }
|
||||
.title { font-weight: bold; }
|
||||
</style>
|
||||
<div class="container">
|
||||
<div class="title"></div>
|
||||
<div class="content"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
class MyWidget extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: "open" });
|
||||
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
||||
this._props = {};
|
||||
}
|
||||
|
||||
// Lifecycle: widget added to DOM
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
// Called when properties change
|
||||
onCustomWidgetBeforeUpdate(changedProps) {
|
||||
this._props = { ...this._props, ...changedProps };
|
||||
}
|
||||
|
||||
onCustomWidgetAfterUpdate(changedProps) {
|
||||
this.render();
|
||||
}
|
||||
|
||||
// Called when widget is resized
|
||||
onCustomWidgetResize(width, height) {
|
||||
// Handle resize
|
||||
}
|
||||
|
||||
// Called when widget is removed
|
||||
onCustomWidgetDestroy() {
|
||||
// Cleanup
|
||||
}
|
||||
|
||||
render() {
|
||||
let titleEl = this.shadowRoot.querySelector(".title");
|
||||
titleEl.textContent = this._props.title || "";
|
||||
}
|
||||
|
||||
// Custom method (callable from SAC script)
|
||||
refresh() {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-custom-widget", MyWidget);
|
||||
})();
|
||||
```
|
||||
|
||||
### Using Custom Widgets in SAC
|
||||
|
||||
```javascript
|
||||
// Access custom widget
|
||||
CustomWidget_1.setTitle("New Title");
|
||||
|
||||
// Call custom method
|
||||
CustomWidget_1.refresh();
|
||||
|
||||
// Handle custom event
|
||||
CustomWidget_1.onClick = function() {
|
||||
// Widget was clicked
|
||||
Application.showMessage(ApplicationMessageType.Info, "Widget clicked!");
|
||||
};
|
||||
```
|
||||
|
||||
### Custom Widget with Data Binding
|
||||
|
||||
```javascript
|
||||
// In custom widget (supports Linked Analysis)
|
||||
this.dataBindings
|
||||
.getDataBinding()
|
||||
.getLinkedAnalysis()
|
||||
.setFilters(selection);
|
||||
```
|
||||
|
||||
### Timer Custom Widget Example
|
||||
|
||||
```javascript
|
||||
// TimeCountdown widget pattern
|
||||
class TimeCountdownWidget extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this._endDate = new Date();
|
||||
this._timer = null;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.startCountdown();
|
||||
}
|
||||
|
||||
startCountdown() {
|
||||
this._timer = setInterval(() => {
|
||||
this.updateDisplay();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
updateDisplay() {
|
||||
const now = new Date();
|
||||
const diff = this._endDate - now;
|
||||
|
||||
if (diff <= 0) {
|
||||
clearInterval(this._timer);
|
||||
this.innerHTML = "Time's up!";
|
||||
return;
|
||||
}
|
||||
|
||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
|
||||
|
||||
this.innerHTML = `${days}d ${hours}h ${minutes}m ${seconds}s`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Script Objects
|
||||
|
||||
Reusable function containers.
|
||||
|
||||
### Creating Script Objects
|
||||
|
||||
1. Outline panel → Script Objects → (+)
|
||||
2. Name the script object (e.g., `Utils`)
|
||||
3. Add functions
|
||||
|
||||
### Script Object Functions
|
||||
|
||||
```javascript
|
||||
// In ScriptObject: Utils
|
||||
|
||||
// Function: formatCurrency
|
||||
function formatCurrency(value, currency) {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: currency || 'USD'
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
// Function: getQuarter
|
||||
function getQuarter(date) {
|
||||
var month = date.getMonth();
|
||||
return Math.floor(month / 3) + 1;
|
||||
}
|
||||
|
||||
// Function: applyStandardFilters
|
||||
function applyStandardFilters(dataSource, year, region) {
|
||||
dataSource.setRefreshPaused(true);
|
||||
dataSource.setDimensionFilter("Year", year);
|
||||
dataSource.setDimensionFilter("Region", region);
|
||||
dataSource.setRefreshPaused(false);
|
||||
}
|
||||
```
|
||||
|
||||
### Calling Script Object Functions
|
||||
|
||||
```javascript
|
||||
// From any event handler
|
||||
var formatted = Utils.formatCurrency(1234.56, "EUR");
|
||||
// Returns: "€1,234.56"
|
||||
|
||||
var quarter = Utils.getQuarter(new Date());
|
||||
// Returns: 1-4
|
||||
|
||||
Utils.applyStandardFilters(
|
||||
Chart_1.getDataSource(),
|
||||
"2024",
|
||||
"EMEA"
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Technical Objects
|
||||
|
||||
Special objects for advanced functionality.
|
||||
|
||||
### Available Technical Objects
|
||||
|
||||
| Object | Description |
|
||||
|--------|-------------|
|
||||
| BookmarkSet | Save/load application state |
|
||||
| Timer | Scheduled script execution |
|
||||
| Calendar | Calendar integration |
|
||||
| DataAction | Execute data actions |
|
||||
| MultiAction | Execute multiple actions |
|
||||
| ScriptVariable | Application-wide variables |
|
||||
|
||||
### Adding Technical Objects
|
||||
|
||||
1. Outline panel → Technical Objects
|
||||
2. Click (+) next to desired object type
|
||||
3. Configure in Builder panel
|
||||
|
||||
### Global Script Variables
|
||||
|
||||
Create via Outline → Script Variables:
|
||||
|
||||
```javascript
|
||||
// Access global variable
|
||||
var currentYear = GlobalYear;
|
||||
|
||||
// Set global variable
|
||||
GlobalYear = "2024";
|
||||
|
||||
// URL parameter (prefix with p_)
|
||||
// URL: ?p_Year=2024
|
||||
// Variable name: p_Year
|
||||
var urlYear = p_Year;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Example: Custom Dashboard Layout
|
||||
|
||||
```javascript
|
||||
// Initialize responsive layout
|
||||
Application.onInitialization = function() {
|
||||
// Set initial layout based on window size
|
||||
adjustLayout(window.innerWidth);
|
||||
};
|
||||
|
||||
Application.onResize = function(width, height) {
|
||||
adjustLayout(width);
|
||||
};
|
||||
|
||||
function adjustLayout(width) {
|
||||
// Mobile
|
||||
if (width < 768) {
|
||||
setMobileLayout();
|
||||
}
|
||||
// Tablet
|
||||
else if (width < 1200) {
|
||||
setTabletLayout();
|
||||
}
|
||||
// Desktop
|
||||
else {
|
||||
setDesktopLayout();
|
||||
}
|
||||
}
|
||||
|
||||
function setMobileLayout() {
|
||||
Panel_Sidebar.setVisible(false);
|
||||
Panel_Main.setWidth("100%");
|
||||
Panel_Main.setLeft(0);
|
||||
|
||||
TabStrip_Charts.setSelectedTab("Tab_Summary");
|
||||
Chart_Detail.setVisible(false);
|
||||
}
|
||||
|
||||
function setTabletLayout() {
|
||||
Panel_Sidebar.setVisible(true);
|
||||
Panel_Sidebar.setWidth(200);
|
||||
Panel_Main.setWidth("calc(100% - 210px)");
|
||||
Panel_Main.setLeft(210);
|
||||
|
||||
Chart_Detail.setVisible(true);
|
||||
Chart_Detail.setWidth("100%");
|
||||
}
|
||||
|
||||
function setDesktopLayout() {
|
||||
Panel_Sidebar.setVisible(true);
|
||||
Panel_Sidebar.setWidth(250);
|
||||
Panel_Main.setWidth("calc(100% - 260px)");
|
||||
Panel_Main.setLeft(260);
|
||||
|
||||
Chart_Detail.setVisible(true);
|
||||
Chart_Detail.setWidth("50%");
|
||||
}
|
||||
|
||||
// Navigation
|
||||
Button_Dashboard.onClick = function() {
|
||||
PageBook_1.setSelectedPage("Page_Dashboard");
|
||||
Utils.highlightNavButton("Dashboard");
|
||||
};
|
||||
|
||||
Button_Analysis.onClick = function() {
|
||||
PageBook_1.setSelectedPage("Page_Analysis");
|
||||
Utils.highlightNavButton("Analysis");
|
||||
};
|
||||
|
||||
Button_Export.onClick = function() {
|
||||
Popup_Export.open();
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [DataSource API](api-datasource.md)
|
||||
- [Widgets API](api-widgets.md)
|
||||
- [Planning API](api-planning.md)
|
||||
- [Calendar & Bookmarks API](api-calendar-bookmarks.md)
|
||||
|
||||
**Official References**:
|
||||
- [Analytics Designer API Reference 2025.14](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
- [Custom Widget Developer Guide](https://help.sap.com/doc/c813a28922b54e50bd2a307b099787dc/release/en-US/CustomWidgetDevGuide_en.pdf)
|
||||
Reference in New Issue
Block a user