maplibre-gl-layer-control 0.1.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.
@@ -0,0 +1,644 @@
1
+ /* Layer Control Styles */
2
+
3
+ /* Control button */
4
+ .maplibregl-ctrl-layer-control {
5
+ background: #fff;
6
+ border-radius: 4px;
7
+ box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
8
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
9
+ }
10
+
11
+ .maplibregl-ctrl-layer-control button {
12
+ background: none;
13
+ border: none;
14
+ padding: 0;
15
+ width: 29px;
16
+ height: 29px;
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ cursor: pointer;
21
+ outline: none;
22
+ color: #1f2a37;
23
+ }
24
+
25
+ .maplibregl-ctrl-layer-control button:hover {
26
+ background-color: rgba(0, 0, 0, 0.05);
27
+ }
28
+
29
+ .maplibregl-ctrl-layer-control .layer-control-icon {
30
+ width: 100%;
31
+ height: 100%;
32
+ position: relative;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ line-height: 0;
37
+ }
38
+
39
+ .maplibregl-ctrl-layer-control .layer-control-icon svg {
40
+ width: 22px;
41
+ height: 22px;
42
+ display: block;
43
+ stroke: currentColor;
44
+ fill: none;
45
+ }
46
+
47
+ /* Panel */
48
+ .layer-control-panel {
49
+ position: absolute;
50
+ top: 100%;
51
+ margin-top: 5px;
52
+ background: #fff;
53
+ border-radius: 4px;
54
+ box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
55
+ min-width: 240px;
56
+ max-width: 420px;
57
+ max-height: 600px;
58
+ overflow-y: auto;
59
+ z-index: 1000;
60
+ padding: 8px;
61
+ font-size: 12px;
62
+ line-height: 1.4;
63
+ display: none;
64
+ }
65
+
66
+ /* Panel positioning based on parent control position */
67
+ .maplibregl-ctrl-top-left .layer-control-panel,
68
+ .maplibregl-ctrl-bottom-left .layer-control-panel {
69
+ left: 0;
70
+ }
71
+
72
+ .maplibregl-ctrl-top-right .layer-control-panel,
73
+ .maplibregl-ctrl-bottom-right .layer-control-panel {
74
+ right: 0;
75
+ }
76
+
77
+ .layer-control-panel.expanded {
78
+ display: block;
79
+ }
80
+
81
+ /* Panel header */
82
+ .layer-control-panel-header {
83
+ display: flex;
84
+ align-items: center;
85
+ justify-content: space-between;
86
+ gap: 10px;
87
+ font-weight: 600;
88
+ color: #333;
89
+ padding: 4px 0 8px 0;
90
+ border-bottom: 1px solid #e0e0e0;
91
+ margin-bottom: 8px;
92
+ }
93
+
94
+ .layer-control-panel-title {
95
+ flex: 1 1 auto;
96
+ font-size: 13px;
97
+ }
98
+
99
+ /* Width control */
100
+ .layer-control-width-control {
101
+ display: flex;
102
+ align-items: center;
103
+ gap: 6px;
104
+ font-weight: 500;
105
+ font-size: 11px;
106
+ color: #5c6a7d;
107
+ flex-shrink: 0;
108
+ margin-left: auto;
109
+ }
110
+
111
+ .layer-control-width-slider {
112
+ position: relative;
113
+ width: 130px;
114
+ height: 16px;
115
+ border-radius: 8px;
116
+ cursor: ew-resize;
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ touch-action: none;
121
+ background: transparent;
122
+ padding: 0 8px;
123
+ box-sizing: border-box;
124
+ }
125
+
126
+ .layer-control-width-value {
127
+ min-width: 44px;
128
+ text-align: right;
129
+ font-feature-settings: 'tnum';
130
+ color: #3a4756;
131
+ }
132
+
133
+ .layer-control-width-track {
134
+ position: absolute;
135
+ left: 8px;
136
+ right: 8px;
137
+ height: 4px;
138
+ border-radius: 2px;
139
+ background: linear-gradient(90deg, #d0d7e2, #2f6fed);
140
+ pointer-events: none;
141
+ }
142
+
143
+ .layer-control-width-thumb {
144
+ position: absolute;
145
+ top: 50%;
146
+ width: 14px;
147
+ height: 14px;
148
+ border-radius: 50%;
149
+ background: #fff;
150
+ border: 2px solid #2f6fed;
151
+ transform: translateY(-50%);
152
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
153
+ pointer-events: none;
154
+ }
155
+
156
+ /* Layer items */
157
+ .layer-control-item {
158
+ display: flex;
159
+ flex-direction: column;
160
+ gap: 6px;
161
+ padding: 4px 0;
162
+ border-bottom: 1px solid #f0f0f0;
163
+ box-sizing: border-box;
164
+ }
165
+
166
+ .layer-control-item:last-child {
167
+ border-bottom: none;
168
+ }
169
+
170
+ .layer-control-row {
171
+ display: flex;
172
+ align-items: center;
173
+ gap: 8px;
174
+ width: 100%;
175
+ box-sizing: border-box;
176
+ }
177
+
178
+ .layer-control-checkbox {
179
+ margin: 0;
180
+ cursor: pointer;
181
+ }
182
+
183
+ .layer-control-name {
184
+ margin: 0;
185
+ flex: 1 1 auto;
186
+ min-width: 120px;
187
+ font-weight: 500;
188
+ color: #333;
189
+ overflow: hidden;
190
+ text-overflow: ellipsis;
191
+ white-space: nowrap;
192
+ }
193
+
194
+ .layer-control-opacity {
195
+ flex: 0 0 140px;
196
+ height: 16px;
197
+ margin: 0;
198
+ cursor: pointer;
199
+ background: linear-gradient(to right, #ddd 0%, #333 100%);
200
+ border-radius: 8px;
201
+ appearance: none;
202
+ outline: none;
203
+ width: auto;
204
+ }
205
+
206
+ .layer-control-opacity::-webkit-slider-thumb {
207
+ appearance: none;
208
+ width: 16px;
209
+ height: 16px;
210
+ background: #fff;
211
+ border: 2px solid #333;
212
+ border-radius: 50%;
213
+ cursor: pointer;
214
+ }
215
+
216
+ .layer-control-opacity::-moz-range-thumb {
217
+ width: 16px;
218
+ height: 16px;
219
+ background: #fff;
220
+ border: 2px solid #333;
221
+ border-radius: 50%;
222
+ cursor: pointer;
223
+ }
224
+
225
+ /* Style button */
226
+ .layer-control-style-button {
227
+ width: 26px;
228
+ height: 26px;
229
+ border-radius: 4px;
230
+ border: none;
231
+ background: none;
232
+ cursor: pointer;
233
+ font-size: 14px;
234
+ color: #444;
235
+ display: flex;
236
+ align-items: center;
237
+ justify-content: center;
238
+ transition: background-color 0.1s ease;
239
+ }
240
+
241
+ .layer-control-style-button:hover {
242
+ background-color: rgba(0, 0, 0, 0.07);
243
+ }
244
+
245
+ .layer-control-style-button[disabled] {
246
+ cursor: not-allowed;
247
+ opacity: 0.45;
248
+ }
249
+
250
+ /* Style editor */
251
+ .layer-control-style-editor {
252
+ display: none;
253
+ width: 100%;
254
+ background: #f7f9fc;
255
+ border: 1px solid #dfe3eb;
256
+ border-radius: 6px;
257
+ padding: 10px;
258
+ margin-top: 6px;
259
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.6);
260
+ align-self: stretch;
261
+ box-sizing: border-box;
262
+ }
263
+
264
+ .layer-control-style-editor.expanded {
265
+ display: block;
266
+ }
267
+
268
+ .layer-control-style-header {
269
+ display: flex;
270
+ align-items: center;
271
+ justify-content: space-between;
272
+ font-weight: 600;
273
+ font-size: 12px;
274
+ margin-bottom: 8px;
275
+ color: #2f3c4a;
276
+ }
277
+
278
+ .layer-control-style-close,
279
+ .layer-control-style-close-secondary {
280
+ border: none;
281
+ background: none;
282
+ cursor: pointer;
283
+ color: #556372;
284
+ font-size: 14px;
285
+ }
286
+
287
+ .layer-control-style-controls {
288
+ display: flex;
289
+ flex-direction: column;
290
+ gap: 8px;
291
+ }
292
+
293
+ .layer-style-control {
294
+ display: flex;
295
+ flex-direction: column;
296
+ gap: 4px;
297
+ font-size: 11px;
298
+ }
299
+
300
+ .layer-style-control label {
301
+ font-weight: 500;
302
+ color: #3a4756;
303
+ }
304
+
305
+ .layer-style-color-group {
306
+ display: flex;
307
+ align-items: center;
308
+ gap: 6px;
309
+ }
310
+
311
+ .layer-style-color {
312
+ width: 36px;
313
+ height: 20px;
314
+ padding: 0;
315
+ border: none;
316
+ cursor: pointer;
317
+ }
318
+
319
+ .layer-style-color-text {
320
+ flex: 1;
321
+ height: 22px;
322
+ border: 1px solid #d0d7e2;
323
+ border-radius: 4px;
324
+ padding: 2px 6px;
325
+ font-family: monospace;
326
+ font-size: 11px;
327
+ background-color: #fff;
328
+ }
329
+
330
+ .layer-style-slider-wrapper {
331
+ display: flex;
332
+ align-items: center;
333
+ gap: 6px;
334
+ }
335
+
336
+ .layer-style-slider {
337
+ flex: 1;
338
+ cursor: pointer;
339
+ }
340
+
341
+ .layer-style-value {
342
+ min-width: 38px;
343
+ text-align: right;
344
+ font-family: monospace;
345
+ color: #3a4756;
346
+ }
347
+
348
+ .layer-control-style-actions {
349
+ display: flex;
350
+ justify-content: flex-end;
351
+ gap: 6px;
352
+ margin-top: 10px;
353
+ }
354
+
355
+ .layer-control-style-action {
356
+ border: none;
357
+ padding: 5px 12px;
358
+ border-radius: 4px;
359
+ cursor: pointer;
360
+ font-size: 11px;
361
+ font-weight: 500;
362
+ }
363
+
364
+ .layer-control-style-apply {
365
+ background-color: #2f6fed;
366
+ color: #fff;
367
+ }
368
+
369
+ .layer-control-style-apply:hover {
370
+ background-color: #2557b8;
371
+ }
372
+
373
+ .layer-control-style-reset {
374
+ background-color: #e4e8f1;
375
+ color: #2f3c4a;
376
+ border: 1px solid #c6cedd;
377
+ }
378
+
379
+ .layer-control-style-reset:hover {
380
+ background-color: #d7deed;
381
+ }
382
+
383
+ .layer-control-style-close-secondary {
384
+ background-color: #f7f9fc;
385
+ color: #2f3c4a;
386
+ border: 1px solid #d7deed;
387
+ }
388
+
389
+ .layer-control-style-close-secondary:hover {
390
+ background-color: #e9edf7;
391
+ }
392
+
393
+ .layer-control-style-empty {
394
+ margin: 0;
395
+ font-size: 11px;
396
+ color: #5c6a7d;
397
+ }
398
+
399
+ /* Style control groups */
400
+ .style-control-group {
401
+ display: flex;
402
+ flex-direction: column;
403
+ gap: 6px;
404
+ margin-bottom: 12px;
405
+ }
406
+
407
+ .style-control-group:last-child {
408
+ margin-bottom: 0;
409
+ }
410
+
411
+ .style-control-label {
412
+ font-size: 11px;
413
+ font-weight: 600;
414
+ color: #3a4756;
415
+ margin: 0;
416
+ }
417
+
418
+ .style-control-input-wrapper {
419
+ display: flex;
420
+ align-items: center;
421
+ gap: 8px;
422
+ }
423
+
424
+ .style-control-slider {
425
+ flex: 1;
426
+ height: 18px;
427
+ cursor: pointer;
428
+ background: linear-gradient(to right, #e0e6ed 0%, #2f6fed 100%);
429
+ border-radius: 9px;
430
+ outline: none;
431
+ appearance: none;
432
+ }
433
+
434
+ .style-control-slider::-webkit-slider-thumb {
435
+ appearance: none;
436
+ width: 16px;
437
+ height: 16px;
438
+ background: #fff;
439
+ border: 2px solid #2f6fed;
440
+ border-radius: 50%;
441
+ cursor: pointer;
442
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
443
+ }
444
+
445
+ .style-control-slider::-moz-range-thumb {
446
+ width: 16px;
447
+ height: 16px;
448
+ background: #fff;
449
+ border: 2px solid #2f6fed;
450
+ border-radius: 50%;
451
+ cursor: pointer;
452
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
453
+ }
454
+
455
+ .style-control-value {
456
+ min-width: 48px;
457
+ text-align: right;
458
+ font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
459
+ font-size: 11px;
460
+ color: #3a4756;
461
+ font-weight: 500;
462
+ }
463
+
464
+ .style-control-color-group {
465
+ display: flex;
466
+ align-items: center;
467
+ gap: 8px;
468
+ }
469
+
470
+ .style-control-color-picker {
471
+ width: 40px;
472
+ height: 28px;
473
+ padding: 2px;
474
+ border: 1px solid #d0d7e2;
475
+ border-radius: 4px;
476
+ cursor: pointer;
477
+ background: #fff;
478
+ }
479
+
480
+ .style-control-color-value {
481
+ flex: 1;
482
+ height: 28px;
483
+ padding: 4px 8px;
484
+ border: 1px solid #d0d7e2;
485
+ border-radius: 4px;
486
+ font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
487
+ font-size: 11px;
488
+ color: #3a4756;
489
+ background: #fff;
490
+ text-align: center;
491
+ font-weight: 500;
492
+ }
493
+
494
+ /* Custom scrollbar */
495
+ .layer-control-panel::-webkit-scrollbar {
496
+ width: 6px;
497
+ }
498
+
499
+ .layer-control-panel::-webkit-scrollbar-track {
500
+ background: #f1f1f1;
501
+ border-radius: 3px;
502
+ }
503
+
504
+ .layer-control-panel::-webkit-scrollbar-thumb {
505
+ background: #c1c1c1;
506
+ border-radius: 3px;
507
+ }
508
+
509
+ .layer-control-panel::-webkit-scrollbar-thumb:hover {
510
+ background: #a8a8a8;
511
+ }
512
+
513
+ /* Style button */
514
+ .layer-control-style-button {
515
+ flex: 0 0 auto;
516
+ width: 24px;
517
+ height: 24px;
518
+ border: 1px solid #ddd;
519
+ border-radius: 3px;
520
+ background: white;
521
+ color: #666;
522
+ cursor: pointer;
523
+ font-size: 14px;
524
+ padding: 0;
525
+ line-height: 1;
526
+ transition: all 0.2s ease;
527
+ }
528
+
529
+ .layer-control-style-button:hover {
530
+ background: #f5f5f5;
531
+ border-color: #2f6fed;
532
+ color: #2f6fed;
533
+ }
534
+
535
+ .layer-control-style-button:active {
536
+ background: #e8e8e8;
537
+ }
538
+
539
+ /* Style editor */
540
+ .layer-control-style-editor {
541
+ display: block;
542
+ margin-top: 8px;
543
+ padding: 12px;
544
+ background: #f7fafc;
545
+ border: 1px solid #e0e6ed;
546
+ border-radius: 4px;
547
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05);
548
+ }
549
+
550
+ .style-editor-header {
551
+ display: flex;
552
+ align-items: center;
553
+ justify-content: space-between;
554
+ margin-bottom: 12px;
555
+ padding-bottom: 8px;
556
+ border-bottom: 1px solid #e0e6ed;
557
+ }
558
+
559
+ .style-editor-title {
560
+ font-size: 13px;
561
+ font-weight: 600;
562
+ color: #333;
563
+ }
564
+
565
+ .style-editor-close {
566
+ width: 20px;
567
+ height: 20px;
568
+ border: none;
569
+ background: transparent;
570
+ color: #999;
571
+ font-size: 20px;
572
+ line-height: 1;
573
+ cursor: pointer;
574
+ padding: 0;
575
+ transition: color 0.2s ease;
576
+ }
577
+
578
+ .style-editor-close:hover {
579
+ color: #333;
580
+ }
581
+
582
+ .style-editor-controls {
583
+ margin-bottom: 12px;
584
+ min-height: 40px;
585
+ color: #666;
586
+ font-size: 12px;
587
+ }
588
+
589
+ .style-editor-actions {
590
+ display: flex;
591
+ gap: 8px;
592
+ justify-content: flex-end;
593
+ padding-top: 8px;
594
+ border-top: 1px solid #e0e6ed;
595
+ }
596
+
597
+ .style-editor-button {
598
+ padding: 8px 16px;
599
+ min-width: 70px;
600
+ border: 1px solid #ddd;
601
+ border-radius: 3px;
602
+ background: white;
603
+ color: #333;
604
+ font-size: 12px;
605
+ font-weight: 500;
606
+ cursor: pointer;
607
+ transition: all 0.2s ease;
608
+ white-space: nowrap;
609
+ }
610
+
611
+ .style-editor-button:hover {
612
+ background: #f5f5f5;
613
+ border-color: #bbb;
614
+ }
615
+
616
+ .style-editor-button:active {
617
+ background: #e0e0e0;
618
+ }
619
+
620
+ .layer-control-style-editor .style-editor-button-reset {
621
+ background: #fb923c !important;
622
+ color: #ffffff !important;
623
+ border-color: #f97316 !important;
624
+ font-weight: 600;
625
+ }
626
+
627
+ .layer-control-style-editor .style-editor-button-reset:hover {
628
+ background: #f97316 !important;
629
+ border-color: #ea580c !important;
630
+ color: #ffffff !important;
631
+ }
632
+
633
+ .layer-control-style-editor .style-editor-button-close {
634
+ background: #6b7280 !important;
635
+ color: #ffffff !important;
636
+ border-color: #4b5563 !important;
637
+ font-weight: 600;
638
+ }
639
+
640
+ .layer-control-style-editor .style-editor-button-close:hover {
641
+ background: #4b5563 !important;
642
+ border-color: #374151 !important;
643
+ color: #ffffff !important;
644
+ }
@@ -0,0 +1 @@
1
+ export { }
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "maplibre-gl-layer-control",
3
+ "version": "0.1.0",
4
+ "description": "A comprehensive layer control for MapLibre GL with advanced styling capabilities",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/types/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/types/index.d.ts",
13
+ "default": "./dist/index.mjs"
14
+ },
15
+ "require": {
16
+ "types": "./dist/types/index.d.ts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "./style.css": "./dist/maplibre-gl-layer-control.css"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "sideEffects": [
28
+ "*.css"
29
+ ],
30
+ "scripts": {
31
+ "dev": "vite",
32
+ "build": "tsc -p tsconfig.build.json && vite build",
33
+ "preview": "vite preview",
34
+ "test": "vitest --run",
35
+ "test:ui": "vitest --ui",
36
+ "test:coverage": "vitest --coverage",
37
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
38
+ "format": "prettier --write \"src/**/*.{ts,tsx,css}\"",
39
+ "prepublishOnly": "npm run build"
40
+ },
41
+ "keywords": [
42
+ "maplibre",
43
+ "maplibre-gl",
44
+ "map",
45
+ "layer-control",
46
+ "layers",
47
+ "react",
48
+ "typescript",
49
+ "control",
50
+ "style-editor"
51
+ ],
52
+ "author": "Qiusheng Wu",
53
+ "license": "MIT",
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "https://github.com/opengeos/maplibre-gl-layer-control.git"
57
+ },
58
+ "peerDependencies": {
59
+ "maplibre-gl": ">=3.0.0",
60
+ "react": ">=18.0.0",
61
+ "react-dom": ">=18.0.0"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "react": {
65
+ "optional": true
66
+ },
67
+ "react-dom": {
68
+ "optional": true
69
+ }
70
+ },
71
+ "devDependencies": {
72
+ "@testing-library/react": "^16.0.0",
73
+ "@testing-library/user-event": "^14.5.0",
74
+ "@types/react": "^18.3.0",
75
+ "@types/react-dom": "^18.3.0",
76
+ "@typescript-eslint/eslint-plugin": "^8.18.0",
77
+ "@typescript-eslint/parser": "^8.18.0",
78
+ "@vitejs/plugin-react": "^4.3.0",
79
+ "@vitest/ui": "^2.1.0",
80
+ "eslint": "^9.17.0",
81
+ "jsdom": "^25.0.0",
82
+ "maplibre-gl": "^4.7.1",
83
+ "prettier": "^3.4.0",
84
+ "react": "^18.3.0",
85
+ "react-dom": "^18.3.0",
86
+ "typescript": "^5.7.0",
87
+ "vite": "^6.0.0",
88
+ "vite-plugin-dts": "^4.3.0",
89
+ "vitest": "^2.1.0"
90
+ }
91
+ }