gridjs-spreadsheet 26.6.1 → 26.7.0

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 (61) hide show
  1. package/{a08275ef8caf83cc1c2eae91f17663e8.svg → 1f345d060b26fa53bc9d78c36441afd6.svg} +106 -26
  2. package/angular.d.ts +67 -0
  3. package/angular.js +174 -0
  4. package/example/README.md +242 -41
  5. package/example/angular-gridjs/.nvmrc +1 -0
  6. package/example/angular-gridjs/angular.json +66 -0
  7. package/example/angular-gridjs/package.json +31 -0
  8. package/example/angular-gridjs/proxy.conf.json +12 -0
  9. package/example/angular-gridjs/src/app/api.ts +7 -0
  10. package/example/angular-gridjs/src/app/app.component.html +57 -0
  11. package/example/angular-gridjs/src/app/app.component.ts +153 -0
  12. package/example/angular-gridjs/src/app/routing.ts +52 -0
  13. package/example/angular-gridjs/src/favicon.ico +0 -0
  14. package/example/angular-gridjs/src/index.html +12 -0
  15. package/example/angular-gridjs/src/main.ts +7 -0
  16. package/example/angular-gridjs/src/styles.css +37 -0
  17. package/example/angular-gridjs/tsconfig.app.json +9 -0
  18. package/example/angular-gridjs/tsconfig.json +29 -0
  19. package/example/pom.xml +3 -3
  20. package/example/react-gridjs/index.html +12 -0
  21. package/example/react-gridjs/package.json +20 -0
  22. package/example/react-gridjs/src/App.jsx +188 -0
  23. package/example/react-gridjs/src/api.js +7 -0
  24. package/example/react-gridjs/src/main.jsx +9 -0
  25. package/example/react-gridjs/src/routing.js +51 -0
  26. package/example/react-gridjs/src/styles.css +37 -0
  27. package/example/react-gridjs/vite.config.js +16 -0
  28. package/example/src/main/java/com/aspose/gridjs/demo/controller/DataController.java +80 -2
  29. package/example/src/main/java/com/aspose/gridjs/demo/controller/GridJs2Controller.java +12 -0
  30. package/example/vanilla-gridjs/npm/index.html +29 -0
  31. package/example/vanilla-gridjs/npm/main.js +8 -0
  32. package/example/vanilla-gridjs/npm/package.json +16 -0
  33. package/example/vanilla-gridjs/npm/vite.config.js +11 -0
  34. package/example/vanilla-gridjs/script/index.html +32 -0
  35. package/example/vanilla-gridjs/script/main.js +9 -0
  36. package/example/vanilla-gridjs/script/package.json +12 -0
  37. package/example/vanilla-gridjs/script/vite.config.js +11 -0
  38. package/example/vanilla-gridjs/shared/demo-app.js +205 -0
  39. package/example/vanilla-gridjs/shared/styles.css +36 -0
  40. package/example/vue-gridjs/index.html +12 -0
  41. package/example/vue-gridjs/package.json +19 -0
  42. package/example/vue-gridjs/src/App.vue +199 -0
  43. package/example/vue-gridjs/src/api.js +7 -0
  44. package/example/vue-gridjs/src/main.js +9 -0
  45. package/example/vue-gridjs/src/routing.js +51 -0
  46. package/example/vue-gridjs/src/styles.css +37 -0
  47. package/example/vue-gridjs/vite.config.js +21 -0
  48. package/index.d.ts +528 -472
  49. package/index.js +1 -1
  50. package/package.json +143 -50
  51. package/react.d.ts +27 -0
  52. package/react.js +111 -0
  53. package/readme.md +183 -380
  54. package/shared.d.ts +66 -0
  55. package/shared.js +204 -0
  56. package/vue.d.ts +20 -0
  57. package/vue.js +125 -0
  58. package/xspreadsheet.css +365 -1
  59. package/xspreadsheet.js +5 -5
  60. package/example/.mvn/wrapper/maven-wrapper.properties +0 -18
  61. package/example/src/test/java/com/aspose/gridjs/demo/GridjsdemoApplicationTests.java +0 -1441
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="description" content="Use gridjs-spreadsheet from npm with Vanilla HTML and Java.">
7
+ <title>GridJS Vanilla npm demo</title>
8
+ </head>
9
+ <body>
10
+ <div id="start-page" class="demo-shell">
11
+ <header class="demo-navbar"><a href="https://docs.aspose.com/cells/java/aspose-cells-gridjs/">Aspose.Cells.GridJs Vanilla npm demo</a></header>
12
+ <main class="demo-container">
13
+ <h1 class="visually-hidden">GridJS Vanilla npm demo</h1>
14
+ <fieldset class="demo-selector">
15
+ <legend>Select a demo:</legend>
16
+ <label class="radio-card active"><input type="radio" name="demoType" value="permanent" checked><span>permanent url load demo</span></label>
17
+ <label class="radio-card"><input type="radio" name="demoType" value="highlight"><span>highlight and custom context menu demo</span></label>
18
+ </fieldset>
19
+ <section class="upload-block"><h2>Upload a local workbook</h2><input id="upload-input" type="file" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"></section>
20
+ <section class="path-block"><h2>Workbook directory: <span id="directory"></span></h2><h2>Choose a workbook to open</h2></section>
21
+ <p id="status-message" class="status-message" role="status"></p>
22
+ <section id="file-list" class="file-list" aria-label="Workbook files"></section>
23
+ </main>
24
+ <footer class="demo-footer">© 2026 · <a href="https://products.aspose.com/cells/java">Aspose.Cells for Java</a></footer>
25
+ </div>
26
+ <main id="editor-page" class="editor-page" hidden><div id="editor-status" class="editor-status" role="status"></div><div id="grid-host"></div></main>
27
+ <script type="module" src="/main.js"></script>
28
+ </body>
29
+ </html>
@@ -0,0 +1,8 @@
1
+ import xSpreadsheet from 'gridjs-spreadsheet';
2
+ import JSZip from 'jszip';
3
+ import 'gridjs-spreadsheet/xspreadsheet.css';
4
+ import '../shared/styles.css';
5
+ import { startGridJsDemo } from '../shared/demo-app.js';
6
+
7
+ window.JSZip = JSZip;
8
+ startGridJsDemo({ clientName: 'vanilla-npm', createSpreadsheet: xSpreadsheet });
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "gridjs-vanilla-npm-java-demo",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite --host 127.0.0.1 --port 5175 --strictPort",
7
+ "build": "vite build"
8
+ },
9
+ "dependencies": {
10
+ "gridjs-spreadsheet": "latest",
11
+ "jszip": "^3.10.1"
12
+ },
13
+ "devDependencies": {
14
+ "vite": "latest"
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'vite';
2
+
3
+ export default defineConfig({
4
+ server: {
5
+ fs: { allow: ['..'] },
6
+ proxy: {
7
+ '/GridJs2': 'http://127.0.0.1:8080',
8
+ '/gridjsdemo': 'http://127.0.0.1:8080',
9
+ },
10
+ },
11
+ });
@@ -0,0 +1,32 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="description" content="Use gridjs-spreadsheet directly with script tags and a Java backend.">
7
+ <title>GridJS Vanilla script demo</title>
8
+ <link rel="stylesheet" href="https://unpkg.com/gridjs-spreadsheet@latest/xspreadsheet.css">
9
+ <script src="https://unpkg.com/jszip@3.10.1/dist/jszip.min.js"></script>
10
+ <script src="https://unpkg.com/gridjs-spreadsheet@latest/xspreadsheet.js"></script>
11
+ </head>
12
+ <body>
13
+ <div id="start-page" class="demo-shell">
14
+ <header class="demo-navbar"><a href="https://docs.aspose.com/cells/java/aspose-cells-gridjs/">Aspose.Cells.GridJs direct script demo</a></header>
15
+ <main class="demo-container">
16
+ <h1 class="visually-hidden">GridJS Vanilla direct script demo</h1>
17
+ <fieldset class="demo-selector">
18
+ <legend>Select a demo:</legend>
19
+ <label class="radio-card active"><input type="radio" name="demoType" value="permanent" checked><span>permanent url load demo</span></label>
20
+ <label class="radio-card"><input type="radio" name="demoType" value="highlight"><span>highlight and custom context menu demo</span></label>
21
+ </fieldset>
22
+ <section class="upload-block"><h2>Upload a local workbook</h2><input id="upload-input" type="file" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"></section>
23
+ <section class="path-block"><h2>Workbook directory: <span id="directory"></span></h2><h2>Choose a workbook to open</h2></section>
24
+ <p id="status-message" class="status-message" role="status"></p>
25
+ <section id="file-list" class="file-list" aria-label="Workbook files"></section>
26
+ </main>
27
+ <footer class="demo-footer">© 2026 · <a href="https://products.aspose.com/cells/java">Aspose.Cells for Java</a></footer>
28
+ </div>
29
+ <main id="editor-page" class="editor-page" hidden><div id="editor-status" class="editor-status" role="status"></div><div id="grid-host"></div></main>
30
+ <script type="module" src="/main.js"></script>
31
+ </body>
32
+ </html>
@@ -0,0 +1,9 @@
1
+ import { startGridJsDemo } from '../shared/demo-app.js';
2
+ import '../shared/styles.css';
3
+
4
+ if (typeof window.x_spreadsheet !== 'function') {
5
+ document.querySelector('#status-message').textContent = 'GridJS failed to load from the script URL.';
6
+ document.querySelector('#status-message').classList.add('status-error');
7
+ } else {
8
+ startGridJsDemo({ clientName: 'vanilla-script', createSpreadsheet: window.x_spreadsheet });
9
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "gridjs-vanilla-script-java-demo",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite --host 127.0.0.1 --port 5176 --strictPort",
7
+ "build": "vite build"
8
+ },
9
+ "devDependencies": {
10
+ "vite": "latest"
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'vite';
2
+
3
+ export default defineConfig({
4
+ server: {
5
+ fs: { allow: ['..'] },
6
+ proxy: {
7
+ '/GridJs2': 'http://127.0.0.1:8080',
8
+ '/gridjsdemo': 'http://127.0.0.1:8080',
9
+ },
10
+ },
11
+ });
@@ -0,0 +1,205 @@
1
+ const DEMO_PERMANENT = 'permanent';
2
+ const DEMO_HIGHLIGHT = 'highlight';
3
+
4
+ async function fetchJson(url, options) {
5
+ const response = await fetch(url, options);
6
+ if (!response.ok) {
7
+ const message = await response.text();
8
+ throw new Error(message || `${response.status} ${response.statusText}`);
9
+ }
10
+ return response.json();
11
+ }
12
+
13
+ function normalizeUid(clientName, file) {
14
+ return `${clientName}-uid-${file.replace(/[^a-zA-Z0-9]/g, '-')}`;
15
+ }
16
+
17
+ function readRoute(clientName) {
18
+ const params = new URLSearchParams(window.location.search);
19
+ const file = params.get('file');
20
+ if (!file) return null;
21
+ const demo = params.get('demo') === DEMO_HIGHLIGHT ? DEMO_HIGHLIGHT : DEMO_PERMANENT;
22
+ return {
23
+ file,
24
+ storedFile: params.get('storedFile') || file,
25
+ demo,
26
+ uid: params.get('uid') || normalizeUid(clientName, file),
27
+ fromUpload: params.get('fromUpload') || '',
28
+ };
29
+ }
30
+
31
+ function pushRoute(workbook) {
32
+ const params = new URLSearchParams({ file: workbook.file, demo: workbook.demo });
33
+ if (workbook.storedFile && workbook.storedFile !== workbook.file) params.set('storedFile', workbook.storedFile);
34
+ if (workbook.demo === DEMO_PERMANENT || workbook.fromUpload) params.set('uid', workbook.uid);
35
+ if (workbook.fromUpload) params.set('fromUpload', workbook.fromUpload);
36
+ window.history.pushState(null, '', `/?${params}`);
37
+ }
38
+
39
+ function workbookEndpoint(workbook) {
40
+ const params = new URLSearchParams({ filename: workbook.storedFile || workbook.file });
41
+ if (workbook.fromUpload) {
42
+ params.set('uid', workbook.uid);
43
+ return `/GridJs2/DetailStreamJsonWithUidFromUpload?${params}`;
44
+ }
45
+ if (workbook.demo === DEMO_PERMANENT) {
46
+ params.set('uid', workbook.uid);
47
+ return `/GridJs2/DetailStreamJsonWithUid?${params}`;
48
+ }
49
+ return `/GridJs2/DetailStreamJson?${params}`;
50
+ }
51
+
52
+ export function startGridJsDemo({ clientName, createSpreadsheet }) {
53
+ const startPage = document.querySelector('#start-page');
54
+ const editorPage = document.querySelector('#editor-page');
55
+ const editorStatus = document.querySelector('#editor-status');
56
+ const status = document.querySelector('#status-message');
57
+ const directory = document.querySelector('#directory');
58
+ const fileList = document.querySelector('#file-list');
59
+ const uploadInput = document.querySelector('#upload-input');
60
+ let current = readRoute(clientName);
61
+ let spreadsheet = null;
62
+
63
+ const selectedDemo = () => document.querySelector('input[name="demoType"]:checked')?.value || DEMO_PERMANENT;
64
+
65
+ function setStatus(message, isError = false) {
66
+ status.textContent = message;
67
+ status.classList.toggle('status-error', isError);
68
+ }
69
+
70
+ function updateDemoSelection(demo) {
71
+ const input = document.querySelector(`input[name="demoType"][value="${demo}"]`);
72
+ if (input) input.checked = true;
73
+ document.querySelectorAll('.radio-card').forEach((label) => {
74
+ label.classList.toggle('active', label.contains(input));
75
+ });
76
+ }
77
+
78
+ async function loadFiles() {
79
+ setStatus('Loading workbook list...');
80
+ try {
81
+ const result = await fetchJson('/gridjsdemo/api/files');
82
+ directory.textContent = result.directory || '';
83
+ fileList.replaceChildren();
84
+ if (!result.files?.length) {
85
+ const empty = document.createElement('p');
86
+ empty.className = 'empty-message';
87
+ empty.textContent = 'No workbooks found in the configured directory.';
88
+ fileList.append(empty);
89
+ } else {
90
+ result.files.forEach((file) => {
91
+ const button = document.createElement('button');
92
+ button.type = 'button';
93
+ button.className = 'file-item';
94
+ button.textContent = file;
95
+ button.addEventListener('click', () => openWorkbook({
96
+ file,
97
+ storedFile: file,
98
+ demo: selectedDemo(),
99
+ uid: normalizeUid(clientName, file),
100
+ fromUpload: '',
101
+ }));
102
+ fileList.append(button);
103
+ });
104
+ }
105
+ setStatus('Ready');
106
+ } catch (error) {
107
+ setStatus(`Failed to load files: ${error.message}`, true);
108
+ }
109
+ }
110
+
111
+ async function mountWorkbook() {
112
+ if (!current) {
113
+ editorPage.hidden = true;
114
+ startPage.hidden = false;
115
+ return;
116
+ }
117
+
118
+ startPage.hidden = true;
119
+ editorPage.hidden = false;
120
+ editorStatus.hidden = false;
121
+ editorStatus.textContent = `Loading ${current.file}...`;
122
+ try {
123
+ const payload = await fetchJson(workbookEndpoint(current));
124
+ if (payload.Error) throw new Error(payload.Error);
125
+ payload.filename = current.file;
126
+ document.querySelector('#grid-host').replaceChildren();
127
+
128
+ const options = {
129
+ updateMode: 'server',
130
+ updateUrl: '/GridJs2/UpdateCell',
131
+ showToolbar: true,
132
+ showContextmenu: true,
133
+ mode: 'edit',
134
+ local: 'en',
135
+ };
136
+ spreadsheet = createSpreadsheet('#grid-host', options).loadData(payload.data, payload.actname);
137
+ spreadsheet.setUniqueId?.(payload.uniqueid);
138
+ spreadsheet.setFileName?.(current.file);
139
+ spreadsheet.setImageInfo?.(
140
+ '/GridJs2/ImageUrl',
141
+ '/GridJs2/AddImage',
142
+ '/GridJs2/AddImageByURL',
143
+ '/GridJs2/CopyImage',
144
+ 5678,
145
+ );
146
+ spreadsheet.setFileDownloadInfo?.('/GridJs2/Download');
147
+ spreadsheet.setOleDownloadInfo?.('/GridJs2/Ole');
148
+ spreadsheet.setLazyLoadingUrl?.('/GridJs2/LazyLoadingStreamJson');
149
+ spreadsheet.setOpenFileUrl?.('/');
150
+ spreadsheet.setActiveSheetByName?.(payload.actname)?.setActiveCell?.(payload.actrow, payload.actcol);
151
+ spreadsheet.change?.((data) => console.log('[GridJS Event] changed', data));
152
+ spreadsheet.on?.('cell-selected', (_, row, column) => console.log('[GridJS Event] cell selected', row, column));
153
+ spreadsheet.on?.('sheet-selected', (_, name) => console.log('[GridJS Event] sheet selected', name));
154
+ spreadsheet.updateCellError?.((message) => console.error('[GridJS update error]', message));
155
+ editorStatus.hidden = true;
156
+ } catch (error) {
157
+ editorStatus.hidden = false;
158
+ editorStatus.textContent = `Workbook load failed: ${error.message}`;
159
+ editorStatus.classList.add('status-error');
160
+ }
161
+ }
162
+
163
+ function openWorkbook(workbook) {
164
+ current = workbook;
165
+ pushRoute(workbook);
166
+ void mountWorkbook();
167
+ }
168
+
169
+ document.querySelectorAll('input[name="demoType"]').forEach((input) => {
170
+ input.addEventListener('change', () => updateDemoSelection(input.value));
171
+ });
172
+
173
+ uploadInput.addEventListener('change', async () => {
174
+ const file = uploadInput.files?.[0];
175
+ if (!file) return;
176
+ const form = new FormData();
177
+ form.append('file', file);
178
+ form.append('client', clientName);
179
+ setStatus(`Uploading ${file.name}...`);
180
+ try {
181
+ const result = await fetchJson('/gridjsdemo/api/upload', { method: 'POST', body: form });
182
+ openWorkbook({
183
+ file: result.displayName || file.name,
184
+ storedFile: result.file,
185
+ demo: selectedDemo(),
186
+ uid: result.uid || normalizeUid(clientName, result.file),
187
+ fromUpload: '1',
188
+ });
189
+ } catch (error) {
190
+ setStatus(`Upload failed: ${error.message}`, true);
191
+ } finally {
192
+ uploadInput.value = '';
193
+ }
194
+ });
195
+
196
+ window.addEventListener('popstate', () => {
197
+ current = readRoute(clientName);
198
+ if (current) updateDemoSelection(current.demo);
199
+ void mountWorkbook();
200
+ });
201
+
202
+ updateDemoSelection(current?.demo || DEMO_PERMANENT);
203
+ void loadFiles();
204
+ void mountWorkbook();
205
+ }
@@ -0,0 +1,36 @@
1
+ :root { font-family: Arial, Helvetica, sans-serif; color: #212529; background: #f8f9fa; }
2
+ * { box-sizing: border-box; }
3
+ html, body { min-height: 100%; }
4
+ body { margin: 0; overflow-y: hidden; background: #f8f9fa; font-size: 14px; }
5
+ button, input { font: inherit; }
6
+ [hidden] { display: none !important; }
7
+ .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
8
+ .demo-shell { min-height: 100dvh; display: flex; flex-direction: column; background: #f8f9fa; }
9
+ .demo-navbar { min-height: 48px; display: flex; align-items: center; justify-content: center; background: #fff; border-bottom: 1px solid #dee2e6; padding: 8px 12px; font-size: 19px; }
10
+ .demo-navbar a, .demo-footer a { color: #0645ad; text-decoration: underline; }
11
+ .demo-container { flex: 1; width: 100%; max-width: 960px; margin: 0 auto; padding: 20px 16px; }
12
+ .demo-selector { border: 1px solid #adb5bd; border-radius: 6px; margin: 0 0 18px; padding: 18px 14px 14px; display: flex; align-items: center; }
13
+ .demo-selector legend { width: auto; padding: 0 6px; font-weight: 600; }
14
+ .radio-card { min-height: 40px; display: inline-flex; align-items: center; gap: 8px; padding: 0 14px; margin-left: -1px; border: 1px solid #adb5bd; background: #f0f0f0; color: #343a40; cursor: pointer; transition: background-color 180ms ease, color 180ms ease; }
15
+ .radio-card:first-of-type { margin-left: 0; border-radius: 5px 0 0 5px; }
16
+ .radio-card:last-of-type { border-radius: 0 5px 5px 0; }
17
+ .radio-card.active { background: #0b63ce; border-color: #0b63ce; color: #fff; z-index: 1; }
18
+ .radio-card:focus-within, .file-item:focus-visible, input:focus-visible, a:focus-visible { outline: 3px solid rgba(11, 99, 206, .35); outline-offset: 2px; }
19
+ .upload-block h2, .path-block h2 { margin: 0 0 10px; font-size: 14px; line-height: 1.4; }
20
+ .upload-block input[type=file] { min-height: 44px; margin-bottom: 12px; }
21
+ .path-block { margin-top: 4px; }
22
+ .status-message { min-height: 20px; margin: 8px 0; color: #495057; }
23
+ .status-error { color: #b42318 !important; }
24
+ .file-list { min-height: 140px; max-height: 260px; overflow-y: auto; border: 1px solid #ced4da; border-radius: 6px; background: #f6f7ef; padding: 8px; }
25
+ .file-item { display: block; width: 100%; min-height: 40px; border: 0; border-radius: 4px; padding: 8px; background: transparent; color: #0645ad; text-align: left; text-decoration: underline; cursor: pointer; }
26
+ .file-item:hover { background: #e7eef8; }
27
+ .empty-message { margin: 16px; color: #5f6b76; }
28
+ .demo-footer { min-height: 44px; border-top: 1px solid #dee2e6; display: flex; align-items: center; justify-content: center; padding: 8px 12px; color: #495057; }
29
+ .editor-page { height: 100dvh; width: 100vw; overflow: hidden; background: #fff; position: relative; }
30
+ #grid-host { width: 100%; height: 100%; }
31
+ .editor-status { position: absolute; inset: 0; z-index: 2; display: grid; place-items: center; padding: 24px; color: #5f6b76; background: #fff; }
32
+ @media (max-width: 700px) {
33
+ body { overflow-y: auto; }
34
+ .demo-selector { flex-direction: column; align-items: stretch; }
35
+ .radio-card { margin-left: 0; width: 100%; border-radius: 5px !important; }
36
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>GridJS Vue Demo</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.js"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "gridjs-vue-java-demo",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite --host 127.0.0.1 --port 5174 --strictPort",
7
+ "build": "vite build",
8
+ "preview": "vite preview --host 127.0.0.1 --port 6174 --strictPort"
9
+ },
10
+ "dependencies": {
11
+ "gridjs-spreadsheet": "latest",
12
+ "jszip": "^3.10.1",
13
+ "vue": "latest"
14
+ },
15
+ "devDependencies": {
16
+ "@vitejs/plugin-vue": "latest",
17
+ "vite": "latest"
18
+ }
19
+ }
@@ -0,0 +1,199 @@
1
+ <script setup>
2
+ // Root demo component.
3
+ // Renders either:
4
+ // - the start page (demo picker + sample file list + upload box), or
5
+ // - the full-page spreadsheet editor once a workbook has been selected.
6
+ // Which one is shown is driven entirely by the URL (see ./routing.js).
7
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
8
+ import { GridJsSpreadsheet } from 'gridjs-spreadsheet/vue';
9
+ import { DEMO_PERMANENT, DEMO_HIGHLIGHT, makeUid, readRoute, pushWorkbookRoute } from './routing';
10
+ import { fetchJson } from './api';
11
+
12
+ // Sample workbooks listed on the start page.
13
+ const files = ref([]);
14
+ const directory = ref('');
15
+ // The workbook selected via the URL; null means "show the start page".
16
+ const current = ref(readRoute());
17
+ // Which demo mode is active (only relevant while on the start page).
18
+ const demoType = ref(current.value?.demo || DEMO_PERMANENT);
19
+ const workbookData = ref(null);
20
+ const status = ref('Loading workbook list...');
21
+ const hasWorkbook = computed(() => Boolean(current.value));
22
+ // Forces the <GridJsSpreadsheet> to remount whenever a different workbook is loaded.
23
+ const workbookKey = computed(() => (workbookData.value?.uniqueid || workbookData.value?.filename || '') + (current.value?.demo || ''));
24
+
25
+ function logEvent(message) {
26
+ console.log('[GridJS Event]', message);
27
+ }
28
+
29
+ // Loads the list of sample workbooks available on the server.
30
+ async function loadFiles() {
31
+ try {
32
+ const result = await fetchJson('/gridjsdemo/api/files');
33
+ files.value = result.files || [];
34
+ directory.value = result.directory || '';
35
+ status.value = 'Ready';
36
+ } catch (error) {
37
+ status.value = 'Failed to load files: ' + error.message;
38
+ }
39
+ }
40
+
41
+ // Navigates to a workbook: updates the URL and local state together.
42
+ function openWorkbook(workbook) {
43
+ const normalized = { ...workbook, demo: workbook.demo || demoType.value };
44
+ pushWorkbookRoute(normalized);
45
+ current.value = normalized;
46
+ demoType.value = normalized.demo;
47
+ }
48
+
49
+ function openFile(file) {
50
+ openWorkbook({ file, storedFile: file, demo: demoType.value, uid: makeUid(file), fromUpload: '' });
51
+ }
52
+
53
+ // Fetches workbook data for the GridJsSpreadsheet component whenever `current`
54
+ // changes. Which endpoint is used depends on the demo mode:
55
+ // - permanent: uid-based endpoint, backend caches parsed data per uid.
56
+ // - highlight: plain endpoint, re-parses the file on every load.
57
+ async function loadWorkbook() {
58
+ if (!current.value?.file) {
59
+ workbookData.value = null;
60
+ return;
61
+ }
62
+ workbookData.value = null;
63
+ status.value = 'Loading ' + current.value.file + '...';
64
+ try {
65
+ const demo = current.value.demo || DEMO_PERMANENT;
66
+ const params = new URLSearchParams({
67
+ filename: current.value.storedFile || current.value.file,
68
+ fromUpload: current.value.fromUpload || '',
69
+ });
70
+ let endpoint = '/GridJs2/DetailStreamJson';
71
+ if (current.value.fromUpload) {
72
+ params.set('uid', current.value.uid || makeUid(current.value.file));
73
+ endpoint = '/GridJs2/DetailStreamJsonWithUidFromUpload';
74
+ } else if (demo === DEMO_PERMANENT) {
75
+ params.set('uid', current.value.uid || makeUid(current.value.file));
76
+ endpoint = '/GridJs2/DetailStreamJsonWithUid';
77
+ }
78
+ const payload = await fetchJson(endpoint + '?' + params);
79
+ workbookData.value = { ...payload, filename: current.value.file };
80
+ status.value = 'Loaded ' + current.value.file;
81
+ } catch (error) {
82
+ status.value = 'Workbook load failed: ' + error.message;
83
+ }
84
+ }
85
+
86
+ async function upload(event) {
87
+ const file = event.target.files?.[0];
88
+ if (!file) return;
89
+ const form = new FormData();
90
+ form.append('file', file);
91
+ form.append('client', 'vue');
92
+ status.value = 'Uploading ' + file.name + '...';
93
+ try {
94
+ const result = await fetchJson('/gridjsdemo/api/upload', { method: 'POST', body: form });
95
+ logEvent('uploaded ' + result.file);
96
+ // Newly uploaded files live under the server's uploads dir, flagged with fromUpload=1.
97
+ openWorkbook({
98
+ file: result.displayName || file.name,
99
+ storedFile: result.file,
100
+ demo: demoType.value,
101
+ uid: result.uid || makeUid(result.file),
102
+ fromUpload: '1',
103
+ });
104
+ } catch (error) {
105
+ status.value = 'Upload failed: ' + error.message;
106
+ } finally {
107
+ event.target.value = '';
108
+ }
109
+ }
110
+
111
+ // Restores the previously active sheet/cell (from the workbook data) once
112
+ // the grid has mounted, and points the "open file" link back at the start page.
113
+ function onReady(instance, adapter) {
114
+ instance.setActiveSheetByName?.(adapter.payload?.actname)?.setActiveCell?.(adapter.payload?.actrow, adapter.payload?.actcol);
115
+ instance.setOpenFileUrl?.('/');
116
+ logEvent('ready ' + (adapter.payload?.filename || current.value?.file));
117
+ }
118
+
119
+ function onError(payload) {
120
+ console.error('GridJS Vue mount error', payload);
121
+ logEvent('error ' + (payload?.error?.message || payload?.message || payload?.type || 'unknown'));
122
+ }
123
+
124
+ // Keeps state in sync when the user navigates with the browser's back/forward buttons.
125
+ function onPopState() {
126
+ current.value = readRoute();
127
+ if (current.value?.demo) demoType.value = current.value.demo;
128
+ }
129
+
130
+ watch(current, loadWorkbook, { immediate: true });
131
+ onMounted(() => {
132
+ loadFiles();
133
+ window.addEventListener('popstate', onPopState);
134
+ });
135
+ onBeforeUnmount(() => window.removeEventListener('popstate', onPopState));
136
+ </script>
137
+
138
+ <template>
139
+ <!-- A workbook is selected -> render the full-page spreadsheet editor. -->
140
+ <main v-if="hasWorkbook" class="editor-page">
141
+ <GridJsSpreadsheet
142
+ v-if="workbookData"
143
+ :key="workbookKey"
144
+ :data="workbookData"
145
+ height="100vh"
146
+ :show-toolbar="true"
147
+ :show-contextmenu="true"
148
+ @ready="onReady"
149
+ @change="logEvent('changed')"
150
+ @error="onError"
151
+ @cell-selected="(_, ri, ci) => logEvent('cell selected ' + ri + ',' + ci)"
152
+ @cell-edited="(text, ri, ci) => logEvent('cell edited ' + ri + ',' + ci)"
153
+ @sheet-selected="(_, name) => logEvent('sheet ' + name)"
154
+ />
155
+ <div v-else class="editor-loading">{{ status }}</div>
156
+ </main>
157
+
158
+ <!-- No workbook selected -> render the start page (demo picker + file list + upload). -->
159
+ <div v-else class="demo-shell">
160
+ <header class="demo-navbar">
161
+ <a href="https://docs.aspose.com/cells/java/aspose-cells-gridjs/">Aspose.Cells.GridJs demo for Java</a>
162
+ </header>
163
+
164
+ <main class="demo-container">
165
+ <fieldset class="demo-selector">
166
+ <legend>Select a demo:</legend>
167
+ <label :class="demoType === DEMO_PERMANENT ? 'radio-card active' : 'radio-card'">
168
+ <input type="radio" name="demoType" :checked="demoType === DEMO_PERMANENT" @change="demoType = DEMO_PERMANENT" />
169
+ <span>permanent url load demo</span>
170
+ </label>
171
+ <label :class="demoType === DEMO_HIGHLIGHT ? 'radio-card active' : 'radio-card'">
172
+ <input type="radio" name="demoType" :checked="demoType === DEMO_HIGHLIGHT" @change="demoType = DEMO_HIGHLIGHT" />
173
+ <span>highlight and custom context menu demo</span>
174
+ </label>
175
+ </fieldset>
176
+
177
+ <section class="upload-block">
178
+ <h4>Upload a local file to start show workbook</h4>
179
+ <input type="file" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" @change="upload" />
180
+ </section>
181
+
182
+ <section class="path-block">
183
+ <h4>The specific file path : {{ directory }}</h4>
184
+ <h4>Navigate one of the below file to start show workbook</h4>
185
+ </section>
186
+
187
+ <p class="status-message" role="status">{{ status }}</p>
188
+
189
+ <section class="file-list" aria-label="Workbook files">
190
+ <p v-if="status === 'Ready' && files.length === 0" class="empty-message">No workbooks found in the configured directory.</p>
191
+ <a v-for="file in files" :href="'/?file=' + encodeURIComponent(file)" :key="file" class="file-item" @click.prevent="openFile(file)">
192
+ <em>{{ file }}</em>
193
+ </a>
194
+ </section>
195
+ </main>
196
+
197
+ <footer class="demo-footer">© 2026 - <a href="https://products.aspose.com/cells/java">Aspose.Cells for Java</a></footer>
198
+ </div>
199
+ </template>
@@ -0,0 +1,7 @@
1
+ // Small fetch() wrapper that turns non-2xx responses into thrown Errors,
2
+ // so callers can just `await fetchJson(url)` and catch failures in one place.
3
+ export async function fetchJson(url, options) {
4
+ const response = await fetch(url, options);
5
+ if (!response.ok) throw new Error(response.status + ' ' + response.statusText);
6
+ return response.json();
7
+ }
@@ -0,0 +1,9 @@
1
+ // Entry point: just mounts the <App> root component (see ./App.vue).
2
+ // Shared route/API helpers live in ./routing.js and ./api.js so App.vue
3
+ // stays focused on component state and markup.
4
+ import { createApp } from 'vue';
5
+ import 'gridjs-spreadsheet/xspreadsheet.css';
6
+ import './styles.css';
7
+ import App from './App.vue';
8
+
9
+ createApp(App).mount('#app');