@softwarity/geojson-editor 1.0.24 → 1.0.25
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/README.md +168 -32
- package/dist/geojson-editor.js +2 -2
- package/package.json +9 -3
- package/src/constants.ts +0 -3
- package/src/geojson-editor.css +48 -43
- package/src/geojson-editor.ts +73 -83
- package/src/types.ts +0 -30
- package/themes/github.css +43 -0
- package/themes/monokai.css +43 -0
- package/themes/solarized.css +44 -0
- package/themes/vscode.css +43 -0
- package/types/types.d.ts +0 -28
package/src/geojson-editor.css
CHANGED
|
@@ -11,10 +11,15 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
:host {
|
|
14
|
+
/* Color scheme - inherits from parent, falls back to system preference if not set */
|
|
15
|
+
color-scheme: inherit;
|
|
16
|
+
|
|
17
|
+
/* Layout variables (internal only) */
|
|
14
18
|
--line-height: 19.5px;
|
|
15
19
|
--gutter-width: 50px;
|
|
16
20
|
--editor-padding-y: 8px;
|
|
17
21
|
--editor-padding-x: 12px;
|
|
22
|
+
|
|
18
23
|
display: flex;
|
|
19
24
|
flex-direction: column;
|
|
20
25
|
position: relative;
|
|
@@ -39,7 +44,7 @@
|
|
|
39
44
|
position: relative;
|
|
40
45
|
width: 100%;
|
|
41
46
|
flex: 1;
|
|
42
|
-
background: var(--bg-color, #fff);
|
|
47
|
+
background: var(--geojson-editor-bg-color, light-dark(#fff, #2b2b2b));
|
|
43
48
|
display: flex;
|
|
44
49
|
overflow: hidden;
|
|
45
50
|
}
|
|
@@ -47,8 +52,8 @@
|
|
|
47
52
|
/* ========== Gutter ========== */
|
|
48
53
|
.gutter {
|
|
49
54
|
width: var(--gutter-width);
|
|
50
|
-
background: var(--gutter-bg, #f0f0f0);
|
|
51
|
-
border-right: 1px solid var(--gutter-border, #e0e0e0);
|
|
55
|
+
background: var(--geojson-editor-gutter-bg, light-dark(#f0f0f0, #313335));
|
|
56
|
+
border-right: 1px solid var(--geojson-editor-gutter-border, light-dark(#e0e0e0, #3c3f41));
|
|
52
57
|
overflow: hidden;
|
|
53
58
|
flex-shrink: 0;
|
|
54
59
|
position: relative;
|
|
@@ -90,12 +95,12 @@
|
|
|
90
95
|
top: 0;
|
|
91
96
|
bottom: 0;
|
|
92
97
|
width: 3px;
|
|
93
|
-
background: var(--error-color, #dc3545);
|
|
98
|
+
background: var(--geojson-editor-error-color, light-dark(#dc3545, #ff6b68));
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
.line-number {
|
|
97
102
|
font-size: 11px;
|
|
98
|
-
color: var(--gutter-text, #999);
|
|
103
|
+
color: var(--geojson-editor-gutter-text, light-dark(#999, #606366));
|
|
99
104
|
user-select: none;
|
|
100
105
|
min-width: 20px;
|
|
101
106
|
text-align: right;
|
|
@@ -118,7 +123,7 @@
|
|
|
118
123
|
flex-shrink: 0;
|
|
119
124
|
background: transparent;
|
|
120
125
|
border: none;
|
|
121
|
-
color: var(--json-punct, #a9b7c6);
|
|
126
|
+
color: var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6));
|
|
122
127
|
font-size: 10px;
|
|
123
128
|
display: flex;
|
|
124
129
|
align-items: center;
|
|
@@ -178,7 +183,7 @@
|
|
|
178
183
|
padding: var(--editor-padding-y) var(--editor-padding-x);
|
|
179
184
|
overscroll-behavior: contain; /* Prevent scroll chaining to parent */
|
|
180
185
|
scrollbar-width: thin;
|
|
181
|
-
scrollbar-color: var(--control-border, #c0c0c0) var(--control-bg, #e8e8e8);
|
|
186
|
+
scrollbar-color: var(--geojson-editor-control-border, light-dark(#c0c0c0, #5a5a5a)) var(--geojson-editor-control-bg, light-dark(#e8e8e8, #3c3f41));
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
/* Scroll content - defines total scrollable height */
|
|
@@ -209,7 +214,7 @@
|
|
|
209
214
|
position: absolute;
|
|
210
215
|
width: 2px;
|
|
211
216
|
height: 1em;
|
|
212
|
-
background: var(--caret-color, #000);
|
|
217
|
+
background: var(--geojson-editor-caret-color, light-dark(#000, #bbb));
|
|
213
218
|
top: 0.15em;
|
|
214
219
|
pointer-events: none;
|
|
215
220
|
animation: cursor-blink 1s step-end infinite;
|
|
@@ -237,7 +242,7 @@
|
|
|
237
242
|
position: absolute;
|
|
238
243
|
height: 100%;
|
|
239
244
|
top: 0;
|
|
240
|
-
background: var(--selection-color, rgba(51, 153, 255, 0.3));
|
|
245
|
+
background: var(--geojson-editor-selection-color, light-dark(rgba(51, 153, 255, 0.3), rgba(51, 153, 255, 0.4)));
|
|
241
246
|
pointer-events: none;
|
|
242
247
|
z-index: 0;
|
|
243
248
|
}
|
|
@@ -259,47 +264,47 @@
|
|
|
259
264
|
}
|
|
260
265
|
|
|
261
266
|
/* ========== Syntax Highlighting ========== */
|
|
262
|
-
.json-key { color: var(--json-key, #660e7a); }
|
|
263
|
-
.json-string { color: var(--json-string, #008000); }
|
|
264
|
-
.json-number { color: var(--json-number, #00f); }
|
|
265
|
-
.json-boolean, .json-null { color: var(--json-boolean, #000080); }
|
|
266
|
-
.json-punctuation { color: var(--json-punct, #000); }
|
|
267
|
-
.json-error { color: var(--json-error, #f00); }
|
|
267
|
+
.json-key { color: var(--geojson-editor-json-key, light-dark(#660e7a, #9876aa)); }
|
|
268
|
+
.json-string { color: var(--geojson-editor-json-string, light-dark(#008000, #6a8759)); }
|
|
269
|
+
.json-number { color: var(--geojson-editor-json-number, light-dark(#00f, #6897bb)); }
|
|
270
|
+
.json-boolean, .json-null { color: var(--geojson-editor-json-boolean, light-dark(#000080, #cc7832)); }
|
|
271
|
+
.json-punctuation { color: var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6)); }
|
|
272
|
+
.json-error { color: var(--geojson-editor-json-error, light-dark(#f00, #ff6b68)); }
|
|
268
273
|
|
|
269
|
-
.geojson-key { color: var(--geojson-key, #660e7a); font-weight: 600; }
|
|
270
|
-
.geojson-type { color: var(--geojson-type, #008000); font-weight: 600; }
|
|
271
|
-
.geojson-type-invalid { color: var(--geojson-type-invalid, #f00); font-weight: 600; }
|
|
274
|
+
.geojson-key { color: var(--geojson-editor-geojson-key, light-dark(#660e7a, #9876aa)); font-weight: 600; }
|
|
275
|
+
.geojson-type { color: var(--geojson-editor-geojson-type, light-dark(#008000, #6a8759)); font-weight: 600; }
|
|
276
|
+
.geojson-type-invalid { color: var(--geojson-editor-geojson-type-invalid, light-dark(#f00, #ff6b68)); font-weight: 600; }
|
|
272
277
|
|
|
273
278
|
/* Collapsed node styling */
|
|
274
279
|
.collapsed-bracket-array,
|
|
275
280
|
.collapsed-bracket-object {
|
|
276
|
-
color: var(--json-punct, #000);
|
|
281
|
+
color: var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6));
|
|
277
282
|
}
|
|
278
283
|
.collapsed-bracket-array::after,
|
|
279
284
|
.collapsed-bracket-object::after {
|
|
280
285
|
content: '…';
|
|
281
|
-
color: var(--json-punct, #
|
|
286
|
+
color: var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6));
|
|
282
287
|
}
|
|
283
288
|
|
|
284
289
|
/* ========== Prefix/Suffix ========== */
|
|
285
290
|
.prefix-wrapper, .suffix-wrapper {
|
|
286
291
|
display: flex;
|
|
287
292
|
flex-shrink: 0;
|
|
288
|
-
background: var(--bg-color, #fff);
|
|
293
|
+
background: var(--geojson-editor-bg-color, light-dark(#fff, #2b2b2b));
|
|
289
294
|
}
|
|
290
295
|
|
|
291
296
|
.prefix-gutter, .suffix-gutter {
|
|
292
297
|
width: var(--gutter-width);
|
|
293
|
-
background: var(--gutter-bg, #f0f0f0);
|
|
294
|
-
border-right: 1px solid var(--gutter-border, #e0e0e0);
|
|
298
|
+
background: var(--geojson-editor-gutter-bg, light-dark(#f0f0f0, #313335));
|
|
299
|
+
border-right: 1px solid var(--geojson-editor-gutter-border, light-dark(#e0e0e0, #3c3f41));
|
|
295
300
|
flex-shrink: 0;
|
|
296
301
|
}
|
|
297
302
|
|
|
298
303
|
.editor-prefix, .editor-suffix {
|
|
299
304
|
flex: 1;
|
|
300
305
|
padding: 4px 12px;
|
|
301
|
-
color: var(--text-color, #000);
|
|
302
|
-
background: var(--bg-color, #fff);
|
|
306
|
+
color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
|
|
307
|
+
background: var(--geojson-editor-bg-color, light-dark(#fff, #2b2b2b));
|
|
303
308
|
user-select: none;
|
|
304
309
|
white-space: pre-wrap;
|
|
305
310
|
word-wrap: break-word;
|
|
@@ -316,7 +321,7 @@
|
|
|
316
321
|
transform: translateY(-50%);
|
|
317
322
|
background: transparent;
|
|
318
323
|
border: none;
|
|
319
|
-
color: var(--text-color, #000);
|
|
324
|
+
color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
|
|
320
325
|
opacity: 0.15;
|
|
321
326
|
cursor: pointer;
|
|
322
327
|
font-size: 0.7rem;
|
|
@@ -339,8 +344,8 @@
|
|
|
339
344
|
top: 2rem;
|
|
340
345
|
right: 0.5rem;
|
|
341
346
|
z-index: 1000;
|
|
342
|
-
background: var(--bg-color, #
|
|
343
|
-
border: 1px solid var(--gutter-border, #e0e0e0);
|
|
347
|
+
background: var(--geojson-editor-bg-color, light-dark(#fff, #2b2b2b));
|
|
348
|
+
border: 1px solid var(--geojson-editor-gutter-border, light-dark(#e0e0e0, #3c3f41));
|
|
344
349
|
border-radius: 6px;
|
|
345
350
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
346
351
|
padding: 12px 16px;
|
|
@@ -356,13 +361,13 @@
|
|
|
356
361
|
.info-popup-title {
|
|
357
362
|
font-weight: bold;
|
|
358
363
|
font-size: 13px;
|
|
359
|
-
color: var(--text-color, #
|
|
364
|
+
color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
|
|
360
365
|
margin-bottom: 4px;
|
|
361
366
|
}
|
|
362
367
|
.info-popup-version {
|
|
363
368
|
display: block;
|
|
364
369
|
font-size: 11px;
|
|
365
|
-
color: var(--text-color, #
|
|
370
|
+
color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
|
|
366
371
|
opacity: 0.6;
|
|
367
372
|
margin-bottom: 8px;
|
|
368
373
|
text-decoration: none;
|
|
@@ -373,7 +378,7 @@
|
|
|
373
378
|
}
|
|
374
379
|
.info-popup-copyright {
|
|
375
380
|
font-size: 10px;
|
|
376
|
-
color: var(--text-color, #
|
|
381
|
+
color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
|
|
377
382
|
opacity: 0.5;
|
|
378
383
|
}
|
|
379
384
|
|
|
@@ -384,7 +389,7 @@
|
|
|
384
389
|
transform: translateY(-50%);
|
|
385
390
|
background: transparent;
|
|
386
391
|
border: none;
|
|
387
|
-
color: var(--text-color, #000);
|
|
392
|
+
color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
|
|
388
393
|
opacity: 0.3;
|
|
389
394
|
cursor: pointer;
|
|
390
395
|
font-size: 0.65rem;
|
|
@@ -413,7 +418,7 @@
|
|
|
413
418
|
.error-nav-btn {
|
|
414
419
|
background: transparent;
|
|
415
420
|
border: none;
|
|
416
|
-
color: var(--error-color, #dc3545);
|
|
421
|
+
color: var(--geojson-editor-error-color, light-dark(#dc3545, #ff6b68));
|
|
417
422
|
cursor: pointer;
|
|
418
423
|
font-size: 8px;
|
|
419
424
|
width: 16px;
|
|
@@ -429,7 +434,7 @@
|
|
|
429
434
|
opacity: 1;
|
|
430
435
|
}
|
|
431
436
|
.error-count {
|
|
432
|
-
color: var(--error-color, #dc3545);
|
|
437
|
+
color: var(--geojson-editor-error-color, light-dark(#dc3545, #ff6b68));
|
|
433
438
|
font-size: 11px;
|
|
434
439
|
min-width: 20px;
|
|
435
440
|
text-align: center;
|
|
@@ -437,9 +442,9 @@
|
|
|
437
442
|
|
|
438
443
|
/* ========== Scrollbar ========== */
|
|
439
444
|
.viewport::-webkit-scrollbar { width: 10px; height: 10px; }
|
|
440
|
-
.viewport::-webkit-scrollbar-track { background: var(--control-bg, #e8e8e8); }
|
|
441
|
-
.viewport::-webkit-scrollbar-thumb { background: var(--control-border, #c0c0c0); border-radius: 5px; }
|
|
442
|
-
.viewport::-webkit-scrollbar-thumb:hover { background: var(--control-color, #000080); }
|
|
445
|
+
.viewport::-webkit-scrollbar-track { background: var(--geojson-editor-control-bg, light-dark(#e8e8e8, #3c3f41)); }
|
|
446
|
+
.viewport::-webkit-scrollbar-thumb { background: var(--geojson-editor-control-border, light-dark(#c0c0c0, #5a5a5a)); border-radius: 5px; }
|
|
447
|
+
.viewport::-webkit-scrollbar-thumb:hover { background: var(--geojson-editor-control-color, light-dark(#000080, #cc7832)); }
|
|
443
448
|
|
|
444
449
|
/* ========== Inline Controls (using ::before pseudo-elements) ========== */
|
|
445
450
|
|
|
@@ -464,24 +469,24 @@
|
|
|
464
469
|
.json-color:hover::before,
|
|
465
470
|
.json-boolean:hover::before {
|
|
466
471
|
transform: translateY(-50%) scale(1.2);
|
|
467
|
-
border-color: var(--control-color, #000080);
|
|
472
|
+
border-color: var(--geojson-editor-control-color, light-dark(#000080, #cc7832));
|
|
468
473
|
}
|
|
469
474
|
|
|
470
475
|
/* Color swatch specific styles */
|
|
471
476
|
.json-color::before {
|
|
472
477
|
background-color: var(--swatch-color);
|
|
473
|
-
border: 1px solid var(--json-punct, #a9b7c6);
|
|
478
|
+
border: 1px solid var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6));
|
|
474
479
|
}
|
|
475
480
|
|
|
476
481
|
/* Boolean checkbox specific styles */
|
|
477
482
|
.json-boolean::before {
|
|
478
|
-
border: 1.5px solid var(--control-border, #c0c0c0);
|
|
483
|
+
border: 1.5px solid var(--geojson-editor-control-border, light-dark(#c0c0c0, #5a5a5a));
|
|
479
484
|
background: transparent;
|
|
480
485
|
}
|
|
481
486
|
.json-bool-true::before {
|
|
482
487
|
content: '✔';
|
|
483
|
-
border-color: var(--control-color, #000080);
|
|
484
|
-
color: var(--control-color, #000080);
|
|
488
|
+
border-color: var(--geojson-editor-control-color, light-dark(#000080, #cc7832));
|
|
489
|
+
color: var(--geojson-editor-control-color, light-dark(#000080, #cc7832));
|
|
485
490
|
font-size: 8px;
|
|
486
491
|
display: flex;
|
|
487
492
|
align-items: center;
|
|
@@ -496,7 +501,7 @@
|
|
|
496
501
|
left: 0;
|
|
497
502
|
top: 3px;
|
|
498
503
|
font-size: 10px;
|
|
499
|
-
color: var(--control-color, #000080);
|
|
504
|
+
color: var(--geojson-editor-control-color, light-dark(#000080, #cc7832));
|
|
500
505
|
opacity: 0.6;
|
|
501
506
|
cursor: pointer;
|
|
502
507
|
z-index: 1;
|
package/src/geojson-editor.ts
CHANGED
|
@@ -4,8 +4,7 @@ import type { Feature } from 'geojson';
|
|
|
4
4
|
|
|
5
5
|
// ========== Imports from extracted modules ==========
|
|
6
6
|
import type {
|
|
7
|
-
SetOptions
|
|
8
|
-
ThemeSettings
|
|
7
|
+
SetOptions
|
|
9
8
|
} from './types.js';
|
|
10
9
|
|
|
11
10
|
import type {
|
|
@@ -35,19 +34,18 @@ import {
|
|
|
35
34
|
RE_BRACKET_POS,
|
|
36
35
|
RE_IS_WORD_CHAR,
|
|
37
36
|
RE_ATTR_AND_BOOL_VALUE,
|
|
38
|
-
RE_TO_KEBAB,
|
|
39
37
|
RE_OPEN_BRACES,
|
|
40
38
|
RE_CLOSE_BRACES,
|
|
41
39
|
RE_OPEN_BRACKETS,
|
|
42
40
|
RE_CLOSE_BRACKET
|
|
43
41
|
} from './constants.js';
|
|
44
42
|
|
|
45
|
-
import { createElement, countBrackets
|
|
43
|
+
import { createElement, countBrackets } from './utils.js';
|
|
46
44
|
import { validateGeoJSON, normalizeToFeatures } from './validation.js';
|
|
47
45
|
import { highlightSyntax, namedColorToHex, isNamedColor } from './syntax-highlighter.js';
|
|
48
46
|
|
|
49
47
|
// Re-export public types
|
|
50
|
-
export type { SetOptions
|
|
48
|
+
export type { SetOptions } from './types.js';
|
|
51
49
|
|
|
52
50
|
// Alias for minification
|
|
53
51
|
const _ce = createElement;
|
|
@@ -94,8 +92,6 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
94
92
|
private renderTimer: ReturnType<typeof setTimeout> | undefined = undefined;
|
|
95
93
|
private inputTimer: ReturnType<typeof setTimeout> | undefined = undefined;
|
|
96
94
|
|
|
97
|
-
// ========== Theme ==========
|
|
98
|
-
themes: ThemeSettings = { dark: {}, light: {} };
|
|
99
95
|
|
|
100
96
|
// ========== Undo/Redo History ==========
|
|
101
97
|
private _undoStack: EditorSnapshot[] = [];
|
|
@@ -116,6 +112,7 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
116
112
|
private _contextMapFirstLine: string | undefined = undefined;
|
|
117
113
|
private _contextMapLastLine: string | undefined = undefined;
|
|
118
114
|
private _errorLinesCache: Set<number> | null = null;
|
|
115
|
+
private _lastCursorFeatureIndex: number | null = null; // For current-feature event deduplication
|
|
119
116
|
|
|
120
117
|
// ========== Cached DOM Elements ==========
|
|
121
118
|
private _viewport: HTMLElement | null = null;
|
|
@@ -440,7 +437,7 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
440
437
|
|
|
441
438
|
// ========== Observed Attributes ==========
|
|
442
439
|
static get observedAttributes() {
|
|
443
|
-
return ['readonly', 'value', 'placeholder', '
|
|
440
|
+
return ['readonly', 'value', 'placeholder', 'internal-add-shortcut'];
|
|
444
441
|
}
|
|
445
442
|
|
|
446
443
|
// ========== Lifecycle ==========
|
|
@@ -449,7 +446,6 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
449
446
|
this._cacheElements();
|
|
450
447
|
this.setupEventListeners();
|
|
451
448
|
this.updatePrefixSuffix();
|
|
452
|
-
this.updateThemeCSS();
|
|
453
449
|
|
|
454
450
|
if (this.value) {
|
|
455
451
|
this.setValue(this.value);
|
|
@@ -484,9 +480,6 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
484
480
|
case 'placeholder':
|
|
485
481
|
this.updatePlaceholderContent();
|
|
486
482
|
break;
|
|
487
|
-
case 'dark-selector':
|
|
488
|
-
this.updateThemeCSS();
|
|
489
|
-
break;
|
|
490
483
|
}
|
|
491
484
|
}
|
|
492
485
|
|
|
@@ -697,12 +690,16 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
697
690
|
editorWrapper.classList.add('focused');
|
|
698
691
|
this._invalidateRenderCache(); // Force re-render to show cursor
|
|
699
692
|
this.scheduleRender();
|
|
693
|
+
// Emit current feature on focus (force to always emit on focus gain)
|
|
694
|
+
this._emitCurrentFeature(true);
|
|
700
695
|
});
|
|
701
696
|
|
|
702
697
|
hiddenTextarea.addEventListener('blur', () => {
|
|
703
698
|
editorWrapper.classList.remove('focused');
|
|
704
699
|
this._invalidateRenderCache(); // Force re-render to hide cursor
|
|
705
700
|
this.scheduleRender();
|
|
701
|
+
// Emit null on blur
|
|
702
|
+
this._emitCurrentFeatureNull();
|
|
706
703
|
});
|
|
707
704
|
|
|
708
705
|
// Scroll handling
|
|
@@ -1324,11 +1321,16 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
1324
1321
|
|
|
1325
1322
|
linesContainer.innerHTML = '';
|
|
1326
1323
|
linesContainer.appendChild(fragment);
|
|
1327
|
-
|
|
1324
|
+
|
|
1328
1325
|
// Render gutter with same range
|
|
1329
1326
|
this.renderGutter(startIndex, endIndex);
|
|
1327
|
+
|
|
1328
|
+
// Emit current-feature event if feature changed (only when editor is focused)
|
|
1329
|
+
if (this._editorWrapper?.classList.contains('focused')) {
|
|
1330
|
+
this._emitCurrentFeature();
|
|
1331
|
+
}
|
|
1330
1332
|
}
|
|
1331
|
-
|
|
1333
|
+
|
|
1332
1334
|
/**
|
|
1333
1335
|
* Insert cursor element at the specified column position
|
|
1334
1336
|
* Uses absolute positioning to avoid affecting text layout
|
|
@@ -2757,7 +2759,14 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
2757
2759
|
// Try to parse as GeoJSON and normalize
|
|
2758
2760
|
let pastedFeatureCount = 0;
|
|
2759
2761
|
try {
|
|
2760
|
-
|
|
2762
|
+
// First try direct parse (single Feature, Feature[], or FeatureCollection)
|
|
2763
|
+
let parsed;
|
|
2764
|
+
try {
|
|
2765
|
+
parsed = JSON.parse(text);
|
|
2766
|
+
} catch {
|
|
2767
|
+
// If direct parse fails, try wrapping with [] (for "feature, feature" format from editor copy)
|
|
2768
|
+
parsed = JSON.parse('[' + text + ']');
|
|
2769
|
+
}
|
|
2761
2770
|
const features = normalizeToFeatures(parsed);
|
|
2762
2771
|
pastedFeatureCount = features.length;
|
|
2763
2772
|
// Valid GeoJSON - insert formatted features
|
|
@@ -3532,6 +3541,51 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
3532
3541
|
}
|
|
3533
3542
|
}
|
|
3534
3543
|
|
|
3544
|
+
/**
|
|
3545
|
+
* Emit current-feature event when cursor moves to a different feature
|
|
3546
|
+
* Only emits when the feature changes (not on every cursor move)
|
|
3547
|
+
* @param force - If true, emit even if feature hasn't changed (used on focus)
|
|
3548
|
+
*/
|
|
3549
|
+
private _emitCurrentFeature(force: boolean = false): void {
|
|
3550
|
+
const featureIndex = this._getFeatureIndexForLine(this.cursorLine);
|
|
3551
|
+
|
|
3552
|
+
// Only emit if feature changed (unless forced)
|
|
3553
|
+
if (!force && featureIndex === this._lastCursorFeatureIndex) return;
|
|
3554
|
+
this._lastCursorFeatureIndex = featureIndex;
|
|
3555
|
+
|
|
3556
|
+
if (featureIndex === -1) {
|
|
3557
|
+
// Cursor is not in a feature
|
|
3558
|
+
this.dispatchEvent(new CustomEvent('current-feature', {
|
|
3559
|
+
detail: null,
|
|
3560
|
+
bubbles: true,
|
|
3561
|
+
composed: true
|
|
3562
|
+
}));
|
|
3563
|
+
} else {
|
|
3564
|
+
// Get the feature at cursor
|
|
3565
|
+
const features = this._parseFeatures();
|
|
3566
|
+
const feature = features[featureIndex] || null;
|
|
3567
|
+
|
|
3568
|
+
this.dispatchEvent(new CustomEvent('current-feature', {
|
|
3569
|
+
detail: feature,
|
|
3570
|
+
bubbles: true,
|
|
3571
|
+
composed: true
|
|
3572
|
+
}));
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
/**
|
|
3577
|
+
* Emit current-feature with null (used on blur)
|
|
3578
|
+
* Always emits to ensure map is cleared when editor loses focus
|
|
3579
|
+
*/
|
|
3580
|
+
private _emitCurrentFeatureNull(): void {
|
|
3581
|
+
this._lastCursorFeatureIndex = null;
|
|
3582
|
+
this.dispatchEvent(new CustomEvent('current-feature', {
|
|
3583
|
+
detail: null,
|
|
3584
|
+
bubbles: true,
|
|
3585
|
+
composed: true
|
|
3586
|
+
}));
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3535
3589
|
// ========== UI Updates ==========
|
|
3536
3590
|
|
|
3537
3591
|
updateReadonly() {
|
|
@@ -3573,74 +3627,6 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
3573
3627
|
if (this._editorSuffix) this._editorSuffix.textContent = this.suffix;
|
|
3574
3628
|
}
|
|
3575
3629
|
|
|
3576
|
-
// ========== Theme ==========
|
|
3577
|
-
|
|
3578
|
-
updateThemeCSS() {
|
|
3579
|
-
const darkSelector = this.getAttribute('dark-selector') || '.dark';
|
|
3580
|
-
const darkRule = parseSelectorToHostRule(darkSelector);
|
|
3581
|
-
|
|
3582
|
-
let themeStyle = this._id('theme-styles') as HTMLStyleElement;
|
|
3583
|
-
if (!themeStyle) {
|
|
3584
|
-
themeStyle = _ce('style') as HTMLStyleElement;
|
|
3585
|
-
themeStyle.id = 'theme-styles';
|
|
3586
|
-
this.shadowRoot!.insertBefore(themeStyle, this.shadowRoot!.firstChild);
|
|
3587
|
-
}
|
|
3588
|
-
|
|
3589
|
-
const darkDefaults = {
|
|
3590
|
-
bgColor: '#2b2b2b',
|
|
3591
|
-
textColor: '#a9b7c6',
|
|
3592
|
-
caretColor: '#bbb',
|
|
3593
|
-
gutterBg: '#313335',
|
|
3594
|
-
gutterBorder: '#3c3f41',
|
|
3595
|
-
gutterText: '#606366',
|
|
3596
|
-
jsonKey: '#9876aa',
|
|
3597
|
-
jsonString: '#6a8759',
|
|
3598
|
-
jsonNumber: '#6897bb',
|
|
3599
|
-
jsonBoolean: '#cc7832',
|
|
3600
|
-
jsonNull: '#cc7832',
|
|
3601
|
-
jsonPunct: '#a9b7c6',
|
|
3602
|
-
jsonError: '#ff6b68',
|
|
3603
|
-
controlColor: '#cc7832',
|
|
3604
|
-
controlBg: '#3c3f41',
|
|
3605
|
-
controlBorder: '#5a5a5a',
|
|
3606
|
-
geojsonKey: '#9876aa',
|
|
3607
|
-
geojsonType: '#6a8759',
|
|
3608
|
-
geojsonTypeInvalid: '#ff6b68',
|
|
3609
|
-
jsonKeyInvalid: '#ff6b68'
|
|
3610
|
-
};
|
|
3611
|
-
|
|
3612
|
-
RE_TO_KEBAB.lastIndex = 0;
|
|
3613
|
-
const toKebab = (str: string) => str.replace(RE_TO_KEBAB, '-$1').toLowerCase();
|
|
3614
|
-
const generateVars = (obj: Record<string, string | undefined>) => Object.entries(obj)
|
|
3615
|
-
.filter((entry): entry is [string, string] => entry[1] !== undefined)
|
|
3616
|
-
.map(([k, v]) => `--${toKebab(k)}: ${v};`)
|
|
3617
|
-
.join('\n ');
|
|
3618
|
-
|
|
3619
|
-
const lightVars = generateVars(this.themes.light as Record<string, string | undefined> || {});
|
|
3620
|
-
const darkTheme = { ...darkDefaults, ...this.themes.dark };
|
|
3621
|
-
const darkVars = generateVars(darkTheme as Record<string, string | undefined>);
|
|
3622
|
-
|
|
3623
|
-
let css = lightVars ? `:host {\n ${lightVars}\n }\n` : '';
|
|
3624
|
-
css += `${darkRule} {\n ${darkVars}\n }`;
|
|
3625
|
-
|
|
3626
|
-
themeStyle.textContent = css;
|
|
3627
|
-
}
|
|
3628
|
-
|
|
3629
|
-
setTheme(theme: ThemeSettings): void {
|
|
3630
|
-
if (theme.dark) this.themes.dark = { ...this.themes.dark, ...theme.dark };
|
|
3631
|
-
if (theme.light) this.themes.light = { ...this.themes.light, ...theme.light };
|
|
3632
|
-
this.updateThemeCSS();
|
|
3633
|
-
}
|
|
3634
|
-
|
|
3635
|
-
resetTheme(): void {
|
|
3636
|
-
this.themes = { dark: {}, light: {} };
|
|
3637
|
-
this.updateThemeCSS();
|
|
3638
|
-
}
|
|
3639
|
-
|
|
3640
|
-
getTheme(): ThemeSettings {
|
|
3641
|
-
return { ...this.themes };
|
|
3642
|
-
}
|
|
3643
|
-
|
|
3644
3630
|
/**
|
|
3645
3631
|
* Find all collapsible ranges using the mappings built by _rebuildNodeIdMappings
|
|
3646
3632
|
* This method only READS the existing mappings, it doesn't create new IDs
|
|
@@ -4066,6 +4052,10 @@ class GeoJsonEditor extends HTMLElement {
|
|
|
4066
4052
|
this.lines = [];
|
|
4067
4053
|
this.collapsedNodes.clear();
|
|
4068
4054
|
this.hiddenFeatures.clear();
|
|
4055
|
+
this.cursorLine = 0;
|
|
4056
|
+
this.cursorColumn = 0;
|
|
4057
|
+
this.selectionStart = null;
|
|
4058
|
+
this.selectionEnd = null;
|
|
4069
4059
|
this.updateModel();
|
|
4070
4060
|
this.scheduleRender();
|
|
4071
4061
|
this.updatePlaceholderVisibility();
|
package/src/types.ts
CHANGED
|
@@ -16,33 +16,3 @@ export interface SetOptions {
|
|
|
16
16
|
*/
|
|
17
17
|
collapsed?: string[] | ((feature: Feature | null, index: number) => string[]);
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
/** Theme configuration */
|
|
21
|
-
export interface ThemeConfig {
|
|
22
|
-
bgColor?: string;
|
|
23
|
-
textColor?: string;
|
|
24
|
-
caretColor?: string;
|
|
25
|
-
gutterBg?: string;
|
|
26
|
-
gutterBorder?: string;
|
|
27
|
-
gutterText?: string;
|
|
28
|
-
jsonKey?: string;
|
|
29
|
-
jsonString?: string;
|
|
30
|
-
jsonNumber?: string;
|
|
31
|
-
jsonBoolean?: string;
|
|
32
|
-
jsonNull?: string;
|
|
33
|
-
jsonPunct?: string;
|
|
34
|
-
jsonError?: string;
|
|
35
|
-
controlColor?: string;
|
|
36
|
-
controlBg?: string;
|
|
37
|
-
controlBorder?: string;
|
|
38
|
-
geojsonKey?: string;
|
|
39
|
-
geojsonType?: string;
|
|
40
|
-
geojsonTypeInvalid?: string;
|
|
41
|
-
jsonKeyInvalid?: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Theme settings for dark and light modes */
|
|
45
|
-
export interface ThemeSettings {
|
|
46
|
-
dark?: ThemeConfig;
|
|
47
|
-
light?: ThemeConfig;
|
|
48
|
-
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Theme for @softwarity/geojson-editor
|
|
3
|
+
*
|
|
4
|
+
* Clean theme inspired by GitHub's code styling.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* <link rel="stylesheet" href="themes/github.css">
|
|
8
|
+
* <geojson-editor></geojson-editor>
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
:root {
|
|
12
|
+
/* Editor background and text */
|
|
13
|
+
--geojson-editor-bg-color: light-dark(#ffffff, #0d1117);
|
|
14
|
+
--geojson-editor-text-color: light-dark(#24292f, #c9d1d9);
|
|
15
|
+
--geojson-editor-caret-color: light-dark(#24292f, #c9d1d9);
|
|
16
|
+
|
|
17
|
+
/* Gutter (line numbers area) */
|
|
18
|
+
--geojson-editor-gutter-bg: light-dark(#f6f8fa, #161b22);
|
|
19
|
+
--geojson-editor-gutter-border: light-dark(#d0d7de, #30363d);
|
|
20
|
+
--geojson-editor-gutter-text: light-dark(#8c959f, #6e7681);
|
|
21
|
+
|
|
22
|
+
/* JSON syntax highlighting */
|
|
23
|
+
--geojson-editor-json-key: light-dark(#0550ae, #79c0ff);
|
|
24
|
+
--geojson-editor-json-string: light-dark(#0a3069, #a5d6ff);
|
|
25
|
+
--geojson-editor-json-number: light-dark(#0550ae, #79c0ff);
|
|
26
|
+
--geojson-editor-json-boolean: light-dark(#cf222e, #ff7b72);
|
|
27
|
+
--geojson-editor-json-punct: light-dark(#24292f, #c9d1d9);
|
|
28
|
+
--geojson-editor-json-error: light-dark(#cf222e, #ff7b72);
|
|
29
|
+
|
|
30
|
+
/* GeoJSON-specific */
|
|
31
|
+
--geojson-editor-geojson-key: light-dark(#8250df, #d2a8ff);
|
|
32
|
+
--geojson-editor-geojson-type: light-dark(#116329, #7ee787);
|
|
33
|
+
--geojson-editor-geojson-type-invalid: light-dark(#cf222e, #ff7b72);
|
|
34
|
+
|
|
35
|
+
/* Controls (checkboxes, color swatches) */
|
|
36
|
+
--geojson-editor-control-color: light-dark(#0550ae, #79c0ff);
|
|
37
|
+
--geojson-editor-control-bg: light-dark(#f6f8fa, #21262d);
|
|
38
|
+
--geojson-editor-control-border: light-dark(#d0d7de, #30363d);
|
|
39
|
+
|
|
40
|
+
/* Selection and errors */
|
|
41
|
+
--geojson-editor-selection-color: light-dark(rgba(5, 80, 174, 0.2), rgba(56, 139, 253, 0.3));
|
|
42
|
+
--geojson-editor-error-color: light-dark(#cf222e, #ff7b72);
|
|
43
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monokai Theme for @softwarity/geojson-editor
|
|
3
|
+
*
|
|
4
|
+
* Classic dark theme inspired by Monokai color scheme.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* <link rel="stylesheet" href="themes/monokai.css">
|
|
8
|
+
* <geojson-editor></geojson-editor>
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
:root {
|
|
12
|
+
/* Editor background and text */
|
|
13
|
+
--geojson-editor-bg-color: light-dark(#fafafa, #272822);
|
|
14
|
+
--geojson-editor-text-color: light-dark(#49483e, #f8f8f2);
|
|
15
|
+
--geojson-editor-caret-color: light-dark(#49483e, #f8f8f0);
|
|
16
|
+
|
|
17
|
+
/* Gutter (line numbers area) */
|
|
18
|
+
--geojson-editor-gutter-bg: light-dark(#f0f0f0, #3e3d32);
|
|
19
|
+
--geojson-editor-gutter-border: light-dark(#e0e0e0, #49483e);
|
|
20
|
+
--geojson-editor-gutter-text: light-dark(#999, #75715e);
|
|
21
|
+
|
|
22
|
+
/* JSON syntax highlighting */
|
|
23
|
+
--geojson-editor-json-key: light-dark(#f92672, #f92672);
|
|
24
|
+
--geojson-editor-json-string: light-dark(#718c00, #e6db74);
|
|
25
|
+
--geojson-editor-json-number: light-dark(#ae81ff, #ae81ff);
|
|
26
|
+
--geojson-editor-json-boolean: light-dark(#66d9ef, #66d9ef);
|
|
27
|
+
--geojson-editor-json-punct: light-dark(#49483e, #f8f8f2);
|
|
28
|
+
--geojson-editor-json-error: light-dark(#f92672, #f92672);
|
|
29
|
+
|
|
30
|
+
/* GeoJSON-specific */
|
|
31
|
+
--geojson-editor-geojson-key: light-dark(#f92672, #a6e22e);
|
|
32
|
+
--geojson-editor-geojson-type: light-dark(#718c00, #e6db74);
|
|
33
|
+
--geojson-editor-geojson-type-invalid: light-dark(#f92672, #f92672);
|
|
34
|
+
|
|
35
|
+
/* Controls (checkboxes, color swatches) */
|
|
36
|
+
--geojson-editor-control-color: light-dark(#66d9ef, #66d9ef);
|
|
37
|
+
--geojson-editor-control-bg: light-dark(#e8e8e8, #3e3d32);
|
|
38
|
+
--geojson-editor-control-border: light-dark(#c0c0c0, #75715e);
|
|
39
|
+
|
|
40
|
+
/* Selection and errors */
|
|
41
|
+
--geojson-editor-selection-color: light-dark(rgba(174, 129, 255, 0.2), rgba(174, 129, 255, 0.3));
|
|
42
|
+
--geojson-editor-error-color: light-dark(#f92672, #f92672);
|
|
43
|
+
}
|