gis-view-webgl2 0.1.0 → 0.1.2

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 (87) hide show
  1. package/README.md +40 -8
  2. package/dist/gis-view.css +1 -1
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +1036 -249
  5. package/dist/index.js +1 -1
  6. package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +194 -0
  7. package/docs/GisView.md +9 -8
  8. package/docs/GisView/345/256/236/347/216/260/345/216/237/347/220/206.md +2 -2
  9. package/docs/ai/06-/344/274/232/350/257/235/347/264/242/345/274/225/344/270/216/345/244/215/346/237/245/346/226/271/346/263/225.md +172 -0
  10. package/docs/api/index.html +545 -59
  11. package/docs/demos/BackendSyncDemo.vue +10 -8
  12. package/docs/demos/BasicDemo.vue +101 -101
  13. package/docs/demos/BatchAddDemo.vue +5 -5
  14. package/docs/demos/ChannelDemo.vue +617 -10
  15. package/docs/demos/ConnectDemo.vue +392 -0
  16. package/docs/demos/CopyMergeDemo.vue +980 -929
  17. package/docs/demos/DecorationDraftDemo.vue +1300 -0
  18. package/docs/demos/DemoShell.vue +569 -240
  19. package/docs/demos/DragDropDemo.vue +11 -11
  20. package/docs/demos/EffectsDemo.vue +6 -6
  21. package/docs/demos/EventDemo.vue +327 -327
  22. package/docs/demos/ExtensionDemo.vue +458 -0
  23. package/docs/demos/GisViewDemo.vue +61 -48
  24. package/docs/demos/GraphicDemo.vue +338 -0
  25. package/docs/demos/LabelDemo.vue +553 -497
  26. package/docs/demos/MinimapDemo.vue +358 -0
  27. package/docs/demos/PerfDemo.vue +29 -0
  28. package/docs/demos/SearchFilterDemo.vue +412 -0
  29. package/docs/demos/StyleDemo.vue +121 -121
  30. package/docs/demos/api-index.js +98 -77
  31. package/docs/demos/demos.js +159 -135
  32. package/docs/demos/shared/demoData.js +107 -107
  33. package/docs/demos/shared/demoStyle.css +249 -249
  34. package/docs/demos/shared/fakeBackend.js +93 -6
  35. package/docs/format/03-/346/213/220/347/202/271/350/243/205/351/245/260/344/270/216/346/240/267/345/274/217.md +28 -8
  36. package/docs/format/04-/351/200/232/351/201/223/344/270/216/346/240/207/347/255/276.md +5 -4
  37. package/docs/format/06-/345/257/274/345/205/245/345/257/274/345/207/272/344/270/216/346/240/207/345/207/206/346/226/207/346/241/243.md +8 -6
  38. package/docs/format/07-/344/272/213/344/273/266/350/275/275/350/215/267/344/270/216/347/244/272/344/276/213/346/225/260/346/215/256.md +6 -6
  39. package/docs/guide/sample-document.html +4 -4
  40. package/docs/index.html +180 -9
  41. package/docs/internals/11-/350/246/206/347/233/226/345/261/202/346/240/207/347/255/276/344/270/216/346/217/220/347/244/272.md +1 -1
  42. package/docs/internals/13-/351/271/260/347/234/274/345/233/276.md +3 -2
  43. package/docs/internals/15-03-/351/200/220/350/275/256/350/256/260/345/275/225-/346/200/247/350/203/275/344/270/216/344/277/256/345/244/215-/344/270/213.md +16 -0
  44. package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +11 -0
  45. package/docs/internals/17-/350/260/203/350/257/225/344/270/216/346/211/251/345/261/225/345/273/272/350/256/256.md +1 -1
  46. package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +18 -4
  47. package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +110 -25
  48. package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +13 -8
  49. package/docs/manual/06-/346/225/260/346/215/256/344/270/216/345/234/272/346/231/257/346/233/264/346/226/260.md +111 -10
  50. package/docs/manual/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +47 -9
  51. package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +17 -15
  52. package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +61 -11
  53. package/docs/manual/10-/351/200/232/351/201/223/347/274/226/350/276/221/344/270/216/346/226/260/345/242/236.md +74 -7
  54. package/docs/manual/11-/346/240/267/345/274/217/344/270/216/350/247/204/345/210/231.md +3 -2
  55. package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +260 -3
  56. package/docs/manual/13-/345/212/250/346/225/210/344/270/216/345/233/276/345/205/203.md +6 -5
  57. package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +27 -14
  58. package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +142 -49
  59. package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +31 -29
  60. package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +55 -39
  61. package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +189 -64
  62. package/docs/manual/19-/350/257/273/346/225/260/344/270/216/346/213/226/346/213/275/351/242/204/350/247/210.md +4 -4
  63. package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +42 -2
  64. package/docs/manual/21-/350/257/264/346/230/216/344/270/216/345/270/270/350/247/201/351/227/256/351/242/230.md +22 -6
  65. package/docs/playground/index.html +5 -5
  66. package/docs/steps/README.md +14 -10
  67. package/docs/steps/check-doc-truth.mjs +1 -1
  68. package/docs/steps/selftest-actions.html +10 -10
  69. package/docs/steps/selftest-data.html +96 -14
  70. package/docs/steps/selftest-decoration.html +577 -0
  71. package/docs/steps/selftest-demo.html +6 -5
  72. package/docs/steps/selftest-demos.html +2201 -1612
  73. package/docs/steps/selftest-docs.html +89 -16
  74. package/docs/steps/selftest-effects.html +5 -5
  75. package/docs/steps/selftest-features.html +757 -86
  76. package/docs/steps/selftest-full.html +583 -583
  77. package/docs/steps/selftest-labels.html +1034 -652
  78. package/docs/steps/selftest-perf.html +1899 -1844
  79. package/docs/steps/selftest-scale.html +117 -7
  80. package/docs/steps/selftest-select.html +24 -17
  81. package/docs/steps/step-10-full.js +1 -1
  82. package/docs//346/216/245/345/217/243/345/215/217/350/256/256.md +167 -17
  83. package/docs//346/225/260/346/215/256/346/240/274/345/274/217.md +1 -1
  84. package/package.json +43 -43
  85. package/docs/demos/HistoryDemo.vue +0 -230
  86. package/docs/demos/InterfaceDemo.vue +0 -309
  87. package/docs/demos/SampleDemo.vue +0 -283
@@ -11,21 +11,63 @@
11
11
  --line: #d7e2ee;
12
12
  --bg: #f5f8fc;
13
13
  --card: #ffffff;
14
+ --card-2: #f0f5fb;
15
+ --control: #ffffff;
14
16
  --accent: #2f6fd0;
17
+ --accent-strong: #235a9f;
18
+ --accent-ink: #0b3d91;
15
19
  --accent-soft: #e8f0fd;
16
20
  --code-bg: #16202c;
17
21
  --code-ink: #d7e3f2;
18
22
  --ok: #2ea36b;
23
+ --warn: #c98618;
24
+ --highlight-bg: #fffdf5;
25
+ --highlight-line: #f0a92e;
26
+ --header-bg: rgba(255, 255, 255, 0.94);
27
+ --shadow: 0 16px 34px -28px rgba(23, 45, 78, 0.55);
28
+ }
29
+ html.theme-dark {
30
+ color-scheme: dark;
31
+ --ink: #c6d4e4;
32
+ --dim: #8ba0b8;
33
+ --line: #253040;
34
+ --bg: #0c131c;
35
+ --card: #141c26;
36
+ --card-2: #1b2532;
37
+ --control: #18212d;
38
+ --accent: #74a9ea;
39
+ --accent-strong: #9cc3ea;
40
+ --accent-ink: #a9cbf0;
41
+ --accent-soft: #1a2a3e;
42
+ --code-bg: #0b1420;
43
+ --code-ink: #d8e6f6;
44
+ --ok: #58c98c;
45
+ --warn: #e0a34a;
46
+ --highlight-bg: #2b2618;
47
+ --highlight-line: #b9781c;
48
+ --header-bg: rgba(12, 19, 28, 0.9);
49
+ --shadow: 0 18px 36px -26px rgba(0, 0, 0, 0.9);
19
50
  }
20
51
  * { box-sizing: border-box; }
52
+ html { scroll-padding-top: 82px; }
21
53
  body {
22
54
  margin: 0;
23
55
  background: var(--bg);
24
56
  color: var(--ink);
25
- font: 13px/1.65 system-ui, 'Microsoft YaHei', sans-serif;
57
+ font: 13.5px/1.7 system-ui, 'Microsoft YaHei', sans-serif;
58
+ -webkit-font-smoothing: antialiased;
26
59
  }
27
60
  code { font-family: ui-monospace, Consolas, 'Courier New', monospace; }
28
61
  a { color: var(--accent); }
62
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
63
+ * { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
64
+ ::-webkit-scrollbar { width: 10px; height: 10px; }
65
+ ::-webkit-scrollbar-thumb {
66
+ border: 3px solid transparent;
67
+ border-radius: 999px;
68
+ background: var(--line);
69
+ background-clip: content-box;
70
+ }
29
71
  .topbar {
30
72
  position: sticky;
31
73
  top: 0;
@@ -37,10 +79,10 @@
37
79
  justify-content: space-between;
38
80
  padding: 10px 18px;
39
81
  border-bottom: 1px solid var(--line);
40
- background: rgba(255, 255, 255, 0.94);
41
- backdrop-filter: blur(6px);
82
+ background: var(--header-bg);
83
+ backdrop-filter: blur(10px);
42
84
  }
43
- .topbar h1 { margin: 0; font-size: 17px; }
85
+ .topbar h1 { margin: 0; color: var(--ink); font-size: 17px; }
44
86
  .topbar .sub { margin: 2px 0 0; color: var(--dim); font-size: 12px; }
45
87
  .topbar .brand { display: flex; gap: 12px; align-items: center; }
46
88
  .topbar .back { flex: none; font-size: 12px; text-decoration: none; }
@@ -50,32 +92,54 @@
50
92
  padding: 6px 10px;
51
93
  border: 1px solid var(--line);
52
94
  border-radius: 8px;
53
- background: #fff;
95
+ background: var(--control);
54
96
  color: inherit;
55
97
  font: inherit;
98
+ transition: border-color 0.15s, box-shadow 0.15s;
99
+ }
100
+ #search:focus {
101
+ border-color: var(--accent);
102
+ outline: none;
103
+ box-shadow: 0 0 0 3px var(--accent-soft);
56
104
  }
57
105
  .tools a, .tools button {
58
106
  padding: 6px 10px;
59
107
  border: 1px solid var(--line);
60
108
  border-radius: 8px;
61
- background: #fff;
109
+ background: var(--control);
62
110
  color: var(--accent);
63
111
  font: inherit;
64
112
  text-decoration: none;
65
113
  cursor: pointer;
114
+ transition: border-color 0.15s, background 0.15s, color 0.15s, transform 0.15s;
115
+ }
116
+ .tools a:hover, .tools button:hover {
117
+ border-color: var(--accent);
118
+ background: var(--accent-soft);
119
+ color: var(--accent-strong);
120
+ transform: translateY(-1px);
121
+ }
122
+ #theme-btn { min-width: 104px; }
123
+ main {
124
+ display: flex;
125
+ align-items: flex-start;
126
+ gap: 20px;
127
+ width: min(100%, 1680px);
128
+ margin: 0 auto;
129
+ padding: 18px 20px 64px;
66
130
  }
67
- main { display: flex; align-items: flex-start; gap: 18px; padding: 16px 18px 60px; }
68
131
  #side {
69
132
  position: sticky;
70
133
  top: 74px;
71
134
  flex: none;
72
- width: 214px;
135
+ width: 232px;
73
136
  max-height: calc(100vh - 96px);
74
137
  overflow: auto;
75
- padding: 10px;
138
+ padding: 12px;
76
139
  border: 1px solid var(--line);
77
140
  border-radius: 12px;
78
141
  background: var(--card);
142
+ box-shadow: var(--shadow);
79
143
  }
80
144
  #side h2 { margin: 0 0 6px; font-size: 12px; color: var(--dim); font-weight: 600; }
81
145
  .side-item {
@@ -92,18 +156,22 @@
92
156
  font: inherit;
93
157
  text-align: left;
94
158
  cursor: pointer;
159
+ transition: background 0.14s, color 0.14s;
95
160
  }
96
161
  .side-item:hover { background: var(--accent-soft); }
162
+ .side-item.on { background: var(--accent-soft); color: var(--accent-strong); }
163
+ .side-item.on b { color: var(--accent); }
97
164
  .side-item b { color: var(--dim); font-weight: 500; }
98
165
  #side .kinds { margin: 8px 0 4px; padding-top: 8px; border-top: 1px dashed var(--line); color: var(--dim); font-size: 12px; }
99
166
  #side .kinds label { display: block; margin: 2px 0; cursor: pointer; }
100
167
  #content { flex: 1 1 auto; min-width: 0; }
101
168
  .card {
102
169
  margin: 0 0 14px;
103
- padding: 12px 14px;
170
+ padding: 14px 16px;
104
171
  border: 1px solid var(--line);
105
172
  border-radius: 12px;
106
173
  background: var(--card);
174
+ box-shadow: var(--shadow);
107
175
  }
108
176
  .card h2 { margin: 0 0 6px; font-size: 15px; }
109
177
  .api-group { margin: 0 0 22px; }
@@ -111,7 +179,7 @@
111
179
  position: sticky;
112
180
  top: 62px;
113
181
  margin: 0 0 8px;
114
- padding: 6px 2px;
182
+ padding: 8px 2px;
115
183
  background: var(--bg);
116
184
  font-size: 16px;
117
185
  border-bottom: 1px solid var(--line);
@@ -124,11 +192,17 @@
124
192
  border-left: 3px solid var(--accent);
125
193
  border-radius: 10px;
126
194
  background: var(--card);
195
+ scroll-margin-top: 82px;
196
+ transition: border-color 0.15s, box-shadow 0.15s;
197
+ }
198
+ .api-member:hover {
199
+ border-color: var(--accent);
200
+ box-shadow: var(--shadow);
127
201
  }
128
- .api-member.highlight { border-color: #f0a92e; border-left-color: #f0a92e; background: #fffdf5; }
202
+ .api-member.highlight { border-color: var(--highlight-line); border-left-color: var(--highlight-line); background: var(--highlight-bg); }
129
203
  .api-member > header { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
130
204
  .api-member h3 { margin: 0; font-size: 14px; }
131
- .api-member h3 code { color: #0b3d91; }
205
+ .api-member h3 code { color: var(--accent-ink); }
132
206
  .api-member .badge {
133
207
  padding: 1px 6px;
134
208
  border-radius: 999px;
@@ -136,17 +210,18 @@
136
210
  color: var(--accent);
137
211
  font-size: 11px;
138
212
  }
139
- .api-member .badge.property { background: #eaf7ef; color: var(--ok); }
140
- .api-member .copy-name { margin-left: auto; border: 1px solid var(--line); border-radius: 7px; background: #fff; color: var(--dim); font: inherit; font-size: 12px; cursor: pointer; }
213
+ .api-member .badge.property { background: var(--card-2); color: var(--ok); }
214
+ .api-member .copy-name { margin-left: auto; border: 1px solid var(--line); border-radius: 7px; background: var(--control); color: var(--dim); font: inherit; font-size: 12px; cursor: pointer; }
141
215
  .api-member dl { display: grid; grid-template-columns: 42px 1fr; gap: 2px 8px; margin: 6px 0 4px; }
142
216
  .api-member dt { color: var(--dim); }
143
- .api-member dd { margin: 0; }
144
- .api-member .doc { margin: 4px 0 8px; }
217
+ .api-member dd { margin: 0; max-width: 88ch; }
218
+ .api-member .doc { max-width: 88ch; margin: 4px 0 8px; line-height: 1.75; }
145
219
  .example { margin: 6px 0 0; border: 1px solid var(--line); border-radius: 9px; overflow: hidden; }
146
- .example-head { display: flex; gap: 8px; align-items: center; padding: 4px 8px; background: #f0f5fb; color: var(--dim); font-size: 12px; }
147
- .example-head button { margin-left: auto; border: 1px solid var(--line); border-radius: 7px; background: #fff; color: var(--accent); font: inherit; font-size: 12px; cursor: pointer; }
148
- .example pre { margin: 0; padding: 10px 12px; overflow: auto; background: var(--code-bg); color: var(--code-ink); }
149
- .example code { font-size: 12px; line-height: 1.6; white-space: pre; }
220
+ .example-head { display: flex; gap: 8px; align-items: center; padding: 5px 9px; background: var(--card-2); color: var(--dim); font-size: 12px; }
221
+ .example-head button { margin-left: auto; border: 1px solid var(--line); border-radius: 7px; background: var(--control); color: var(--accent); font: inherit; font-size: 12px; cursor: pointer; }
222
+ .example-head button + button { margin-left: 0; }
223
+ .example pre { margin: 0; padding: 11px 13px; overflow: auto; background: var(--code-bg); color: var(--code-ink); tab-size: 2; }
224
+ .example code { font-size: 12px; line-height: 1.65; white-space: pre; }
150
225
  .tok-comment { color: #7d93ab; }
151
226
  .tok-string { color: #9adc9a; }
152
227
  .tok-key { color: #7fb6f6; }
@@ -159,51 +234,363 @@
159
234
  padding: 7px 4px;
160
235
  border-bottom: 1px dashed var(--line);
161
236
  }
162
- .api-option .name { color: #0b3d91; }
237
+ .api-option .name { color: var(--accent-ink); }
163
238
  .api-option .type, .api-option .def { color: var(--dim); font-size: 12px; }
164
239
  .api-option .doc { grid-column: 1 / -1; margin: 0; }
165
240
  .hidden { display: none !important; }
166
241
  #hits { color: var(--dim); font-size: 12px; }
167
242
  @media (max-width: 900px) {
168
- main { flex-direction: column; }
169
- #side { position: static; width: 100%; max-height: none; }
243
+ main { flex-direction: column; padding: 14px 14px 48px; }
244
+ #side { position: static; width: 100%; max-height: none; box-shadow: none; }
245
+ #side-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 2px; }
246
+ }
247
+ @media (max-width: 680px) {
248
+ .topbar { align-items: flex-start; padding: 10px 12px; }
249
+ .topbar .brand { width: 100%; }
250
+ .topbar .sub { display: none; }
251
+ .tools { width: 100%; flex-wrap: wrap; }
252
+ #search { flex: 1 1 100%; width: 100%; }
253
+ .tools a, .tools button { flex: 1 1 auto; text-align: center; }
254
+ .api-group > h2 { top: 104px; }
255
+ .api-member dl { grid-template-columns: 38px 1fr; }
256
+ }
257
+ @media (prefers-reduced-motion: reduce) {
258
+ *, *::before, *::after {
259
+ scroll-behavior: auto !important;
260
+ transition-duration: 0.001s !important;
261
+ }
262
+ }
263
+ /* ------------------------------------------------------------------
264
+ API 速查视觉层:Ant Design / ECharts 文档那种「先扫结构、再看细节」的密度
265
+ ------------------------------------------------------------------ */
266
+ :root {
267
+ --ink: #1d2129;
268
+ --dim: #667085;
269
+ --line: #e6e9ef;
270
+ --bg: #f5f6f8;
271
+ --card: #ffffff;
272
+ --card-2: #f7f9fc;
273
+ --control: #ffffff;
274
+ --accent: #1677ff;
275
+ --accent-strong: #0958d9;
276
+ --accent-ink: #0958d9;
277
+ --accent-soft: #eaf2ff;
278
+ --code-bg: #111827;
279
+ --code-ink: #e5edf8;
280
+ --ok: #00a870;
281
+ --warn: #d48806;
282
+ --highlight-bg: #fffbe6;
283
+ --highlight-line: #faad14;
284
+ --header-bg: rgba(255, 255, 255, 0.9);
285
+ --shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 8px 24px -18px rgba(16, 24, 40, 0.35);
286
+ }
287
+ html.theme-dark {
288
+ --ink: #e6edf7;
289
+ --dim: #8c99ac;
290
+ --line: #263243;
291
+ --bg: #0f141c;
292
+ --card: #151c26;
293
+ --card-2: #1b2431;
294
+ --control: #18212d;
295
+ --accent: #4d9cff;
296
+ --accent-strong: #7db5ff;
297
+ --accent-ink: #8abaff;
298
+ --accent-soft: #172a44;
299
+ --code-bg: #0a111b;
300
+ --code-ink: #dbe8f8;
301
+ --ok: #4fd39b;
302
+ --warn: #e8b04b;
303
+ --highlight-bg: #2b2416;
304
+ --highlight-line: #b77c1d;
305
+ --header-bg: rgba(15, 20, 28, 0.9);
306
+ --shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 12px 30px -22px rgba(0, 0, 0, 0.9);
307
+ }
308
+ html { scroll-padding-top: 86px; }
309
+ body {
310
+ font-size: 14px;
311
+ line-height: 1.65;
312
+ background: radial-gradient(circle at 50% -20%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 36rem), var(--bg);
313
+ }
314
+ .topbar {
315
+ min-height: 64px;
316
+ flex-wrap: nowrap;
317
+ gap: 20px;
318
+ padding: 9px clamp(14px, 3vw, 34px);
319
+ border-bottom-color: var(--line);
320
+ box-shadow: 0 1px 0 rgba(16, 24, 40, 0.02);
321
+ }
322
+ .topbar .brand { min-width: 0; gap: 11px; }
323
+ .topbar .brand-logo {
324
+ display: grid;
325
+ place-items: center;
326
+ width: 36px;
327
+ height: 36px;
328
+ flex: none;
329
+ border-radius: 9px;
330
+ background: linear-gradient(145deg, #1677ff, #5b8ff9);
331
+ color: #fff;
332
+ font: 700 16px/1 ui-monospace, Consolas, monospace;
333
+ text-decoration: none;
334
+ box-shadow: 0 6px 14px -8px #1677ff;
335
+ }
336
+ .topbar h1 { font-size: 16px; letter-spacing: 0.01em; }
337
+ .topbar .sub { margin-top: 1px; font-size: 11.5px; }
338
+ .topbar .tools { flex: 1 1 auto; justify-content: flex-end; gap: 6px; }
339
+ .search-box {
340
+ display: flex;
341
+ align-items: center;
342
+ gap: 7px;
343
+ width: min(420px, 34vw);
344
+ padding: 0 10px;
345
+ border: 1px solid var(--line);
346
+ border-radius: 8px;
347
+ background: var(--control);
348
+ transition: border-color 0.15s, box-shadow 0.15s;
349
+ }
350
+ .search-box:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
351
+ .search-box .search-icon { color: var(--dim); font-size: 16px; line-height: 1; }
352
+ #search {
353
+ width: 100%;
354
+ padding: 7px 0;
355
+ border: 0;
356
+ border-radius: 0;
357
+ background: transparent;
358
+ box-shadow: none;
359
+ }
360
+ #search:focus { box-shadow: none; }
361
+ .tools a, .tools button {
362
+ padding: 6px 9px;
363
+ border-color: transparent;
364
+ background: transparent;
365
+ color: var(--dim);
366
+ font-size: 12.5px;
367
+ }
368
+ .tools a:hover, .tools button:hover { border-color: var(--line); background: var(--card); color: var(--accent); transform: none; }
369
+ #theme-btn { min-width: 0; }
370
+ main {
371
+ align-items: flex-start;
372
+ gap: 24px;
373
+ width: min(100%, 1560px);
374
+ padding: 24px clamp(14px, 3vw, 34px) 72px;
375
+ }
376
+ #side {
377
+ top: 80px;
378
+ width: 252px;
379
+ max-height: calc(100vh - 104px);
380
+ padding: 14px 12px;
381
+ border-radius: 12px;
382
+ box-shadow: none;
383
+ }
384
+ .side-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0 6px 10px; border-bottom: 1px solid var(--line); }
385
+ .side-head h2 { margin: 0; font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; }
386
+ #hits { max-width: 132px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; font-size: 10.5px; }
387
+ #side .kinds { display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0 12px; padding: 0; border: 0; }
388
+ #side .kinds label {
389
+ display: inline-flex;
390
+ align-items: center;
391
+ gap: 5px;
392
+ padding: 4px 8px;
393
+ border: 1px solid var(--line);
394
+ border-radius: 999px;
395
+ background: var(--card-2);
396
+ color: var(--dim);
397
+ font-size: 11.5px;
398
+ cursor: pointer;
399
+ }
400
+ #side .kinds label:has(input:checked) { border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); background: var(--accent-soft); color: var(--accent-ink); }
401
+ #side .kinds input { width: 12px; height: 12px; margin: 0; accent-color: var(--accent); }
402
+ #side-list { display: flex; flex-direction: column; gap: 2px; }
403
+ .side-item { align-items: center; padding: 7px 8px; border-radius: 7px; font-size: 12.5px; }
404
+ .side-item b { min-width: 24px; padding: 1px 6px; border-radius: 999px; background: var(--card-2); color: var(--dim); font-size: 10.5px; text-align: center; }
405
+ .side-item.on b { background: var(--accent); color: #fff; }
406
+ .hero {
407
+ display: grid;
408
+ grid-template-columns: minmax(0, 1.02fr) minmax(320px, 0.98fr);
409
+ gap: clamp(20px, 3vw, 36px);
410
+ margin: 0 0 24px;
411
+ padding: clamp(22px, 3.2vw, 36px);
412
+ border: 1px solid var(--line);
413
+ border-radius: 16px;
414
+ background:
415
+ linear-gradient(135deg, color-mix(in srgb, var(--accent) 6%, var(--card)), var(--card) 58%),
416
+ var(--card);
417
+ box-shadow: var(--shadow);
418
+ overflow: hidden;
419
+ }
420
+ .hero-copy { min-width: 0; align-self: center; }
421
+ .eyebrow { display: inline-flex; align-items: center; gap: 7px; margin-bottom: 10px; color: var(--accent); font-size: 11px; font-weight: 700; letter-spacing: 0.14em; }
422
+ .eyebrow::before { content: ''; width: 18px; height: 2px; border-radius: 999px; background: currentColor; }
423
+ .hero h2 { margin: 0; max-width: 18em; font-size: clamp(24px, 3vw, 34px); line-height: 1.25; letter-spacing: -0.025em; }
424
+ .hero-summary { max-width: 62ch; margin: 12px 0 20px; color: var(--dim); font-size: 13.5px; line-height: 1.75; }
425
+ .stat-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 9px; }
426
+ .api-stat { padding: 11px 12px; border: 1px solid var(--line); border-radius: 10px; background: color-mix(in srgb, var(--card) 78%, transparent); }
427
+ .api-stat b { display: block; color: var(--ink); font: 650 21px/1.15 ui-monospace, Consolas, monospace; letter-spacing: -0.035em; }
428
+ .api-stat span { display: block; margin-top: 4px; color: var(--dim); font-size: 11px; }
429
+ .hero-code { min-width: 0; align-self: center; }
430
+ .hero-code .example { margin: 0; border-color: color-mix(in srgb, var(--accent) 18%, var(--line)); box-shadow: 0 14px 30px -24px rgba(22, 119, 255, 0.65); }
431
+ .hero-note { margin: 10px 2px 0; color: var(--dim); font-size: 11.5px; }
432
+ @media (max-width: 980px) {
433
+ .hero { grid-template-columns: 1fr; }
434
+ .topbar .tools { gap: 4px; }
435
+ .tools a:nth-of-type(2) { display: none; }
436
+ }
437
+ #content { padding-bottom: 20px; }
438
+ .api-group { margin: 0 0 30px; scroll-margin-top: 86px; }
439
+ .api-group > h2 {
440
+ top: 64px;
441
+ display: flex;
442
+ align-items: center;
443
+ gap: 9px;
444
+ margin: 0 0 12px;
445
+ padding: 11px 2px 10px;
446
+ border-bottom: 1px solid var(--line);
447
+ background: color-mix(in srgb, var(--bg) 92%, transparent);
448
+ backdrop-filter: blur(8px);
449
+ font-size: 17px;
450
+ letter-spacing: -0.01em;
451
+ }
452
+ .api-group > h2 b { margin-left: auto; padding: 2px 8px; border-radius: 999px; background: var(--card-2); font: 500 11px/1.6 ui-monospace, Consolas, monospace; }
453
+ .group-index { color: var(--accent); font: 600 11px/1 ui-monospace, Consolas, monospace; letter-spacing: 0.04em; }
454
+ .api-member {
455
+ margin: 0 0 12px;
456
+ padding: 16px 18px 17px;
457
+ border: 1px solid var(--line);
458
+ border-left: 1px solid var(--line);
459
+ border-radius: 11px;
460
+ background: var(--card);
461
+ box-shadow: 0 1px 1px rgba(16, 24, 40, 0.02);
462
+ scroll-margin-top: 86px;
463
+ }
464
+ .api-member:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); box-shadow: var(--shadow); }
465
+ .api-member.highlight { border-color: var(--highlight-line); background: var(--highlight-bg); box-shadow: 0 0 0 3px color-mix(in srgb, var(--highlight-line) 14%, transparent); }
466
+ .api-member > header { align-items: center; gap: 10px; }
467
+ .member-id { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; }
468
+ .api-member h3 { font-size: 15px; line-height: 1.35; }
469
+ .api-member h3 code { font-weight: 600; letter-spacing: -0.02em; }
470
+ .api-member .badge { padding: 2px 7px; border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent); font-size: 10.5px; line-height: 1.5; }
471
+ .api-member .badge.property { border-color: color-mix(in srgb, var(--ok) 24%, transparent); background: color-mix(in srgb, var(--ok) 10%, transparent); }
472
+ .api-member .copy-name { padding: 4px 8px; border-color: transparent; background: transparent; font-size: 11px; }
473
+ .api-member .copy-name:hover { border-color: var(--line); background: var(--card-2); color: var(--accent); }
474
+ .signature-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 12px 0 10px; }
475
+ .signature-item { min-width: 0; padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--card-2); }
476
+ .signature-item span { display: block; margin-bottom: 2px; color: var(--dim); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; }
477
+ .signature-item code { display: block; overflow-wrap: anywhere; color: var(--ink); font-size: 11.5px; line-height: 1.55; }
478
+ .api-member .doc { max-width: 92ch; margin: 9px 0 12px; color: var(--dim); line-height: 1.8; }
479
+ .example { margin: 9px 0 0; border-color: var(--line); border-radius: 9px; background: var(--card); }
480
+ .example summary { list-style: none; cursor: pointer; user-select: none; }
481
+ .example summary::-webkit-details-marker { display: none; }
482
+ .example-head { min-height: 34px; padding: 5px 9px; background: var(--card-2); font-size: 11.5px; }
483
+ .example-head::before { content: '›'; width: 15px; color: var(--dim); font-size: 17px; line-height: 1; transform: rotate(0); transition: transform 0.15s; }
484
+ .example[open] > .example-head::before { transform: rotate(90deg); }
485
+ .example-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
486
+ .example-actions { display: inline-flex; gap: 5px; margin-left: auto; }
487
+ .example-head button { margin: 0; padding: 3px 7px; border-color: transparent; background: transparent; font-size: 11px; }
488
+ .example-head button:hover { border-color: var(--line); background: var(--card); }
489
+ .example pre { padding: 13px 15px; background: var(--code-bg); }
490
+ .example code { font-size: 11.5px; line-height: 1.7; }
491
+ .option-table { overflow: hidden; border: 1px solid var(--line); border-radius: 11px; background: var(--card); }
492
+ .option-head, .api-option {
493
+ display: grid;
494
+ grid-template-columns: minmax(180px, 1.05fr) minmax(140px, 1fr) minmax(105px, 0.7fr) minmax(240px, 2.1fr);
495
+ gap: 12px;
496
+ align-items: start;
497
+ }
498
+ .option-head { padding: 9px 14px; border-bottom: 1px solid var(--line); background: var(--card-2); color: var(--dim); font-size: 10.5px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
499
+ .api-option { padding: 12px 14px; border-bottom: 1px solid var(--line); }
500
+ .api-option:last-child { border-bottom: 0; }
501
+ .api-option:hover { background: color-mix(in srgb, var(--accent) 3%, var(--card)); }
502
+ .api-option .name code { color: var(--accent-ink); font-weight: 600; }
503
+ .api-option .type { color: var(--ink); font: 11.5px/1.6 ui-monospace, Consolas, monospace; overflow-wrap: anywhere; }
504
+ .api-option .def { color: var(--dim); font: 11.5px/1.6 ui-monospace, Consolas, monospace; }
505
+ .api-option .doc { grid-column: auto; margin: 0; color: var(--dim); line-height: 1.7; }
506
+ .side-foot { margin-top: 12px; padding: 10px 7px 2px; border-top: 1px solid var(--line); color: var(--dim); font-size: 10.5px; line-height: 1.65; }
507
+ .side-foot kbd { padding: 1px 4px; border: 1px solid var(--line); border-radius: 4px; background: var(--card-2); font: inherit; }
508
+ @media (max-width: 1100px) {
509
+ .option-head, .api-option { grid-template-columns: minmax(160px, 1fr) minmax(140px, 1fr) minmax(220px, 1.8fr); }
510
+ .option-head span:nth-child(3), .api-option .def { display: none; }
511
+ }
512
+ @media (max-width: 900px) {
513
+ main { gap: 16px; padding-top: 16px; }
514
+ #side { position: static; max-height: none; }
515
+ #side-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 3px; }
516
+ .side-head { margin-bottom: 8px; }
517
+ #side .kinds { margin-bottom: 8px; }
518
+ .api-group > h2 { position: static; backdrop-filter: none; }
519
+ }
520
+ @media (max-width: 680px) {
521
+ .topbar { min-height: 0; flex-wrap: wrap; gap: 8px; padding: 9px 12px; }
522
+ .topbar .brand { width: 100%; }
523
+ .topbar .sub { display: none; }
524
+ .topbar .tools { width: 100%; flex-wrap: wrap; }
525
+ .search-box { flex: 1 1 100%; width: 100%; }
526
+ .tools a, .tools button { flex: 1 1 auto; text-align: center; }
527
+ .hero { padding: 20px 16px; border-radius: 13px; }
528
+ .hero h2 { font-size: 26px; }
529
+ .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
530
+ .api-member { padding: 14px; }
531
+ .signature-grid { grid-template-columns: 1fr; }
532
+ .option-head { display: none; }
533
+ .option-table { border: 0; background: transparent; overflow: visible; }
534
+ .api-option { grid-template-columns: 1fr; gap: 4px; margin-bottom: 8px; padding: 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--card); }
535
+ .api-option .def { display: block; }
536
+ .api-option .doc { margin-top: 3px; }
170
537
  }
171
538
  </style>
172
539
  </head>
173
540
  <body>
174
541
  <header class="topbar">
175
542
  <div class="brand">
176
- <a class="back" href="../index.html#GisView.md">← 文档中心</a>
543
+ <a class="brand-logo" href="../index.html#GisView.md" aria-label="返回文档中心">G</a>
177
544
  <div>
178
- <h1>GisView API 速查</h1>
179
- <p class="sub" id="summary"></p>
545
+ <h1>GisView API</h1>
546
+ <p class="sub">WebGL2 电网可视化组件 · 方法、配置与示例</p>
180
547
  </div>
181
548
  </div>
182
549
  <div class="tools">
183
- <input id="search" type="search" placeholder="搜方法 / 参数 / 说明 / options(Ctrl+K)" />
550
+ <label class="search-box">
551
+ <span class="search-icon" aria-hidden="true">⌕</span>
552
+ <input id="search" type="search" placeholder="搜索方法 / 参数 / 配置项(Ctrl+K)" />
553
+ </label>
554
+ <button id="theme-btn" type="button" title="跟随系统 / 浅色 / 深色 轮换(与文档中心共用选择)">主题:跟随系统</button>
184
555
  <button id="clear" type="button">清空</button>
185
- <a href="../playground/index.html">调试演示页 ↗</a>
186
- <a href="../../index.html" target="_blank" rel="noreferrer">打开 Vue 演示页 ↗</a>
556
+ <a href="../playground/index.html">调试页 ↗</a>
557
+ <a href="../../index.html" target="_blank" rel="noreferrer">Vue 演示 ↗</a>
187
558
  </div>
188
559
  </header>
189
560
  <main>
190
- <nav id="side">
191
- <h2>分组</h2>
192
- <div id="side-list"></div>
193
- <div class="kinds">
561
+ <nav id="side" aria-label="API 导航">
562
+ <div class="side-head">
563
+ <h2>目录</h2>
194
564
  <span id="hits"></span>
565
+ </div>
566
+ <div class="kinds" aria-label="成员类型筛选">
195
567
  <label><input type="checkbox" id="kind-method" checked /> 方法</label>
196
568
  <label><input type="checkbox" id="kind-property" checked /> 属性</label>
197
569
  <label><input type="checkbox" id="kind-option" checked /> options</label>
198
570
  </div>
571
+ <div id="side-list"></div>
572
+ <div class="side-foot">按 <kbd>Ctrl</kbd> + <kbd>K</kbd> 聚焦搜索;<kbd>Esc</kbd> 清空查询。</div>
199
573
  </nav>
200
574
  <section id="content">
201
- <article class="card" id="quickstart">
202
- <h2>三十秒上手</h2>
203
- <p class="sub">页面数据来自《GisView 手册》的 <code>docs/manual/16-实例方法总表.md</code> / <code>17-参数速查.md</code> / <code>18-逐方法示例.md</code>(由 <code>docs/steps/gen-api-index.mjs</code> 生成 <code>docs/demos/api-index.js</code>):每个成员都带一段能直接粘进控制台 / <code>.vue</code> 里的写法 —— 点「复制代码」拿走,或点「在调试页跑 →」带着这段代码打开调试演示页当场运行。</p>
204
- <div class="example">
205
- <div class="example-head"><span>最小可跑</span><button type="button" data-copy-target="quickstart-code">复制代码</button><button type="button" class="try-code" data-code-target="quickstart-code" title="把这段代码带到调试演示页并打开">在调试页跑 →</button></div>
206
- <pre><code id="quickstart-code"></code></pre>
575
+ <article class="hero" id="quickstart">
576
+ <div class="hero-copy">
577
+ <span class="eyebrow">API REFERENCE</span>
578
+ <h2>先看签名,再看参数,最后直接运行</h2>
579
+ <p class="hero-summary" id="summary"></p>
580
+ <div class="stat-grid" id="stats" aria-label="API 清单统计"></div>
581
+ </div>
582
+ <div class="hero-code">
583
+ <details class="example" open>
584
+ <summary class="example-head">
585
+ <span class="example-title">最小可跑</span>
586
+ <span class="example-actions">
587
+ <button type="button" data-copy-target="quickstart-code">复制代码</button>
588
+ <button type="button" class="try-code" data-code-target="quickstart-code" title="把这段代码带到调试演示页并打开">运行 →</button>
589
+ </span>
590
+ </summary>
591
+ <pre><code id="quickstart-code"></code></pre>
592
+ </details>
593
+ <p class="hero-note">数据与示例由 <code>docs/steps/gen-api-index.mjs</code> 从手册生成;每个成员都至少有这一段可直接粘走的代码。</p>
207
594
  </div>
208
595
  </article>
209
596
  <div id="cards"></div>
@@ -223,7 +610,7 @@
223
610
  " zoomScaleAuto: true // 自适应比例:包围盒变了节点与通道间距一起变(见下方选项)",
224
611
  "})",
225
612
  "",
226
- "// 2. 改数据 / 改视角 / 读读数:都在左侧这 243 个成员里",
613
+ "// 2. 改数据 / 改视角 / 读读数:都在左侧这 " + API_SUMMARY.members + " 个成员里",
227
614
  "view.updateLine('l1', { color: '#f00', width: 3 })",
228
615
  "view.focusOn('b', { padding: 60, select: true })",
229
616
  "const snapshot = view.snapshotScene()"
@@ -254,34 +641,88 @@
254
641
  const state = { query: '', kinds: { method: true, property: true, option: true } }
255
642
  const members = []
256
643
  const optionNodes = []
644
+ const sideButtons = new Map()
257
645
  const root = document.querySelector('#cards')
258
646
  const sideList = document.querySelector('#side-list')
259
647
  const hitsNode = document.querySelector('#hits')
648
+ /**
649
+ * 主题与文档中心共用 `gisDocs.reading`:在任一页面切过深色,另一页下次打开也跟上。
650
+ * 三档仍是「跟随系统 / 浅色 / 深色」,这里只读写 theme 字段,不碰字号与阅读宽度。
651
+ */
652
+ const READING_KEY = 'gisDocs.reading'
653
+ const darkMedia = typeof matchMedia === 'function' ? matchMedia('(prefers-color-scheme: dark)') : null
654
+ let themeMode = 'auto'
655
+ try {
656
+ const saved = JSON.parse(localStorage.getItem(READING_KEY) ?? 'null')
657
+ if (saved?.theme === 'light' || saved?.theme === 'dark') {
658
+ themeMode = saved.theme
659
+ }
660
+ }
661
+ catch (error) { /* 存过的值坏了就当没存过 */ }
662
+ const themeButton = document.querySelector('#theme-btn')
663
+ const effectiveTheme = () => (themeMode === 'auto' ? (darkMedia?.matches ? 'dark' : 'light') : themeMode)
664
+ const applyTheme = () => {
665
+ document.documentElement.classList.toggle('theme-dark', effectiveTheme() === 'dark')
666
+ themeButton.textContent = `主题:${themeMode === 'auto' ? '跟随系统' : themeMode === 'dark' ? '深色' : '浅色'}`
667
+ themeButton.setAttribute('aria-label', `当前${themeMode === 'auto' ? '跟随系统' : themeMode === 'dark' ? '深色' : '浅色'}主题,点击切换`)
668
+ }
669
+ const setTheme = (theme) => {
670
+ themeMode = theme === 'light' || theme === 'dark' ? theme : 'auto'
671
+ try {
672
+ const saved = JSON.parse(localStorage.getItem(READING_KEY) ?? 'null') ?? {}
673
+ localStorage.setItem(READING_KEY, JSON.stringify({ ...saved, theme: themeMode }))
674
+ }
675
+ catch (error) { /* 隐私模式 / 禁用存储时忽略 */ }
676
+ applyTheme()
677
+ return themeMode
678
+ }
679
+ const cycleTheme = () => setTheme(themeMode === 'auto' ? 'light' : themeMode === 'light' ? 'dark' : 'auto')
680
+ applyTheme()
681
+ themeButton.addEventListener('click', cycleTheme)
682
+ darkMedia?.addEventListener?.('change', () => {
683
+ if (themeMode === 'auto') {
684
+ applyTheme()
685
+ }
686
+ })
260
687
 
261
688
  document.querySelector('#summary').textContent =
262
- `${API_SUMMARY.members} 个实例成员(${API_SUMMARY.methods} 个方法 / ${API_SUMMARY.properties} 个属性)· ${API_SUMMARY.options} 个 options · ${API_SUMMARY.sections} 个分组 · 每个成员都带示例`
689
+ `${API_SUMMARY.members} 个实例成员、${API_SUMMARY.options} 个配置项,按 ${API_SUMMARY.sections} 个主题分组。搜索、签名、参数与返回值、可直接运行的示例放在同一页。`
690
+
691
+ const exampleCount = API_SECTIONS.reduce((total, section) => total + section.items.reduce((sum, item) => sum + item.examples.length, 0), 0)
692
+ document.querySelector('#stats').innerHTML = [
693
+ ['members', API_SUMMARY.members, '实例成员'],
694
+ ['methods', API_SUMMARY.methods, '方法'],
695
+ ['properties', API_SUMMARY.properties, '属性'],
696
+ ['options', API_SUMMARY.options, '配置项'],
697
+ ['sections', API_SUMMARY.sections, '主题分组'],
698
+ ['examples', exampleCount, '可运行示例']
699
+ ].map(([key, value, label]) => `<div class="api-stat" data-stat="${key}"><b>${Number(value).toLocaleString()}</b><span>${label}</span></div>`).join('')
263
700
 
264
701
  const exampleHtml = (example, index) => `
265
- <div class="example">
266
- <div class="example-head">
267
- <span>示例${index ? ` ${index + 1}` : ''} · 来自「${escapeHtml(example.group)}」${example.from === 'code' ? '(同一节里真实的调用)' : ''}</span>
268
- <button type="button" class="copy-code">复制代码</button>
269
- <button type="button" class="try-code" title="把这段代码带到调试演示页并打开">在调试页跑 →</button>
270
- </div>
702
+ <details class="example"${index === 0 ? ' open' : ''}>
703
+ <summary class="example-head">
704
+ <span class="example-title">示例${index ? ` ${index + 1}` : ''} · ${escapeHtml(example.group)}${example.from === 'code' ? ' · 文档中的真实调用' : ''}</span>
705
+ <span class="example-actions">
706
+ <button type="button" class="copy-code">复制</button>
707
+ <button type="button" class="try-code" title="把这段代码带到调试演示页并打开">运行 →</button>
708
+ </span>
709
+ </summary>
271
710
  <pre><code>${highlightCode(example.code)}</code></pre>
272
- </div>`
711
+ </details>`
273
712
 
274
713
  const memberHtml = (member) => `
275
714
  <article class="api-member" id="${escapeHtml(member.name)}" data-name="${escapeHtml(member.name)}" data-kind="${member.kind}" data-examples="${member.examples.length}">
276
715
  <header>
277
- <h3><code>${escapeHtml(member.name)}</code></h3>
278
- <span class="badge${member.kind === 'property' ? ' property' : ''}">${member.kind === 'property' ? '属性' : '方法'}</span>
716
+ <div class="member-id">
717
+ <span class="badge${member.kind === 'property' ? ' property' : ''}">${member.kind === 'property' ? '属性' : '方法'}</span>
718
+ <h3><code>${escapeHtml(member.name)}</code></h3>
719
+ </div>
279
720
  <button type="button" class="copy-name" title="复制成员名">复制名字</button>
280
721
  </header>
281
- <dl>
282
- <dt>参数</dt><dd>${escapeHtml(member.params || '无')}</dd>
283
- <dt>返回</dt><dd>${escapeHtml(member.returns || '—')}</dd>
284
- </dl>
722
+ <div class="signature-grid">
723
+ <div class="signature-item"><span>参数</span><code>${escapeHtml(member.params || '无')}</code></div>
724
+ <div class="signature-item"><span>返回</span><code>${escapeHtml(member.returns || '—')}</code></div>
725
+ </div>
285
726
  <p class="doc">${escapeHtml(member.doc || '')}</p>
286
727
  ${member.examples.map(exampleHtml).join('\n')}
287
728
  </article>`
@@ -292,15 +733,20 @@
292
733
  group.className = 'api-group'
293
734
  group.id = `group-${groupIndex}`
294
735
  group.dataset.group = String(groupIndex)
295
- group.innerHTML = `<h2>${escapeHtml(section.title)} <b>${section.items.length} 个</b></h2>` + section.items.map(memberHtml).join('\n')
736
+ const groupNumber = String(groupIndex + 1).padStart(2, '0')
737
+ group.innerHTML = `<h2><span class="group-index">${groupNumber}</span><span>${escapeHtml(section.title)}</span><b>${section.items.length} 个</b></h2>` + section.items.map(memberHtml).join('\n')
296
738
  root.appendChild(group)
297
739
  const side = document.createElement('button')
298
740
  side.type = 'button'
299
741
  side.className = 'side-item'
300
742
  side.dataset.group = String(groupIndex)
301
743
  side.innerHTML = `<span>${escapeHtml(section.title)}</span><b>${section.items.length}</b>`
302
- side.addEventListener('click', () => group.scrollIntoView({ behavior: 'smooth', block: 'start' }))
744
+ side.addEventListener('click', () => {
745
+ setActiveGroup(String(groupIndex))
746
+ group.scrollIntoView({ behavior: 'smooth', block: 'start' })
747
+ })
303
748
  sideList.appendChild(side)
749
+ sideButtons.set(String(groupIndex), side)
304
750
  for (const node of group.querySelectorAll('.api-member')) {
305
751
  members.push({ node, section: section.title })
306
752
  }
@@ -310,7 +756,7 @@
310
756
  const optionsGroup = document.createElement('section')
311
757
  optionsGroup.className = 'api-group'
312
758
  optionsGroup.id = 'options'
313
- optionsGroup.innerHTML = `<h2>options <b>${API_OPTIONS.length} 个</b></h2><div id="option-list"></div>`
759
+ optionsGroup.innerHTML = `<h2><span class="group-index">${String(groupIndex + 1).padStart(2, '0')}</span><span>options</span><b>${API_OPTIONS.length} 个</b></h2><div class="option-table"><div class="option-head" role="row"><span>字段</span><span>类型</span><span>默认值</span><span>说明</span></div><div id="option-list"></div></div>`
314
760
  root.appendChild(optionsGroup)
315
761
  const optionList = optionsGroup.querySelector('#option-list')
316
762
  for (const option of API_OPTIONS) {
@@ -326,8 +772,40 @@
326
772
  sideOption.type = 'button'
327
773
  sideOption.className = 'side-item'
328
774
  sideOption.innerHTML = `<span>options</span><b>${API_OPTIONS.length}</b>`
329
- sideOption.addEventListener('click', () => optionsGroup.scrollIntoView({ behavior: 'smooth', block: 'start' }))
775
+ sideOption.addEventListener('click', () => {
776
+ setActiveGroup('options')
777
+ optionsGroup.scrollIntoView({ behavior: 'smooth', block: 'start' })
778
+ })
330
779
  sideList.appendChild(sideOption)
780
+ sideButtons.set('options', sideOption)
781
+
782
+ /** 左侧分组跟着当前读到的位置亮:点分组时先亮,滚动时由 IntersectionObserver 接上 */
783
+ const setActiveGroup = (key) => {
784
+ for (const [id, button] of sideButtons) {
785
+ const on = id === key
786
+ button.classList.toggle('on', on)
787
+ if (on) {
788
+ button.setAttribute('aria-current', 'location')
789
+ }
790
+ else {
791
+ button.removeAttribute('aria-current')
792
+ }
793
+ }
794
+ }
795
+ setActiveGroup('0')
796
+ if (typeof IntersectionObserver === 'function') {
797
+ const groupObserver = new IntersectionObserver((entries) => {
798
+ const visible = entries
799
+ .filter(entry => entry.isIntersecting)
800
+ .sort((left, right) => Math.abs(left.boundingClientRect.top - 82) - Math.abs(right.boundingClientRect.top - 82))[0]
801
+ if (visible) {
802
+ setActiveGroup(visible.target.id === 'options' ? 'options' : visible.target.dataset.group)
803
+ }
804
+ }, { rootMargin: '-82px 0px -58% 0px', threshold: [0, 0.05, 0.25] })
805
+ for (const group of root.querySelectorAll('.api-group')) {
806
+ groupObserver.observe(group)
807
+ }
808
+ }
331
809
 
332
810
  const haystack = node => (node.textContent || '').toLowerCase()
333
811
  const applyFilter = () => {
@@ -407,6 +885,10 @@
407
885
  if (!button) {
408
886
  return
409
887
  }
888
+ // 示例头里的按钮在 <summary> 内:不拦默认动作会顺带把代码块折起来
889
+ if (button.closest('.example-head')) {
890
+ event.preventDefault()
891
+ }
410
892
  const card = button.closest('.api-member')
411
893
  if (button.classList.contains('copy-name') && card) {
412
894
  copy(card.dataset.name, button)
@@ -463,6 +945,10 @@
463
945
  state,
464
946
  setQuery,
465
947
  select: selectMember,
948
+ theme: () => themeMode,
949
+ setTheme,
950
+ cycleTheme,
951
+ activeGroup: () => [...sideButtons].find(([_key, button]) => button.classList.contains('on'))?.[0] ?? null,
466
952
  memberNames: () => members.map(entry => entry.node.dataset.name),
467
953
  optionNames: () => optionNodes.map(node => node.dataset.name)
468
954
  }