@makolabs/ripple 0.0.1-dev.9 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/dist/adapters/storage/BaseAdapter.d.ts +20 -0
  3. package/dist/adapters/storage/BaseAdapter.js +171 -0
  4. package/dist/adapters/storage/S3Adapter.d.ts +21 -0
  5. package/dist/adapters/storage/S3Adapter.js +194 -0
  6. package/dist/adapters/storage/index.d.ts +3 -0
  7. package/dist/adapters/storage/index.js +3 -0
  8. package/dist/adapters/storage/types.d.ts +102 -0
  9. package/dist/adapters/storage/types.js +4 -0
  10. package/dist/charts/Chart.svelte +59 -47
  11. package/dist/charts/Chart.svelte.d.ts +1 -1
  12. package/dist/drawer/drawer.js +3 -3
  13. package/dist/elements/accordion/Accordion.svelte +98 -0
  14. package/dist/elements/accordion/Accordion.svelte.d.ts +4 -0
  15. package/dist/elements/accordion/accordion.d.ts +227 -0
  16. package/dist/elements/accordion/accordion.js +138 -0
  17. package/dist/elements/alert/Alert.svelte +7 -3
  18. package/dist/elements/dropdown/Dropdown.svelte +74 -107
  19. package/dist/elements/dropdown/Select.svelte +81 -62
  20. package/dist/elements/dropdown/dropdown.js +1 -1
  21. package/dist/elements/dropdown/select.js +8 -8
  22. package/dist/elements/file-upload/FileUpload.svelte +17 -95
  23. package/dist/elements/file-upload/FilesPreview.svelte +93 -0
  24. package/dist/elements/file-upload/FilesPreview.svelte.d.ts +4 -0
  25. package/dist/elements/progress/Progress.svelte +83 -25
  26. package/dist/file-browser/FileBrowser.svelte +877 -0
  27. package/dist/file-browser/FileBrowser.svelte.d.ts +14 -0
  28. package/dist/file-browser/index.d.ts +1 -0
  29. package/dist/file-browser/index.js +1 -0
  30. package/dist/filters/CompactFilters.svelte +147 -0
  31. package/dist/filters/CompactFilters.svelte.d.ts +4 -0
  32. package/dist/filters/index.d.ts +1 -0
  33. package/dist/filters/index.js +1 -0
  34. package/dist/forms/Checkbox.svelte +2 -2
  35. package/dist/forms/DateRange.svelte +21 -21
  36. package/dist/forms/Input.svelte +3 -3
  37. package/dist/forms/NumberInput.svelte +1 -1
  38. package/dist/forms/RadioInputs.svelte +3 -3
  39. package/dist/forms/Tags.svelte +5 -5
  40. package/dist/forms/Toggle.svelte +3 -3
  41. package/dist/forms/slider.js +4 -4
  42. package/dist/header/PageHeader.svelte +49 -11
  43. package/dist/index.d.ts +256 -143
  44. package/dist/index.js +19 -2
  45. package/dist/layout/card/MetricCard.svelte +64 -0
  46. package/dist/layout/card/MetricCard.svelte.d.ts +4 -0
  47. package/dist/layout/card/StatsCard.svelte +4 -3
  48. package/dist/layout/card/StatsCard.svelte.d.ts +1 -1
  49. package/dist/layout/card/metric-card.d.ts +49 -0
  50. package/dist/layout/card/metric-card.js +10 -0
  51. package/dist/layout/card/stats-card.d.ts +0 -15
  52. package/dist/layout/card/stats-card.js +1 -1
  53. package/dist/layout/sidebar/NavGroup.svelte +1 -7
  54. package/dist/layout/sidebar/NavItem.svelte +2 -2
  55. package/dist/layout/sidebar/Sidebar.svelte +103 -49
  56. package/dist/layout/table/Table.svelte +465 -88
  57. package/dist/layout/table/Table.svelte.d.ts +1 -1
  58. package/dist/layout/table/table.d.ts +0 -47
  59. package/dist/layout/table/table.js +0 -8
  60. package/dist/layout/tabs/Tab.svelte +9 -6
  61. package/dist/layout/tabs/Tab.svelte.d.ts +1 -1
  62. package/dist/layout/tabs/TabContent.svelte +1 -2
  63. package/dist/layout/tabs/TabContent.svelte.d.ts +1 -1
  64. package/dist/layout/tabs/TabGroup.svelte +10 -5
  65. package/dist/layout/tabs/TabGroup.svelte.d.ts +2 -2
  66. package/dist/layout/tabs/tabs.d.ts +61 -76
  67. package/dist/layout/tabs/tabs.js +170 -28
  68. package/dist/modal/Modal.svelte +3 -3
  69. package/dist/modal/modal.js +3 -3
  70. package/dist/utils/Portal.svelte +108 -0
  71. package/dist/utils/Portal.svelte.d.ts +8 -0
  72. package/dist/utils/dateUtils.d.ts +7 -0
  73. package/dist/utils/dateUtils.js +26 -0
  74. package/dist/variants.d.ts +11 -1
  75. package/dist/variants.js +17 -0
  76. package/package.json +4 -3
  77. package/dist/header/pageheaders.d.ts +0 -10
  78. package/dist/header/pageheaders.js +0 -1
package/README.md CHANGED
@@ -41,7 +41,7 @@ Paste the following CSS import code in `app.css`
41
41
  @source "../node_modules/@makolabs/ripple";
42
42
 
43
43
  @theme {
44
- /* Default (Gray) */
44
+ /* Default (default) */
45
45
  --color-default-50: oklch(0.984 0.003 247.858);
46
46
  --color-default-100: oklch(0.96 0.006 247.858);
47
47
  --color-default-200: oklch(0.91 0.008 247.858);
@@ -0,0 +1,20 @@
1
+ import type { StorageAdapter, FileItem, FileListResult, ImportOptions, ImportResult, ImportStatus, BatchImportResult } from './types.js';
2
+ /**
3
+ * Base storage adapter with common functionality
4
+ */
5
+ export declare abstract class BaseAdapter implements StorageAdapter {
6
+ abstract getName(): string;
7
+ abstract list(path: string, searchQuery?: string): Promise<FileListResult>;
8
+ abstract download(file: FileItem): Promise<string>;
9
+ abstract isConfigured(): Promise<boolean>;
10
+ abstract authenticate?(): Promise<boolean>;
11
+ import(file: FileItem, options: ImportOptions): Promise<ImportResult>;
12
+ getImportStatus(importId: string, fileKey: string): Promise<ImportStatus>;
13
+ batchImport(files: FileItem[], options: ImportOptions): Promise<BatchImportResult>;
14
+ protected abstract getApiPath(): string;
15
+ setReopenFlag(): void;
16
+ shouldReopenBrowser(): boolean;
17
+ clearReopenFlag(): void;
18
+ protected getDetailedStatus(status: string): string;
19
+ protected calculateProgress(status: string, processedRows?: number, totalRows?: number): number;
20
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Base storage adapter with common functionality
3
+ */
4
+ export class BaseAdapter {
5
+ // Common implementation of import and getImportStatus
6
+ async import(file, options) {
7
+ try {
8
+ const endpoint = `/api/${this.getApiPath()}/import`;
9
+ const response = await fetch(endpoint, {
10
+ method: 'POST',
11
+ headers: {
12
+ 'Content-Type': 'application/json'
13
+ },
14
+ body: JSON.stringify({
15
+ key: file.key,
16
+ fileId: file.id, // For Google Drive
17
+ contentType: options.contentType,
18
+ fileFormat: options.fileFormat,
19
+ reference: options.reference || file.name,
20
+ insurer: options.contentType === 'insurer_statement' ? options.insurer : null
21
+ })
22
+ });
23
+ if (!response.ok) {
24
+ const errorData = await response.json();
25
+ throw new Error(errorData.error || 'Failed to import file');
26
+ }
27
+ const result = await response.json();
28
+ return {
29
+ success: true,
30
+ message: `File ${file.name} imported successfully`,
31
+ fileId: result.fileId || result.importResponse?.uuid,
32
+ status: 'pending',
33
+ progress: 10
34
+ };
35
+ }
36
+ catch (err) {
37
+ console.error(`Error importing ${this.getName()} file:`, err);
38
+ return {
39
+ success: false,
40
+ message: `Failed to import file from ${this.getName()}`,
41
+ error: err instanceof Error ? err.message : 'Unknown error'
42
+ };
43
+ }
44
+ }
45
+ async getImportStatus(importId, fileKey) {
46
+ try {
47
+ const response = await fetch(`/api/v1/uploads/${importId}`);
48
+ if (!response.ok) {
49
+ if (response.status === 404) {
50
+ throw new Error(`404: File ${importId} not found on server`);
51
+ }
52
+ throw new Error(`Failed to get file status: ${response.status} ${response.statusText}`);
53
+ }
54
+ const fileData = await response.json();
55
+ return {
56
+ fileId: importId,
57
+ status: fileData.processing_status,
58
+ progress: this.calculateProgress(fileData.processing_status, fileData.processed_rows, fileData.total_rows),
59
+ processedRows: fileData.processed_rows,
60
+ totalRows: fileData.total_rows,
61
+ detailedStatus: this.getDetailedStatus(fileData.processing_status),
62
+ error: fileData.error_details?.message,
63
+ created_at: fileData.created_at
64
+ };
65
+ }
66
+ catch (err) {
67
+ console.error('Error polling file status:', err);
68
+ throw err;
69
+ }
70
+ }
71
+ // Common implementation of batch import
72
+ async batchImport(files, options) {
73
+ // Process files sequentially
74
+ const individualResults = [];
75
+ for (const file of files) {
76
+ try {
77
+ const result = await this.import(file, options);
78
+ individualResults.push(result);
79
+ }
80
+ catch (err) {
81
+ individualResults.push({
82
+ success: false,
83
+ message: `Failed to import ${file.name}`,
84
+ error: err instanceof Error ? err.message : 'Unknown error',
85
+ fileId: file.id
86
+ });
87
+ }
88
+ }
89
+ // Count successes and failures
90
+ const succeeded = individualResults.filter(r => r.success).length;
91
+ const failed = individualResults.length - succeeded;
92
+ // Return batch import result
93
+ return {
94
+ success: failed === 0, // Overall success if all files succeeded
95
+ message: `Imported ${succeeded} of ${individualResults.length} files`,
96
+ summary: {
97
+ total: individualResults.length,
98
+ succeeded,
99
+ failed
100
+ },
101
+ results: individualResults.map(r => ({
102
+ key: r.fileId || '',
103
+ success: r.success,
104
+ fileId: r.fileId,
105
+ error: r.error
106
+ }))
107
+ };
108
+ }
109
+ // Set a flag to reopen the browser after authentication
110
+ setReopenFlag() {
111
+ // Default implementation does nothing
112
+ // Subclasses should override this method to implement storage-specific reopening flags
113
+ }
114
+ // Check if the browser should be reopened
115
+ shouldReopenBrowser() {
116
+ // Default implementation returns false
117
+ // Subclasses should override this method to check their specific reopening flags
118
+ return false;
119
+ }
120
+ // Clear the browser reopening flag
121
+ clearReopenFlag() {
122
+ // Default implementation does nothing
123
+ // Subclasses should override this method to clear their specific reopening flags
124
+ }
125
+ // Helper method to get detailed status
126
+ getDetailedStatus(status) {
127
+ const statusMap = {
128
+ 'pending': 'Waiting to start',
129
+ 'pending_import': 'Preparing import',
130
+ 'initializing': 'Initializing import',
131
+ 'pending_file_retrieval': 'Retrieving file',
132
+ 'retrieving_file': 'Downloading file',
133
+ 'file_retrieved': 'File downloaded',
134
+ 'loading_file_contents_to_db': 'Loading data',
135
+ 'file_contents_loaded': 'Data loaded',
136
+ 'ready_to_process': 'Ready for processing',
137
+ 'validating_source_file_rows': 'Validating file',
138
+ 'converting_to_business_objects': 'Converting data',
139
+ 'processing': 'Processing data',
140
+ 'completed': 'Import completed',
141
+ 'failed': 'Import failed',
142
+ 'partially_processed': 'Partially processed'
143
+ };
144
+ return statusMap[status] || status;
145
+ }
146
+ // Helper method to calculate progress
147
+ calculateProgress(status, processedRows, totalRows) {
148
+ // If we have row counts, use them for more accurate progress
149
+ if (processedRows !== undefined && totalRows && totalRows > 0) {
150
+ return Math.min(95, Math.round((processedRows / totalRows) * 100));
151
+ }
152
+ // Otherwise, use status-based progress estimates
153
+ const progressMap = {
154
+ 'pending': 5,
155
+ 'pending_import': 10,
156
+ 'initializing': 15,
157
+ 'pending_file_retrieval': 20,
158
+ 'retrieving_file': 30,
159
+ 'file_retrieved': 40,
160
+ 'loading_file_contents_to_db': 50,
161
+ 'file_contents_loaded': 60,
162
+ 'ready_to_process': 70,
163
+ 'validating_source_file_rows': 80,
164
+ 'converting_to_business_objects': 90,
165
+ 'completed': 100,
166
+ 'failed': 0,
167
+ 'partially_processed': 95
168
+ };
169
+ return progressMap[status] || 50;
170
+ }
171
+ }
@@ -0,0 +1,21 @@
1
+ import type { FileItem, FileListResult } from './types.js';
2
+ import { BaseAdapter } from './BaseAdapter.js';
3
+ /**
4
+ * S3 storage adapter for the FileBrowser component
5
+ */
6
+ export declare class S3Adapter extends BaseAdapter {
7
+ private basePath;
8
+ constructor(basePath?: string, publicS3BasePath?: string);
9
+ getName(): string;
10
+ isConfigured(): Promise<boolean>;
11
+ authenticate(): Promise<boolean>;
12
+ setReopenFlag(): void;
13
+ shouldReopenBrowser(): boolean;
14
+ clearReopenFlag(): void;
15
+ list(path: string, searchQuery?: string): Promise<FileListResult>;
16
+ download(file: FileItem): Promise<string>;
17
+ private removeFileExtensions;
18
+ private createBreadcrumbs;
19
+ private extractFolderName;
20
+ protected getApiPath(): string;
21
+ }
@@ -0,0 +1,194 @@
1
+ import { BaseAdapter } from './BaseAdapter.js';
2
+ /**
3
+ * S3 storage adapter for the FileBrowser component
4
+ */
5
+ export class S3Adapter extends BaseAdapter {
6
+ basePath;
7
+ constructor(basePath, publicS3BasePath) {
8
+ super();
9
+ this.basePath = basePath || publicS3BasePath || 'export/clarkfin/';
10
+ }
11
+ getName() {
12
+ return 'S3';
13
+ }
14
+ async isConfigured() {
15
+ // S3 should always be configured if the environment variables are set
16
+ return true;
17
+ }
18
+ // S3 does not require authentication, but we implement the method to satisfy the interface
19
+ async authenticate() {
20
+ return true; // S3 is considered authenticated if isConfigured() returns true
21
+ }
22
+ // Override setReopenFlag to store the S3 browser reopening flag
23
+ setReopenFlag() {
24
+ if (typeof localStorage !== 'undefined') {
25
+ localStorage.setItem('reopen_s3_browser', 'true');
26
+ }
27
+ }
28
+ // Check if the S3 browser should be reopened
29
+ shouldReopenBrowser() {
30
+ if (typeof localStorage !== 'undefined') {
31
+ return localStorage.getItem('reopen_s3_browser') === 'true';
32
+ }
33
+ return false;
34
+ }
35
+ // Clear the S3 browser reopening flag
36
+ clearReopenFlag() {
37
+ if (typeof localStorage !== 'undefined') {
38
+ localStorage.removeItem('reopen_s3_browser');
39
+ }
40
+ }
41
+ async list(path, searchQuery) {
42
+ try {
43
+ // Ensure path ends with a forward slash
44
+ const normalizedPath = path.endsWith('/') ? path : path + '/';
45
+ // Build API URL including search term if provided
46
+ let apiUrl = `/api/s3/list?prefix=${encodeURIComponent(normalizedPath)}`;
47
+ if (searchQuery) {
48
+ apiUrl += `&search=${encodeURIComponent(searchQuery)}`;
49
+ }
50
+ // Use the API endpoint for listing S3 files
51
+ const response = await fetch(apiUrl);
52
+ if (!response.ok) {
53
+ const errorData = await response.json();
54
+ throw new Error(errorData.error || 'Failed to fetch files');
55
+ }
56
+ const result = await response.json();
57
+ // Transform folders - use the folderName property from the API
58
+ const folders = (result.folders || [])
59
+ .map((folder) => {
60
+ return {
61
+ key: folder.prefix,
62
+ // Use the folderName from the API if available, otherwise extract it from the prefix
63
+ name: folder.folderName || this.extractFolderName(folder.prefix, normalizedPath),
64
+ lastModified: new Date(),
65
+ size: 0,
66
+ isFolder: true
67
+ };
68
+ })
69
+ .filter((folder) => folder.name); // Filter out empty folder names
70
+ // Transform files - only include files that are directly in this directory
71
+ const fileItems = (result.files || [])
72
+ .filter((file) => {
73
+ // Skip the directory marker itself if it exists as a file
74
+ if (file.key === normalizedPath)
75
+ return false;
76
+ // Get the relative path within the current directory
77
+ const relativePath = file.key.slice(normalizedPath.length);
78
+ // Only include files that don't have additional path separators
79
+ // (indicating they're directly in this directory)
80
+ return !relativePath.includes('/');
81
+ })
82
+ .map((file) => ({
83
+ key: file.key,
84
+ name: this.removeFileExtensions(file.key.slice(normalizedPath.length)),
85
+ lastModified: new Date(file.lastModified),
86
+ size: file.size,
87
+ isFolder: false
88
+ }));
89
+ // Combine folders and files
90
+ const files = [...folders, ...fileItems];
91
+ // Sort by lastModified in reverse order (newest first), but keep folders first
92
+ files.sort((a, b) => {
93
+ // Keep folders first
94
+ if (a.isFolder !== b.isFolder) {
95
+ return a.isFolder ? -1 : 1;
96
+ }
97
+ // Sort by lastModified in reverse order (newest first)
98
+ return b.lastModified.getTime() - a.lastModified.getTime();
99
+ });
100
+ // Create breadcrumbs
101
+ const breadcrumbs = this.createBreadcrumbs(normalizedPath);
102
+ // Determine parent path
103
+ const pathParts = normalizedPath.split('/').filter(Boolean);
104
+ let parentPath = '';
105
+ if (pathParts.length > 0) {
106
+ // Remove the last part and join the rest
107
+ pathParts.pop();
108
+ parentPath = pathParts.length > 0 ? pathParts.join('/') + '/' : this.basePath;
109
+ // Ensure we never navigate outside the basePath
110
+ if (!parentPath.startsWith(this.basePath)) {
111
+ parentPath = this.basePath;
112
+ }
113
+ }
114
+ // Debug log
115
+ console.log(`S3 listing path: ${normalizedPath}, files: ${fileItems.length}, folders: ${folders.length}`);
116
+ if (folders.length > 0) {
117
+ console.log('Sample folder names:', folders.slice(0, 3).map((f) => f.name));
118
+ }
119
+ return {
120
+ files,
121
+ currentPath: normalizedPath,
122
+ parentPath: normalizedPath !== this.basePath ? parentPath : undefined,
123
+ breadcrumbs
124
+ };
125
+ }
126
+ catch (err) {
127
+ console.error('Error fetching S3 files:', err);
128
+ throw err;
129
+ }
130
+ }
131
+ async download(file) {
132
+ try {
133
+ const response = await fetch(`/api/s3/download?key=${encodeURIComponent(file.key)}`);
134
+ if (!response.ok) {
135
+ const errorData = await response.json();
136
+ throw new Error(errorData.error || 'Failed to download file');
137
+ }
138
+ const url = await response.text();
139
+ return url;
140
+ }
141
+ catch (err) {
142
+ console.error('Error downloading S3 file:', err);
143
+ throw err;
144
+ }
145
+ }
146
+ // Helper method to remove file extensions
147
+ removeFileExtensions(filename) {
148
+ // First remove .csv.gz if it exists
149
+ let name = filename.replace(/\.csv\.gz$/, '');
150
+ // Then remove any remaining extension
151
+ return name.replace(/\.[^/.]+$/, '');
152
+ }
153
+ // Helper method to create breadcrumbs
154
+ createBreadcrumbs(path) {
155
+ // If we're at base path, reset breadcrumbs
156
+ if (path === this.basePath) {
157
+ return [{ name: 'S3', path: this.basePath, current: true, clickable: true }];
158
+ }
159
+ // Extract parts for display
160
+ const pathWithoutBase = path.startsWith(this.basePath) ? path.slice(this.basePath.length) : '';
161
+ // Add base path as the first item
162
+ let breadcrumbs = [{ name: 'S3', path: this.basePath, current: false, clickable: true }];
163
+ // Add current directory parts
164
+ if (pathWithoutBase) {
165
+ const parts = pathWithoutBase.split('/').filter(Boolean);
166
+ let currentPath = this.basePath;
167
+ parts.forEach((part, index) => {
168
+ currentPath += part + '/';
169
+ breadcrumbs.push({
170
+ name: part,
171
+ path: currentPath,
172
+ current: index === parts.length - 1,
173
+ clickable: true
174
+ });
175
+ });
176
+ }
177
+ return breadcrumbs;
178
+ }
179
+ // Helper method to extract folder name from a prefix
180
+ extractFolderName(prefix, currentPath) {
181
+ // If the prefix starts with the current path, extract the next segment
182
+ if (prefix.startsWith(currentPath)) {
183
+ const relativePath = prefix.slice(currentPath.length);
184
+ return relativePath.split('/')[0];
185
+ }
186
+ // Fallback: use the last non-empty segment before the trailing slash
187
+ const segments = prefix.split('/').filter(Boolean);
188
+ return segments.length > 0 ? segments[segments.length - 1] : '';
189
+ }
190
+ // Implement the abstract method from BaseAdapter
191
+ getApiPath() {
192
+ return 's3';
193
+ }
194
+ }
@@ -0,0 +1,3 @@
1
+ export * from './types.js';
2
+ export * from './BaseAdapter.js';
3
+ export * from './S3Adapter.js';
@@ -0,0 +1,3 @@
1
+ export * from './types.js';
2
+ export * from './BaseAdapter.js';
3
+ export * from './S3Adapter.js';
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Storage adapter interfaces for the unified FileBrowser component
3
+ */
4
+ export interface FileItem {
5
+ key: string;
6
+ name: string;
7
+ lastModified: Date;
8
+ createdAt?: Date;
9
+ size: number;
10
+ isFolder: boolean;
11
+ id?: string;
12
+ mimeType?: string;
13
+ parentId?: string;
14
+ }
15
+ export interface FileActionSingle {
16
+ label: (file: FileItem) => string;
17
+ action: (file: FileItem) => void | Promise<void>;
18
+ isAllowed: (file: FileItem) => boolean;
19
+ batchAction?: never;
20
+ }
21
+ export interface FileActionBatch {
22
+ label: (files: FileItem[]) => string;
23
+ batchAction: (files: FileItem[]) => void | Promise<void>;
24
+ isAllowed: (files: FileItem[]) => boolean;
25
+ action?: never;
26
+ }
27
+ export type FileAction = FileActionSingle | FileActionBatch;
28
+ export interface Breadcrumb {
29
+ name: string;
30
+ path: string;
31
+ id?: string;
32
+ current: boolean;
33
+ clickable: boolean;
34
+ }
35
+ export interface FileListResult {
36
+ files: FileItem[];
37
+ currentPath: string;
38
+ parentPath?: string;
39
+ breadcrumbs?: Breadcrumb[];
40
+ }
41
+ export interface ImportOptions {
42
+ contentType: string;
43
+ fileFormat: string;
44
+ reference?: string;
45
+ insurer?: string;
46
+ }
47
+ export interface ImportResult {
48
+ success: boolean;
49
+ message: string;
50
+ fileId?: string;
51
+ error?: string;
52
+ status?: string;
53
+ progress?: number;
54
+ }
55
+ export interface BatchImportResult {
56
+ success: boolean;
57
+ message: string;
58
+ summary: {
59
+ total: number;
60
+ succeeded: number;
61
+ failed: number;
62
+ };
63
+ results: {
64
+ key: string;
65
+ success: boolean;
66
+ fileId?: string;
67
+ error?: string;
68
+ }[];
69
+ }
70
+ export interface ImportStatus {
71
+ fileId: string;
72
+ status: string;
73
+ progress: number;
74
+ processedRows?: number;
75
+ totalRows?: number;
76
+ detailedStatus?: string;
77
+ error?: string;
78
+ created_at?: string;
79
+ }
80
+ export interface ErrorReportOptions {
81
+ errorType: string;
82
+ errorMessage: string;
83
+ errorDetails?: string;
84
+ clientInfo?: Record<string, string>;
85
+ userAction?: string;
86
+ }
87
+ type URLString = string;
88
+ export interface StorageAdapter {
89
+ getName(): string;
90
+ list(path: string, searchQuery?: string): Promise<FileListResult>;
91
+ download(file: FileItem): Promise<URLString>;
92
+ import(file: FileItem, options: ImportOptions): Promise<ImportResult>;
93
+ batchImport(files: FileItem[], options: ImportOptions): Promise<BatchImportResult>;
94
+ getImportStatus(importId: string, fileKey: string): Promise<ImportStatus>;
95
+ reportError?(importId: string, options: ErrorReportOptions): Promise<boolean>;
96
+ isConfigured(): Promise<boolean>;
97
+ authenticate?(): Promise<boolean>;
98
+ setReopenFlag(): void;
99
+ shouldReopenBrowser(): boolean;
100
+ clearReopenFlag(): void;
101
+ }
102
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Storage adapter interfaces for the unified FileBrowser component
3
+ */
4
+ export {};
@@ -26,7 +26,13 @@
26
26
  } from 'echarts/components';
27
27
  // @ts-expect-error - ECharts types are not available
28
28
  import { SVGRenderer } from 'echarts/renderers';
29
- import type { ChartProps, ChartColors, ChartColorString } from '../index.js';
29
+ import {
30
+ type ChartProps,
31
+ type ChartColorString,
32
+ type ChartColors,
33
+ type SeriesConfig
34
+ } from '../index.js';
35
+ import { defaultChartColors } from '../variants.js';
30
36
 
31
37
  // @ts-expect-error - ECharts types are not available
32
38
  echarts.use([
@@ -104,31 +110,54 @@
104
110
  let chart: echarts.ECharts;
105
111
  let resizeObserver: ResizeObserver;
106
112
 
107
- const defaultChartColors: ChartColors = {
108
- health: '#1F69FF',
109
- property: '#2D9D78',
110
- auto: '#E8A317',
111
- life: '#E34974',
112
- other: '#7B3FE4',
113
- default: '#6B7280'
114
- };
115
-
116
113
  const chartColors: ChartColors = { ...defaultChartColors, ...colors };
114
+ const baseColors = [
115
+ chartColors.health,
116
+ chartColors.other,
117
+ chartColors.property,
118
+ chartColors.auto,
119
+ chartColors.life,
120
+ chartColors.default
121
+ ];
117
122
 
118
123
  function getColor(color?: ChartColorString): string | undefined {
119
124
  return color ? chartColors[color as keyof ChartColors] || color : undefined;
120
125
  }
121
126
 
122
- function getSeriesColors(count: number): string[] {
123
- const baseColors = [
124
- chartColors.health,
125
- chartColors.other,
126
- chartColors.property,
127
- chartColors.auto,
128
- chartColors.life,
129
- chartColors.default
130
- ];
127
+ function getAreaStyle(seriesConfig: SeriesConfig<any>) {
128
+ if (!seriesConfig.color) return undefined;
129
+ if (!seriesConfig.showArea) return undefined;
130
+
131
+ const baseLinearAreaStyle = {
132
+ type: 'linear',
133
+ x: 0,
134
+ y: 0,
135
+ x2: 0,
136
+ y2: 1
137
+ };
138
+ const color = getColor(seriesConfig.color);
139
+ const opacity = seriesConfig.areaOpacity || 0.2;
140
+ const opacityHex = Math.floor(opacity * 255)
141
+ .toString(16)
142
+ .padStart(2, '0');
143
+ return {
144
+ color: {
145
+ ...baseLinearAreaStyle,
146
+ colorStops: [
147
+ {
148
+ offset: 0,
149
+ color: color + opacityHex
150
+ },
151
+ {
152
+ offset: 1,
153
+ color: color + '00'
154
+ }
155
+ ]
156
+ }
157
+ };
158
+ }
131
159
 
160
+ function getSeriesColors(count: number): string[] {
132
161
  let colors = [...baseColors];
133
162
  while (colors.length < count) {
134
163
  const index = colors.length % baseColors.length;
@@ -228,29 +257,7 @@
228
257
  color: getColor(seriesConfig.color),
229
258
  type: seriesConfig.lineStyle?.type || 'solid'
230
259
  },
231
- areaStyle: seriesConfig.showArea
232
- ? {
233
- color: {
234
- type: 'linear',
235
- x: 0,
236
- y: 0,
237
- x2: 0,
238
- y2: 1,
239
- colorStops: [
240
- {
241
- offset: 0,
242
- color:
243
- getColor(seriesConfig.color) +
244
- Math.floor((seriesConfig.areaOpacity || 0.2) * 255).toString(16)
245
- },
246
- {
247
- offset: 1,
248
- color: getColor(seriesConfig.color) + '00'
249
- }
250
- ]
251
- }
252
- }
253
- : undefined
260
+ areaStyle: getAreaStyle(seriesConfig)
254
261
  }),
255
262
 
256
263
  ...(actualType === 'bar' && {
@@ -422,11 +429,19 @@
422
429
  }),
423
430
  formatter: xAxis.format || '{value}'
424
431
  },
432
+ boundaryGap: series.every((s) => s.type === 'line')
433
+ ? false
434
+ : series.some((s) => s.type === 'bar' || s.type === 'horizontal-bar')
435
+ ? true
436
+ : undefined,
425
437
  name: xAxis.label,
426
438
  nameLocation: 'middle',
427
439
  nameGap: 30,
428
440
  splitLine: {
429
441
  show: grid.vertical || false
442
+ },
443
+ axisLine: xAxis.axisLine || {
444
+ show: false
430
445
  }
431
446
  },
432
447
 
@@ -439,11 +454,8 @@
439
454
  position: axis.position || (index === 0 ? 'left' : 'right'),
440
455
  min: axis.min,
441
456
  max: axis.max,
442
- axisLine: {
443
- show: true,
444
- lineStyle: {
445
- color: getColor(axis.color)
446
- }
457
+ axisLine: axis.axisLine || {
458
+ show: false
447
459
  },
448
460
  axisLabel: {
449
461
  // @ts-expect-error - ECharts types are not available