@shumoku/renderer 0.2.1 → 0.2.4
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.
- package/dist/html/index.d.ts +25 -0
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +742 -158
- package/dist/html/index.js.map +1 -1
- package/dist/html/navigation.d.ts +54 -0
- package/dist/html/navigation.d.ts.map +1 -0
- package/dist/html/navigation.js +210 -0
- package/dist/html/navigation.js.map +1 -0
- package/dist/html/runtime.d.ts +2 -1
- package/dist/html/runtime.d.ts.map +1 -1
- package/dist/html/runtime.js +245 -482
- package/dist/html/runtime.js.map +1 -1
- package/dist/html/spotlight.d.ts +9 -0
- package/dist/html/spotlight.d.ts.map +1 -0
- package/dist/html/spotlight.js +119 -0
- package/dist/html/spotlight.js.map +1 -0
- package/dist/html/tooltip.d.ts +14 -0
- package/dist/html/tooltip.d.ts.map +1 -0
- package/dist/html/tooltip.js +133 -0
- package/dist/html/tooltip.js.map +1 -0
- package/dist/html/viewbox.d.ts +14 -0
- package/dist/html/viewbox.d.ts.map +1 -0
- package/dist/html/viewbox.js +21 -0
- package/dist/html/viewbox.js.map +1 -0
- package/dist/iife-string.d.ts +2 -0
- package/dist/iife-string.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/shumoku-interactive.iife.js +25 -20
- package/dist/svg.d.ts +27 -0
- package/dist/svg.d.ts.map +1 -1
- package/dist/svg.js +202 -101
- package/dist/svg.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/build-iife-string.ts +26 -19
- package/src/html/index.ts +880 -226
- package/src/html/navigation.ts +256 -0
- package/src/html/runtime.ts +412 -654
- package/src/html/spotlight.ts +135 -0
- package/src/html/tooltip.ts +141 -0
- package/src/html/viewbox.ts +28 -0
- package/src/index.ts +25 -22
- package/src/svg.ts +1640 -1502
- package/src/types.ts +127 -125
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation UI for hierarchical network diagrams
|
|
3
|
+
* Provides breadcrumb, tabs, and back button for sheet navigation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Navigation state for hierarchical diagrams
|
|
8
|
+
*/
|
|
9
|
+
export interface NavigationState {
|
|
10
|
+
/**
|
|
11
|
+
* Current sheet ID (undefined = root)
|
|
12
|
+
*/
|
|
13
|
+
currentSheet?: string
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Breadcrumb path from root to current sheet
|
|
17
|
+
*/
|
|
18
|
+
breadcrumb: string[]
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Available sheets
|
|
22
|
+
*/
|
|
23
|
+
sheets: Map<string, SheetInfo>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Information about a sheet
|
|
28
|
+
*/
|
|
29
|
+
export interface SheetInfo {
|
|
30
|
+
id: string
|
|
31
|
+
label: string
|
|
32
|
+
parentId?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generate breadcrumb HTML
|
|
37
|
+
*/
|
|
38
|
+
export function generateBreadcrumb(state: NavigationState): string {
|
|
39
|
+
const items = state.breadcrumb.map((id, index) => {
|
|
40
|
+
const isLast = index === state.breadcrumb.length - 1
|
|
41
|
+
const label = id === 'root' ? 'Overview' : (state.sheets.get(id)?.label ?? id)
|
|
42
|
+
|
|
43
|
+
if (isLast) {
|
|
44
|
+
return `<span class="breadcrumb-current">${escapeHtml(label)}</span>`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return `<button class="breadcrumb-item" data-sheet="${escapeHtml(id)}">${escapeHtml(label)}</button>`
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
return `<nav class="breadcrumb">${items.join('<span class="breadcrumb-sep">/</span>')}</nav>`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Generate tab navigation HTML for sibling sheets
|
|
55
|
+
*/
|
|
56
|
+
export function generateTabs(state: NavigationState, siblingIds: string[]): string {
|
|
57
|
+
if (siblingIds.length <= 1) return ''
|
|
58
|
+
|
|
59
|
+
const tabs = siblingIds.map((id) => {
|
|
60
|
+
const info = state.sheets.get(id)
|
|
61
|
+
const label = info?.label ?? id
|
|
62
|
+
const isActive = id === state.currentSheet
|
|
63
|
+
|
|
64
|
+
return `<button class="tab ${isActive ? 'active' : ''}" data-sheet="${escapeHtml(id)}">${escapeHtml(label)}</button>`
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return `<div class="tabs">${tabs.join('')}</div>`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Generate back button HTML
|
|
72
|
+
*/
|
|
73
|
+
export function generateBackButton(state: NavigationState): string {
|
|
74
|
+
if (state.breadcrumb.length <= 1) return ''
|
|
75
|
+
|
|
76
|
+
const parentId = state.breadcrumb[state.breadcrumb.length - 2]
|
|
77
|
+
const parentLabel =
|
|
78
|
+
parentId === 'root' ? 'Overview' : (state.sheets.get(parentId)?.label ?? parentId)
|
|
79
|
+
|
|
80
|
+
return `<button class="back-btn" data-sheet="${escapeHtml(parentId)}">
|
|
81
|
+
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
82
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
|
83
|
+
</svg>
|
|
84
|
+
${escapeHtml(parentLabel)}
|
|
85
|
+
</button>`
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Generate complete navigation toolbar HTML
|
|
90
|
+
*/
|
|
91
|
+
export function generateNavigationToolbar(
|
|
92
|
+
state: NavigationState,
|
|
93
|
+
siblingIds: string[] = [],
|
|
94
|
+
): string {
|
|
95
|
+
const breadcrumb = generateBreadcrumb(state)
|
|
96
|
+
const backBtn = generateBackButton(state)
|
|
97
|
+
const tabs = generateTabs(state, siblingIds)
|
|
98
|
+
|
|
99
|
+
return `<div class="nav-toolbar">
|
|
100
|
+
<div class="nav-row">
|
|
101
|
+
${backBtn}
|
|
102
|
+
${breadcrumb}
|
|
103
|
+
</div>
|
|
104
|
+
${tabs}
|
|
105
|
+
</div>`
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get CSS styles for navigation components
|
|
110
|
+
*/
|
|
111
|
+
export function getNavigationStyles(): string {
|
|
112
|
+
return `
|
|
113
|
+
.nav-toolbar {
|
|
114
|
+
padding: 8px 16px;
|
|
115
|
+
background: white;
|
|
116
|
+
border-bottom: 1px solid #e5e5e5;
|
|
117
|
+
}
|
|
118
|
+
.nav-row {
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
gap: 12px;
|
|
122
|
+
}
|
|
123
|
+
.back-btn {
|
|
124
|
+
display: flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
gap: 4px;
|
|
127
|
+
padding: 6px 12px;
|
|
128
|
+
border: 1px solid #e5e5e5;
|
|
129
|
+
background: white;
|
|
130
|
+
border-radius: 6px;
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
font-size: 13px;
|
|
133
|
+
color: #555;
|
|
134
|
+
transition: all 0.15s;
|
|
135
|
+
}
|
|
136
|
+
.back-btn:hover {
|
|
137
|
+
background: #f5f5f5;
|
|
138
|
+
border-color: #ccc;
|
|
139
|
+
}
|
|
140
|
+
.breadcrumb {
|
|
141
|
+
display: flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
gap: 4px;
|
|
144
|
+
font-size: 13px;
|
|
145
|
+
}
|
|
146
|
+
.breadcrumb-item {
|
|
147
|
+
background: none;
|
|
148
|
+
border: none;
|
|
149
|
+
color: #0066cc;
|
|
150
|
+
cursor: pointer;
|
|
151
|
+
padding: 4px 8px;
|
|
152
|
+
border-radius: 4px;
|
|
153
|
+
}
|
|
154
|
+
.breadcrumb-item:hover {
|
|
155
|
+
background: #f0f7ff;
|
|
156
|
+
}
|
|
157
|
+
.breadcrumb-current {
|
|
158
|
+
color: #333;
|
|
159
|
+
font-weight: 500;
|
|
160
|
+
}
|
|
161
|
+
.breadcrumb-sep {
|
|
162
|
+
color: #999;
|
|
163
|
+
}
|
|
164
|
+
.tabs {
|
|
165
|
+
display: flex;
|
|
166
|
+
gap: 4px;
|
|
167
|
+
margin-top: 8px;
|
|
168
|
+
border-bottom: 1px solid #e5e5e5;
|
|
169
|
+
padding-bottom: 0;
|
|
170
|
+
}
|
|
171
|
+
.tab {
|
|
172
|
+
padding: 8px 16px;
|
|
173
|
+
border: none;
|
|
174
|
+
background: none;
|
|
175
|
+
cursor: pointer;
|
|
176
|
+
font-size: 13px;
|
|
177
|
+
color: #666;
|
|
178
|
+
border-bottom: 2px solid transparent;
|
|
179
|
+
margin-bottom: -1px;
|
|
180
|
+
transition: all 0.15s;
|
|
181
|
+
}
|
|
182
|
+
.tab:hover {
|
|
183
|
+
color: #333;
|
|
184
|
+
background: #f5f5f5;
|
|
185
|
+
}
|
|
186
|
+
.tab.active {
|
|
187
|
+
color: #0066cc;
|
|
188
|
+
border-bottom-color: #0066cc;
|
|
189
|
+
font-weight: 500;
|
|
190
|
+
}
|
|
191
|
+
`
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get JavaScript code for navigation event handling
|
|
196
|
+
*/
|
|
197
|
+
export function getNavigationScript(): string {
|
|
198
|
+
return `
|
|
199
|
+
(function() {
|
|
200
|
+
function navigateToSheet(sheetId) {
|
|
201
|
+
// Dispatch custom event for sheet navigation
|
|
202
|
+
var event = new CustomEvent('shumoku:navigate', {
|
|
203
|
+
detail: { sheetId: sheetId },
|
|
204
|
+
bubbles: true
|
|
205
|
+
});
|
|
206
|
+
document.dispatchEvent(event);
|
|
207
|
+
|
|
208
|
+
// For standalone HTML, show alert (sheets are not embedded yet)
|
|
209
|
+
console.log('[Shumoku] Navigate to sheet:', sheetId);
|
|
210
|
+
alert('Navigate to: ' + sheetId + '\\n\\nNote: Multi-sheet navigation requires embedded sheets.');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Listen for navigation events from subgraph clicks
|
|
214
|
+
document.addEventListener('shumoku:navigate', function(e) {
|
|
215
|
+
var sheetId = e.detail && e.detail.sheetId;
|
|
216
|
+
if (sheetId) {
|
|
217
|
+
console.log('[Shumoku] Subgraph clicked, sheet:', sheetId);
|
|
218
|
+
alert('Navigate to: ' + sheetId + '\\n\\nNote: Multi-sheet navigation requires embedded sheets.');
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Handle breadcrumb clicks
|
|
223
|
+
document.querySelectorAll('.breadcrumb-item').forEach(function(btn) {
|
|
224
|
+
btn.addEventListener('click', function() {
|
|
225
|
+
var sheetId = this.getAttribute('data-sheet');
|
|
226
|
+
if (sheetId) navigateToSheet(sheetId);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Handle back button clicks
|
|
231
|
+
document.querySelectorAll('.back-btn').forEach(function(btn) {
|
|
232
|
+
btn.addEventListener('click', function() {
|
|
233
|
+
var sheetId = this.getAttribute('data-sheet');
|
|
234
|
+
if (sheetId) navigateToSheet(sheetId);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Handle tab clicks
|
|
239
|
+
document.querySelectorAll('.tab').forEach(function(btn) {
|
|
240
|
+
btn.addEventListener('click', function() {
|
|
241
|
+
var sheetId = this.getAttribute('data-sheet');
|
|
242
|
+
if (sheetId) navigateToSheet(sheetId);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
})();
|
|
246
|
+
`
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function escapeHtml(str: string): string {
|
|
250
|
+
return str
|
|
251
|
+
.replace(/&/g, '&')
|
|
252
|
+
.replace(/</g, '<')
|
|
253
|
+
.replace(/>/g, '>')
|
|
254
|
+
.replace(/"/g, '"')
|
|
255
|
+
.replace(/'/g, ''')
|
|
256
|
+
}
|