Commit c21d15b2 by Ooh-Ao

pivot

parent f0065caa
......@@ -1118,23 +1118,104 @@ export class WidgetConfigRegistryService {
// ========================================
'SyncfusionPivotWidgetComponent': {
...baseConfig,
// Basic Configuration
expandAll: false,
displayOptionView: 'Both',
chartType: 'Column',
dataSource: 'static',
apiEndpoint: '',
// Pivot Features
showToolbar: true,
showFieldList: true,
showGroupingBar: true,
showPager: true,
allowCalculatedField: false,
allowConditionalFormatting: false,
allowNumberFormatting: true,
allowFieldDragDrop: true,
allowSubTotal: true,
allowGrandTotal: true,
allowDrillThrough: false,
allowSorting: true,
allowExcelExport: true,
allowPdfExport: true,
allowCsvExport: true,
allowPrint: true,
aggregationType: 'sum',
numberFormat: 'N',
// Pivot Fields
rows: [],
columns: [],
values: [],
filters: [],
enableDrillThrough: true,
enableSorting: true,
// Enhanced UI Configuration
dataCount: 0,
lastUpdated: new Date(),
shadow: 'medium',
isLoading: false,
hasError: false,
errorMessage: '',
isEmpty: false,
// Enhanced Interaction Configuration
enableKeyboard: true,
enableFocus: true,
hoverEffect: true,
clickEffect: true,
enableTooltip: true,
enableSelection: true,
enableExport: true,
enableRefresh: true,
clickAction: 'none',
customClickHandler: '',
// Enhanced Layout Configuration
fullWidth: false,
fullHeight: false,
widthUnit: 'px',
heightUnit: 'px',
sizeOption: 'medium',
aspectRatio: 'auto',
// Enhanced Data Configuration
enableFiltering: true,
enableSorting: true,
enableGrouping: true,
enablePaging: true,
pageSize: 10,
currentPage: 1,
totalPages: 1,
// Enhanced Security Configuration
requireHttps: false,
allowedRoles: '',
dataEncryption: false,
auditLog: false,
rateLimit: 0,
// Enhanced Animation Configuration
enableAnimations: true,
animationType: 'fade',
animationDuration: 300,
animationDelay: 0,
// Enhanced Style Configuration
customCSS: '',
theme: 'light',
fontSize: 14,
fontWeight: 'normal',
fontFamily: 'system-ui, -apple-system, sans-serif',
padding: 16,
margin: 8,
// Additional Pivot Configuration
showGrandTotals: true,
showSubTotals: true,
showRowTotals: true,
showColumnTotals: true,
enableExport: true,
exportFormats: ['excel', 'pdf', 'csv']
},
......
<div
class="syncfusion-pivot-widget"
[ngStyle]="getAllStyles()"
[ngClass]="['custom-styled', getInteractionClasses()]"
[ngClass]="['custom-styled', getInteractionClasses(), 'shadow-' + shadow]"
[attr.data-widget-id]="widgetId"
[attr.data-source]="dataSource"
[title]="enableTooltip ? (title + ' - Pivot Table Widget') : null"
......@@ -21,13 +21,24 @@
<div class="header-content">
<div class="header-left">
<h4 class="widget-title">{{ title }}</h4>
<div class="data-count" *ngIf="dataCount > 0">
<i class="bi bi-database"></i>
<span>{{ dataCount }} records</span>
</div>
<div class="last-updated" *ngIf="lastUpdated">
<i class="bi bi-clock"></i>
<span>{{ lastUpdated | date:'short' }}</span>
</div>
</div>
<div class="header-actions">
<div *ngIf="enableExport" class="export-button" (click)="exportData($event)">
<div *ngIf="enableExport" class="action-button export-button" (click)="exportData($event)" title="Export Data">
<i class="bi bi-download"></i>
</div>
<div *ngIf="enableRefresh && dataSource !== 'static'" class="refresh-button" (click)="refreshData($event)">
<i class="bi bi-arrow-clockwise"></i>
<div *ngIf="enableRefresh && dataSource !== 'static'" class="action-button refresh-button" (click)="refreshData($event)" title="Refresh Data">
<i class="bi bi-arrow-clockwise" [class.rotating]="isLoading"></i>
</div>
<div *ngIf="enableFiltering" class="action-button filter-button" (click)="toggleFilter($event)" title="Toggle Filter">
<i class="bi bi-funnel"></i>
</div>
</div>
</div>
......@@ -35,6 +46,10 @@
<i class="bi bi-info-circle"></i>
<span>{{ getDataSourceInfo() }}</span>
</div>
<div *ngIf="requireHttps && !dataSource.includes('https')" class="security-warning">
<i class="bi bi-shield-exclamation"></i>
<span>HTTPS required for secure data transmission</span>
</div>
</div>
<!-- Security Warning -->
......@@ -47,32 +62,57 @@
<div *ngIf="hasRequiredRole()" class="widget-body">
<!-- Loading State -->
<div *ngIf="isLoading" class="flex justify-center items-center h-full">
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
<div *ngIf="isLoading" class="loading-state">
<div class="loading-spinner">
<div class="spinner"></div>
</div>
<p class="loading-text">Loading pivot data...</p>
</div>
<!-- Error State -->
<div *ngIf="hasError" class="flex flex-col justify-center items-center h-full text-red-500">
<i class="bi bi-exclamation-circle-fill text-4xl"></i>
<p class="mt-2 font-semibold">{{ errorMessage }}</p>
<div *ngIf="hasError" class="error-state">
<div class="error-icon">
<i class="bi bi-exclamation-circle-fill"></i>
</div>
<h3 class="error-title">Error Loading Data</h3>
<p class="error-message">{{ errorMessage }}</p>
<button class="retry-button" (click)="refreshData($event)">
<i class="bi bi-arrow-clockwise"></i>
Retry
</button>
</div>
<!-- Empty State -->
<div *ngIf="isEmpty && !isLoading && !hasError" class="empty-state">
<div class="empty-icon">
<i class="bi bi-table"></i>
</div>
<h3 class="empty-title">No Data Available</h3>
<p class="empty-message">There is no data to display in the pivot table.</p>
<button class="refresh-button" (click)="refreshData($event)">
<i class="bi bi-arrow-clockwise"></i>
Refresh
</button>
</div>
<!-- Pivot View -->
<ejs-pivotview #pivotview *ngIf="!isLoading && !hasError"
[dataSourceSettings]="dataSourceSettings"
[allowCalculatedField]="allowCalculatedField"
[showFieldList]="showFieldList"
[showToolbar]="showToolbar"
[showGroupingBar]="showGroupingBar"
[allowPdfExport]="allowPdfExport && enableExport"
[allowExcelExport]="allowExcelExport && enableExport"
(toolbarClick)="toolbarClick($event)"
[toolbar]="toolbar"
[displayOption]="displayOption"
[chartSettings]="chartSettings"
(dataBound)="onDataBound($event)"
[height]="'100%'">
</ejs-pivotview>
<div *ngIf="!isLoading && !hasError && !isEmpty" class="pivot-container">
<ejs-pivotview #pivotview
[dataSourceSettings]="dataSourceSettings"
[allowCalculatedField]="allowCalculatedField"
[showFieldList]="showFieldList"
[showToolbar]="showToolbar"
[showGroupingBar]="showGroupingBar"
[allowPdfExport]="allowPdfExport && enableExport"
[allowExcelExport]="allowExcelExport && enableExport"
(toolbarClick)="toolbarClick($event)"
[toolbar]="toolbar"
[displayOption]="displayOption"
[chartSettings]="chartSettings"
(dataBound)="onDataBound($event)"
[height]="'100%'">
</ejs-pivotview>
</div>
</div>
</div>
......@@ -5,13 +5,36 @@
height: 100%;
position: relative;
overflow: hidden;
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
transition: all 0.3s ease;
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
// Shadow variants
&.shadow-none {
box-shadow: none;
}
&.shadow-small {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
&.shadow-medium {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
&.shadow-large {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
// Widget Header
.widget-header {
flex-shrink: 0;
padding: 12px 16px;
padding: 16px 20px;
border-bottom: 1px solid rgba(229, 231, 235, 0.5);
position: relative;
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
border-radius: 12px 12px 0 0;
.header-content {
display: flex;
......@@ -20,14 +43,41 @@
.header-left {
display: flex;
align-items: center;
gap: 8px;
flex-direction: column;
gap: 4px;
.widget-title {
font-size: 16px;
font-weight: 600;
font-size: 18px;
font-weight: 700;
margin: 0;
color: inherit;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.data-count {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #6b7280;
font-weight: 500;
i {
font-size: 10px;
}
}
.last-updated {
display: flex;
align-items: center;
gap: 4px;
font-size: 11px;
color: #9ca3af;
font-weight: 400;
i {
font-size: 9px;
}
}
}
......@@ -35,32 +85,39 @@
display: flex;
gap: 8px;
.export-button,
.refresh-button {
width: 32px;
height: 32px;
border-radius: 6px;
.action-button {
width: 36px;
height: 36px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.1);
color: inherit;
opacity: 0.7;
transition: all 0.3s ease;
background: rgba(255, 255, 255, 0.8);
color: #374151;
border: 1px solid rgba(229, 231, 235, 0.5);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
&:hover {
opacity: 1;
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
background: rgba(59, 130, 246, 0.1);
border-color: rgba(59, 130, 246, 0.3);
color: #3b82f6;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
&:active {
transform: translateY(0);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
i {
font-size: 14px;
font-size: 16px;
}
&.rotating i {
animation: spin 1s linear infinite;
}
}
}
......@@ -120,6 +177,174 @@
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
}
// Loading State
.loading-state {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
.loading-spinner {
margin-bottom: 16px;
.spinner {
width: 40px;
height: 40px;
border: 4px solid #e2e8f0;
border-top: 4px solid #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
}
.loading-text {
font-size: 14px;
color: #6b7280;
font-weight: 500;
margin: 0;
}
}
// Error State
.error-state {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
text-align: center;
.error-icon {
margin-bottom: 16px;
i {
font-size: 48px;
color: #ef4444;
}
}
.error-title {
font-size: 18px;
font-weight: 600;
color: #dc2626;
margin: 0 0 8px 0;
}
.error-message {
font-size: 14px;
color: #7f1d1d;
margin: 0 0 20px 0;
}
.retry-button {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
background: #ef4444;
color: white;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: #dc2626;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(239, 68, 68, 0.3);
}
&:active {
transform: translateY(0);
}
i {
font-size: 16px;
}
}
}
// Empty State
.empty-state {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
text-align: center;
.empty-icon {
margin-bottom: 16px;
i {
font-size: 48px;
color: #9ca3af;
}
}
.empty-title {
font-size: 18px;
font-weight: 600;
color: #374151;
margin: 0 0 8px 0;
}
.empty-message {
font-size: 14px;
color: #6b7280;
margin: 0 0 20px 0;
}
.refresh-button {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
background: #3b82f6;
color: white;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: #2563eb;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);
}
&:active {
transform: translateY(0);
}
i {
font-size: 16px;
}
}
}
// Pivot Container
.pivot-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
background: white;
border-radius: 0 0 12px 12px;
}
// Interaction States
......@@ -189,6 +414,15 @@
}
// Animation Keyframes
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes fadeIn {
from {
opacity: 0;
......@@ -238,6 +472,15 @@
}
}
@keyframes shimmer {
0% {
background-position: -200px 0;
}
100% {
background-position: calc(200px + 100%) 0;
}
}
// Aspect Ratio Constraints
&.aspect-ratio-16-9 {
aspect-ratio: 16/9;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment