mapshaper 0.7.61 → 0.7.63

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/www/page.css CHANGED
@@ -577,6 +577,19 @@ textarea:focus {
577
577
  box-shadow: 0 0 0 2px rgba(212, 168, 0, 0.4);
578
578
  }
579
579
 
580
+ /* The interface is pointer-only. No custom control carries a tabindex, because
581
+ partial keyboard support was worse than none: tabbing reached a few buttons
582
+ and skipped most, and the menus a tab stop could open could not then be
583
+ navigated or dismissed from the keyboard.
584
+
585
+ This is the backstop for what is still focusable of necessity -- a span made
586
+ contenteditable for renaming, an element focused by script -- so that a UA
587
+ focus ring cannot appear over the interface. Text fields are excluded: they
588
+ have to show where the caret is, and the rule above is how they show it. */
589
+ :focus:not(input):not(textarea):not(select) {
590
+ outline: none;
591
+ }
592
+
580
593
  ::placeholder {
581
594
  color: #aaa;
582
595
  opacity: 1;
@@ -1224,8 +1237,9 @@ body.simplify .layer-control-btn {
1224
1237
  }
1225
1238
 
1226
1239
  .layer-control .no-layer-note {
1227
- color: #999;
1240
+ color: #777;
1228
1241
  margin: 0 0 5px 0;
1242
+ font-size: 13px;
1229
1243
  }
1230
1244
 
1231
1245
  .layer-add-links {
@@ -1248,6 +1262,11 @@ body.simplify .layer-control-btn {
1248
1262
  font-weight: bold;
1249
1263
  }
1250
1264
 
1265
+ /* "Draw:", which labels the links that follow it rather than being one */
1266
+ .new-layer-label {
1267
+ color: #777;
1268
+ }
1269
+
1251
1270
  .layer-menu .layer-list {
1252
1271
  margin-top: -5px;
1253
1272
  /* margin: 0 0 7px 0; */
@@ -1265,6 +1284,26 @@ body.simplify .layer-control-btn {
1265
1284
  cursor: pointer;
1266
1285
  }
1267
1286
 
1287
+ .map-frame-section {
1288
+ border-top: 1px solid #ddd;
1289
+ margin-top: 8px;
1290
+ padding-top: 7px;
1291
+ }
1292
+
1293
+ .map-frame-heading h3 {
1294
+ margin: 0 0 1px 0;
1295
+ }
1296
+
1297
+ .map-frame-list .layer-item {
1298
+ cursor: pointer;
1299
+ }
1300
+
1301
+ .map-frame-empty {
1302
+ cursor: pointer;
1303
+ font-size: 12px;
1304
+ padding: 7px 4px 4px;
1305
+ }
1306
+
1268
1307
  .pin-all img,
1269
1308
  .layer-item img
1270
1309
  {
@@ -1972,6 +2011,7 @@ div.basemap-style-btn.active {
1972
2011
 
1973
2012
  .map-layers.add-points,
1974
2013
  .map-layers.draw-tool,
2014
+ .map-layers.label-tool,
1975
2015
  .map-layers.snip-tool,
1976
2016
  .map-layers.ruler-tool {
1977
2017
  cursor: crosshair;
@@ -1981,6 +2021,12 @@ div.basemap-style-btn.active {
1981
2021
  cursor: default;
1982
2022
  }
1983
2023
 
2024
+ /* Over a label's knot or anchor handle, where a drag moves the handle rather
2025
+ than placing a label or panning the map. */
2026
+ .map-layers.label-handle {
2027
+ cursor: move;
2028
+ }
2029
+
1984
2030
  .map-layers.ruler-dragging {
1985
2031
  cursor: grabbing;
1986
2032
  }
@@ -2020,6 +2066,41 @@ body.pan.panning .map-layers:not(.drawing) {
2020
2066
  overflow: visible;
2021
2067
  }
2022
2068
 
2069
+ .map-layers > svg.preview-overlay {
2070
+ top: 0;
2071
+ left: 0;
2072
+ z-index: 15;
2073
+ pointer-events: none;
2074
+ overflow: hidden;
2075
+ }
2076
+
2077
+ .map-layers > svg.preview-background-overlay {
2078
+ top: 0;
2079
+ left: 0;
2080
+ pointer-events: none;
2081
+ overflow: hidden;
2082
+ }
2083
+
2084
+ .preview-outside-mask {
2085
+ fill: rgba(24, 28, 32, 0.38);
2086
+ fill: #999;
2087
+ fill: #000;
2088
+ opacity: 0.85;
2089
+ opacity: 0.3;
2090
+ fill-rule: evenodd;
2091
+ }
2092
+
2093
+ .preview-page-border {
2094
+ fill: none;
2095
+ stroke: rgba(30, 30, 30, 0.9);
2096
+ stroke-width: 1.5px;
2097
+ vector-effect: non-scaling-stroke;
2098
+ }
2099
+
2100
+ .preview-page-neatline {
2101
+ pointer-events: none;
2102
+ }
2103
+
2023
2104
  .ruler-line {
2024
2105
  fill: none;
2025
2106
  stroke: #cc35a5;
@@ -2060,23 +2141,181 @@ body.pan.panning .map-layers:not(.drawing) {
2060
2141
  cursor: pointer;
2061
2142
  }
2062
2143
 
2063
- .map-layers > svg text.active-label {
2064
- stroke: #fac0ff; /* #efacf4; */
2144
+ .map-layers > svg text.label-style-selected {
2145
+ stroke: #fce300;
2146
+ stroke-opacity: 0.25;
2065
2147
  paint-order: stroke fill;
2066
2148
  stroke-width: 4px;
2067
2149
  stroke-linecap: round;
2068
2150
  stroke-linejoin: round;
2069
2151
  }
2070
2152
 
2071
- .map-layers > svg text.label-style-selected {
2072
- stroke: #fce300;
2073
- stroke-opacity: 0.25;
2153
+ /* A path label whose text is longer than its path. Export drops these; the
2154
+ editor keeps them visible and marked, because a hidden label can't be
2155
+ found and so can't be fixed. Same halo convention as the states above. */
2156
+ .map-layers > svg text.label-overflow {
2157
+ stroke: #ff7f2a;
2158
+ stroke-opacity: 0.45;
2074
2159
  paint-order: stroke fill;
2075
2160
  stroke-width: 4px;
2076
2161
  stroke-linecap: round;
2077
2162
  stroke-linejoin: round;
2078
2163
  }
2079
2164
 
2165
+ /* --- IN-PLACE LABEL EDITING --- */
2166
+
2167
+ /* The textarea is the model of record for an editing session: it holds focus so
2168
+ that keyboard handling, selection, clipboard and IME all come from the
2169
+ browser. It is invisible but must stay focusable, so it cannot be hidden with
2170
+ display:none or visibility:hidden. */
2171
+ .label-edit-input {
2172
+ position: absolute;
2173
+ z-index: -1;
2174
+ opacity: 0;
2175
+ width: 1px;
2176
+ height: 1em;
2177
+ padding: 0;
2178
+ border: none;
2179
+ resize: none;
2180
+ overflow: hidden;
2181
+ white-space: pre;
2182
+ pointer-events: none;
2183
+ }
2184
+
2185
+ .label-edit-overlay {
2186
+ pointer-events: none;
2187
+ }
2188
+
2189
+ /* Label tool cues: an outline around the object rather than a halo over the
2190
+ glyphs, so that it reads as something you have hold of. A solid line, to
2191
+ distinguish it from the dashed box of a label being typed into -- the two
2192
+ states look different because they behave differently.
2193
+
2194
+ Hover and selection draw the same shapes, because they mean the same thing
2195
+ about the same object; hover is simply lighter. The alternative, highlighting
2196
+ the anchor point on hover and the label on selection, made the two look like
2197
+ different objects. */
2198
+ .label-cue {
2199
+ pointer-events: none;
2200
+ }
2201
+
2202
+ .label-cue-hovered {
2203
+ opacity: 0.4;
2204
+ }
2205
+
2206
+ /* Hairline, like every other reference shape the tool draws: the box says where
2207
+ the label is, and a heavier outline reads as an object in its own right
2208
+ rather than as a mark on one. */
2209
+ .label-cue-box {
2210
+ fill: none;
2211
+ stroke: #3a7ddc;
2212
+ stroke-width: 1px;
2213
+ vector-effect: non-scaling-stroke;
2214
+ }
2215
+
2216
+ /* The anchor is what the text is positioned against, and label-pos can put it
2217
+ well outside the box, so it is worth marking on its own.
2218
+ Drawn as a ring rather than a dot, and beneath the glyphs: a centered label
2219
+ sits right on top of its anchor, and a filled marker would blot out a letter.
2220
+ Same treatment as the path guide's knots, since both are handles. */
2221
+ .label-cue-anchor {
2222
+ fill: #fff;
2223
+ stroke: #3a7ddc;
2224
+ stroke-width: 1.5px;
2225
+ vector-effect: non-scaling-stroke;
2226
+ }
2227
+
2228
+ /* The line between an anchor and the text being dragged away from it, drawn
2229
+ only while that drag is on. Dashed, and a shade lighter than the box: it is
2230
+ the relationship being edited rather than a part of either end. */
2231
+ .label-cue-tether {
2232
+ fill: none;
2233
+ stroke: #3a7ddc;
2234
+ stroke-width: 1px;
2235
+ stroke-dasharray: 2 2;
2236
+ opacity: 0.7;
2237
+ vector-effect: non-scaling-stroke;
2238
+ }
2239
+
2240
+ /* A copy of the baseline the label's text runs along. Unfilled: stroking an
2241
+ open curve must not paint the area it encloses. */
2242
+ .label-cue-curve {
2243
+ fill: none;
2244
+ stroke: #3a7ddc;
2245
+ stroke-width: 1px;
2246
+ stroke-opacity: 0.8;
2247
+ vector-effect: non-scaling-stroke;
2248
+ }
2249
+
2250
+ /* The grab handles on a selected curve, one per knot. Same ring treatment as
2251
+ the anchor marker and the path guide's knots, because all three are handles
2252
+ and a knot is an anchor on a curve.
2253
+ A corner knot is drawn as a square by the renderer; both shapes share this
2254
+ rule, so a corner differs in outline and not in colour. */
2255
+ .label-cue-knot {
2256
+ fill: #fff;
2257
+ stroke: #3a7ddc;
2258
+ stroke-width: 1.5px;
2259
+ vector-effect: non-scaling-stroke;
2260
+ }
2261
+
2262
+ /* Transparent rather than fill:none / stroke:none, because an unpainted shape
2263
+ is not hit-testable. */
2264
+ .label-edit-hit-area {
2265
+ fill: transparent;
2266
+ stroke: none;
2267
+ pointer-events: all;
2268
+ }
2269
+
2270
+ /* The thickened baseline follows the curve, so only its stroke should catch
2271
+ clicks -- filling an open curve would claim the whole area it encloses. */
2272
+ .label-edit-hit-baseline {
2273
+ fill: none;
2274
+ stroke: transparent;
2275
+ pointer-events: stroke;
2276
+ }
2277
+
2278
+ /* The curve a label's text is set along, ghosted while the label is open for
2279
+ editing. Fainter than the guide drawn while the path is being placed, and in
2280
+ the same violet, because it is the same curve at a later stage. */
2281
+ .label-edit-path {
2282
+ fill: none;
2283
+ stroke: #cc6acc;
2284
+ stroke-width: 1.2px;
2285
+ stroke-opacity: 0.4;
2286
+ vector-effect: non-scaling-stroke;
2287
+ }
2288
+
2289
+ /* Shows where an anchored label is even when it has no text -- the state a
2290
+ label is in between being created and being typed into. A path label shows
2291
+ its ghosted curve instead.
2292
+ Solid, not dashed: at the size of an empty label the dashes were most of the
2293
+ outline, which made the box look tentative rather than like a place to type. */
2294
+ .label-edit-box {
2295
+ fill: none;
2296
+ stroke: #cc6acc;
2297
+ stroke-width: 1px;
2298
+ vector-effect: non-scaling-stroke;
2299
+ }
2300
+
2301
+ .label-edit-selection {
2302
+ fill: #cc6acc;
2303
+ fill-opacity: 0.3;
2304
+ stroke: none;
2305
+ }
2306
+
2307
+ .label-edit-caret {
2308
+ stroke: #000;
2309
+ stroke-width: 1.5px;
2310
+ vector-effect: non-scaling-stroke;
2311
+ animation: label-caret-blink 1.1s steps(1) infinite;
2312
+ }
2313
+
2314
+ @keyframes label-caret-blink {
2315
+ 0%, 50% { stroke-opacity: 1; }
2316
+ 50.01%, 100% { stroke-opacity: 0; }
2317
+ }
2318
+
2080
2319
  /* --- MAP BUTTONS --- */
2081
2320
 
2082
2321
  /*.pointer-btn {
@@ -2151,12 +2390,12 @@ body.pan.panning .map-layers:not(.drawing) {
2151
2390
  position: relative;
2152
2391
  cursor:pointer;
2153
2392
  /* padding: 3px 3px 5px 3px; */
2154
- padding: 4px 5px;
2393
+ padding: 3px 4px;
2155
2394
  line-height: 1;
2156
2395
  }
2157
2396
 
2158
2397
  .nav-btn.menu-btn {
2159
- padding-top: 3px;
2398
+ padding-top: 2px;
2160
2399
  }
2161
2400
 
2162
2401
  .nav-btn.selected {
@@ -2173,12 +2412,111 @@ body.pan.panning .map-layers:not(.drawing) {
2173
2412
  fill: white;
2174
2413
  }
2175
2414
 
2415
+ .nav-btn.preview-toggle {
2416
+ align-items: center;
2417
+ box-sizing: border-box;
2418
+ display: flex;
2419
+ height: 28px;
2420
+ justify-content: center;
2421
+ margin-top: 2px;
2422
+ padding-bottom: 0;
2423
+ padding-top: 1px;
2424
+ }
2425
+
2426
+ .preview-toggle svg {
2427
+ display: block;
2428
+ height: 16px;
2429
+ transform: translateY(-1px);
2430
+ width: 16px;
2431
+ }
2432
+
2433
+ .preview-readout {
2434
+ position: absolute;
2435
+ z-index: 20;
2436
+ top: 6px;
2437
+ left: 50%;
2438
+ transform: translateX(-50%);
2439
+ background: rgba(255, 255, 255, 0.96);
2440
+ border: 1px solid #ddd;
2441
+ border-radius: 5px;
2442
+ box-shadow: 0 1px 5px rgba(0,0,0,0.12);
2443
+ color: #333;
2444
+ cursor: pointer;
2445
+ font-size: 12px;
2446
+ line-height: 15px;
2447
+ padding: 3px 7px;
2448
+ white-space: nowrap;
2449
+ }
2450
+
2451
+ .preview-readout-arrow {
2452
+ color: #777;
2453
+ font-size: 16px;
2454
+ font-weight: bold;
2455
+ line-height: 9px;
2456
+ margin-left: 6px;
2457
+ position: relative;
2458
+ top: -1px;
2459
+ }
2460
+
2461
+ .preview-scale-menu {
2462
+ position: absolute;
2463
+ z-index: 1;
2464
+ display: none;
2465
+ pointer-events: none;
2466
+ padding: 5px 0;
2467
+ background-color: white;
2468
+ top: calc(100% + 4px);
2469
+ right: 0;
2470
+ text-align: right;
2471
+ white-space: nowrap;
2472
+ border-radius: 6px;
2473
+ border: 1px solid #ddd;
2474
+ box-shadow: 0 1px 7px rgba(0,0,0,0.12);
2475
+ }
2476
+
2477
+ .preview-readout.open .preview-scale-menu {
2478
+ display: block;
2479
+ pointer-events: inherit;
2480
+ }
2481
+
2482
+ .preview-scale-menu-item {
2483
+ color: #555;
2484
+ padding: 3px 9px;
2485
+ white-space: nowrap;
2486
+ cursor: pointer;
2487
+ }
2488
+
2489
+ .preview-scale-menu-item:hover {
2490
+ color: black;
2491
+ background: var(--lt-theme-col);
2492
+ }
2493
+
2176
2494
  .label-style-panel {
2495
+ /* The shape of a field in every style panel: the label panel set them,
2496
+ and the line, polygon and circle panels are drawn from the same parts.
2497
+ A row is only legible when the select, the colour field, the size field
2498
+ and the button beside them are all the same height. */
2499
+ --label-field-height: 22px;
2500
+ --label-field-radius: 4px;
2501
+ --label-field-border: #bbb;
2502
+ /* The narrow column of a split row, wide enough for the longest caption
2503
+ over it -- "Letter spacing", which measures 71px -- and the same in
2504
+ every row of every panel, so that the field edges line up down the
2505
+ whole stack. */
2506
+ --label-split-b-width: 74px;
2507
+ /* Same glyph on native <select>s and the saved-style menu. The custom menu
2508
+ used a CSS triangle; the font menus used the OS arrow. A drawn stroke
2509
+ rather than a filled triangle: it is lighter than the text beside it,
2510
+ which is what a menu's arrow should be. */
2511
+ --label-menu-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath fill='none' stroke='%23666' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5l4 4 4-4'/%3E%3C/svg%3E");
2177
2512
  position: absolute;
2178
2513
  z-index: 21;
2179
2514
  top: 6px;
2180
2515
  right: 36px;
2181
- width: 185px;
2516
+ /* Wide enough for a pair of controls side by side, which is what these
2517
+ panels are mostly made of: at 185px the narrow column of a pair was too
2518
+ narrow for its own caption, and "Letter spacing" was clipped. */
2519
+ width: 216px;
2182
2520
  padding: 8px 12px 12px;
2183
2521
  text-align: left;
2184
2522
  background-color: white;
@@ -2190,375 +2528,896 @@ body.pan.panning .map-layers:not(.drawing) {
2190
2528
  .label-style-panel button,
2191
2529
  .label-style-panel select,
2192
2530
  .label-style-panel input {
2193
- font-size: 12.5px;
2531
+ font-size: 12px;
2194
2532
  }
2195
2533
 
2196
2534
  .label-style-panel button.label-style-close {
2197
2535
  font-size: 16px; /* close button icon */
2198
2536
  }
2199
2537
 
2200
-
2201
- .label-style-panel select {
2202
- padding: 1px 0;
2538
+ .frame-properties-popup {
2539
+ width: 264px;
2203
2540
  }
2204
2541
 
2205
- .label-style-panel-title {
2206
- position: relative;
2207
- font-weight: 600;
2208
- font-size: 16px;
2542
+ .frame-properties-form.label-style-panel,
2543
+ .frame-create-form.label-style-panel {
2544
+ background: transparent;
2545
+ border: 0;
2546
+ border-radius: 0;
2547
+ box-shadow: none;
2548
+ box-sizing: border-box;
2549
+ max-height: none;
2550
+ overflow: visible;
2551
+ padding: 0;
2552
+ position: static;
2553
+ width: 100%;
2209
2554
  }
2210
2555
 
2211
- .label-style-close {
2212
- position: absolute;
2213
- right: -5px;
2214
- border: 0;
2215
- background: transparent;
2216
- cursor: pointer;
2217
- font-size: 16px;
2218
- line-height: 1;
2556
+ /* Three columns on the appearance rows: the neatline's width sits with the
2557
+ colour and opacity it applies to, and the background row leaves the last
2558
+ column empty rather than pushing a lone field onto a line of its own. */
2559
+ .frame-properties-form .label-split-row {
2560
+ column-gap: 8px;
2561
+ grid-template-columns: 1fr 54px 54px;
2219
2562
  }
2220
2563
 
2221
- .label-style-row {
2222
- display: block;
2223
- margin-top: 7px;
2564
+ /* Wide enough for "Aspect ratio", and fixed rather than auto so that the ratio
2565
+ at the top of the panel lines up with the details at the bottom, which are a
2566
+ separate grid. */
2567
+ .frame-property-detail-row {
2568
+ display: grid;
2569
+ font-size: 12px;
2570
+ gap: 6px;
2571
+ grid-template-columns: 66px 1fr;
2572
+ margin-top: 6px;
2224
2573
  }
2225
2574
 
2226
- .label-style-row span,
2227
- .label-style-row-label {
2228
- display: block;
2229
- /* margin-bottom: 3px; */
2575
+ .frame-property-detail-row > span:first-child {
2230
2576
  color: #555;
2231
2577
  }
2232
2578
 
2233
- .label-style-row select {
2234
- width: 100%;
2235
- box-sizing: border-box;
2579
+ .frame-property-detail-value {
2580
+ overflow-wrap: anywhere;
2236
2581
  }
2237
2582
 
2238
- .label-style-panel select {
2239
- background-color: white;
2240
- border: 1px solid #999;
2241
- box-shadow: none;
2583
+ .frame-create-popup {
2584
+ width: 264px;
2242
2585
  }
2243
2586
 
2244
- .label-style-panel select:focus {
2245
- border-color: #d3aa00;
2246
- box-shadow: none;
2247
- outline: none;
2587
+ /* The field tips are drawn above the "?" that opens them, which the box's own
2588
+ overflow would clip. The dialog is three rows and two buttons and never has
2589
+ anything to scroll. */
2590
+ .alert-box.frame-create-box {
2591
+ overflow: visible;
2248
2592
  }
2249
2593
 
2250
- .label-style-panel input[type="text"] {
2251
- -webkit-appearance: none;
2252
- appearance: none;
2253
- background: white;
2254
- border: 1px solid #999;
2255
- border-radius: 0;
2256
- box-shadow: none;
2594
+ /* A field and the "?" beside it. The field itself is a .frame-field-row, shared
2595
+ with Frame properties, so that the dialog that creates a frame and the panel
2596
+ that edits it look like one program. The gap matches the option rows below,
2597
+ so that every field ends on the same vertical and the tips line up. */
2598
+ .frame-create-row {
2599
+ align-items: center;
2600
+ display: flex;
2601
+ gap: 8px;
2602
+ margin-top: 6px;
2257
2603
  }
2258
2604
 
2259
- .label-style-panel input[type="text"]:focus {
2260
- border-color: #d3aa00;
2261
- box-shadow: none;
2262
- outline: none;
2605
+ .frame-create-row > .frame-field-row {
2606
+ flex: 1;
2607
+ min-width: 0;
2263
2608
  }
2264
2609
 
2265
- .label-create-expression-row input {
2266
- width: 100%;
2267
- box-sizing: border-box;
2610
+ /* float is what the static tips in index.html are placed with; here the row is
2611
+ a flex container, which ignores it. */
2612
+ .frame-create-popup .tip-button {
2613
+ flex: none;
2614
+ float: none;
2615
+ margin-top: 0;
2616
+ margin-left: 0;
2617
+ width: 12px;
2268
2618
  }
2269
2619
 
2270
- .label-create-copy-row {
2620
+ /* The button names what it does and is as wide as its name, so the two options
2621
+ are left-aligned at a common width rather than sized by their labels. */
2622
+ .frame-create-option-row {
2623
+ align-items: center;
2271
2624
  display: flex;
2272
- align-items: flex-start;
2273
- gap: 5px;
2625
+ gap: 8px;
2626
+ margin-top: 6px;
2274
2627
  }
2275
2628
 
2276
- .label-create-copy-row input {
2277
- margin-top: 1px;
2629
+ /* flex: none, or the margin field beside the first button shrinks it and the
2630
+ two options stop looking like peers. */
2631
+ .frame-create-option-row > .dialog-btn {
2632
+ box-sizing: border-box;
2633
+ flex: none;
2634
+ margin: 0;
2635
+ text-align: center;
2636
+ width: 130px;
2278
2637
  }
2279
2638
 
2280
- .label-create-copy-row span {
2281
- display: inline;
2282
- margin: 0;
2639
+ /* Stretches so that the margin field ends where the fields above it do: the
2640
+ row is shorter than they are, and a ragged right edge reads as unfinished
2641
+ rather than as deliberate grouping. */
2642
+ .frame-create-margin {
2643
+ align-items: center;
2644
+ display: flex;
2645
+ flex: 1;
2646
+ gap: 6px;
2647
+ min-width: 0; /* a flex item defaults to min-width:auto and will not shrink below its field */
2648
+ }
2649
+
2650
+ .frame-create-margin > span {
2283
2651
  color: #555;
2652
+ font-size: 12px;
2284
2653
  }
2285
2654
 
2286
- .label-saved-style-row select {
2287
- width: 100%;
2288
- margin-bottom: 4px;
2655
+ .frame-create-margin > input {
2656
+ box-sizing: border-box;
2657
+ flex: 1;
2658
+ min-width: 0;
2289
2659
  }
2290
2660
 
2291
- .label-saved-style-row {
2292
- margin-top: 10px;
2293
- padding-top: 8px;
2294
- border-top: 1px solid #ccc;
2661
+ .frame-field-row {
2662
+ align-items: center;
2663
+ display: flex;
2664
+ gap: 8px;
2295
2665
  }
2296
2666
 
2297
- .label-saved-style-row button,
2298
- .label-style-row button {
2299
- margin-right: 5px;
2667
+ .frame-field-row > span {
2668
+ flex: 0 0 82px;
2300
2669
  }
2301
2670
 
2302
- .label-split-row {
2671
+ .frame-field-row > input,
2672
+ .frame-field-row > select {
2673
+ flex: 1;
2674
+ min-width: 0;
2675
+ }
2676
+
2677
+ .frame-field-row > .label-panel-action-btn {
2678
+ flex: none;
2679
+ }
2680
+
2681
+ /* Two class names, because .label-style-row's display:block is declared later
2682
+ in this file and a single class would lose to it -- which is what stacked
2683
+ width and height down the panel at full width instead of pairing them. */
2684
+ .label-style-row.frame-size-row {
2685
+ align-items: end;
2303
2686
  display: grid;
2304
- grid-template-columns: 105px 1fr;
2305
- column-gap: 8px;
2306
- align-items: start;
2687
+ gap: 6px;
2688
+ grid-template-columns: 1fr 1fr 54px;
2307
2689
  }
2308
2690
 
2309
- .label-split-cell > span,
2310
- .label-split-cell > .label-style-row-label {
2691
+ .frame-input-cell > span {
2692
+ color: #555;
2693
+ font-size: 12px;
2694
+ }
2695
+
2696
+ .frame-input-cell > input {
2697
+ box-sizing: border-box;
2698
+ width: 100%;
2699
+ }
2700
+
2701
+ .frame-units-select {
2702
+ width: 54px;
2703
+ }
2704
+
2705
+ .export-frame-info {
2706
+ color: #555;
2707
+ font-size: 12px;
2708
+ margin: 8px 0 12px;
2709
+ }
2710
+
2711
+ .label-style-panel select,
2712
+ .label-saved-style-btn {
2713
+ background-image: var(--label-menu-chevron);
2714
+ background-repeat: no-repeat;
2715
+ background-position: right 6px center;
2716
+ background-size: 10px 7px;
2717
+ }
2718
+
2719
+ .label-style-panel select {
2720
+ -webkit-appearance: none;
2721
+ appearance: none;
2722
+ padding: 0 20px 0 6px;
2723
+ }
2724
+
2725
+ /* The size the app's dialogs title themselves at (.info-box h3), so that a
2726
+ panel pinned over the map is headed like the menus beside it. */
2727
+ .label-style-panel-title {
2728
+ position: relative;
2729
+ font-weight: 600;
2730
+ font-size: 17px;
2731
+ }
2732
+
2733
+ .label-style-close {
2734
+ position: absolute;
2735
+ right: -5px;
2736
+ border: 0;
2737
+ background: transparent;
2738
+ cursor: pointer;
2739
+ font-size: 16px;
2740
+ line-height: 1;
2741
+ }
2742
+
2743
+ .label-style-row {
2311
2744
  display: block;
2312
- margin-bottom: 2px;
2745
+ margin-top: 7px;
2746
+ }
2747
+
2748
+ .label-style-row span,
2749
+ .label-style-row-label {
2750
+ display: block;
2751
+ /* margin-bottom: 3px; */
2313
2752
  color: #555;
2314
2753
  }
2315
2754
 
2316
- .label-split-row .label-size-row,
2317
- .label-split-row .label-icon-size-row {
2318
- justify-self: end;
2319
- white-space: nowrap;
2755
+ .label-style-row select {
2756
+ width: 100%;
2757
+ box-sizing: border-box;
2320
2758
  }
2321
2759
 
2322
- .label-color-row {
2760
+ .label-style-panel select,
2761
+ .label-style-panel input[type="text"] {
2762
+ box-sizing: border-box;
2763
+ height: var(--label-field-height);
2764
+ background-color: white;
2765
+ border: 1px solid var(--label-field-border);
2766
+ border-radius: var(--label-field-radius);
2767
+ box-shadow: none;
2768
+ }
2769
+
2770
+ .label-style-panel select:focus {
2771
+ border-color: #d3aa00;
2772
+ box-shadow: none;
2773
+ outline: none;
2774
+ }
2775
+
2776
+ .label-style-panel input[type="text"] {
2777
+ -webkit-appearance: none;
2778
+ appearance: none;
2779
+ }
2780
+
2781
+ .label-style-panel input[type="text"]:focus {
2782
+ border-color: #d3aa00;
2783
+ box-shadow: none;
2784
+ outline: none;
2785
+ }
2786
+
2787
+ .label-create-expression-row input {
2788
+ width: 100%;
2789
+ box-sizing: border-box;
2790
+ }
2791
+
2792
+ .label-create-copy-row {
2793
+ display: flex;
2794
+ align-items: flex-start;
2795
+ gap: 5px;
2796
+ }
2797
+
2798
+ .label-create-copy-row input {
2799
+ margin-top: 1px;
2800
+ }
2801
+
2802
+ .label-create-copy-row span {
2803
+ display: inline;
2804
+ margin: 0;
2805
+ color: #555;
2806
+ }
2807
+
2808
+ .label-style-row button {
2809
+ margin-right: 5px;
2810
+ }
2811
+
2812
+ /* Saved styles: a menu that applies one and a button that saves the current
2813
+ one, under a heading. The menu is built from divs rather than being a
2814
+ <select>, because each row carries its own delete button -- see
2815
+ gui-style-preset-control.mjs.
2816
+
2817
+ A section of the panel like Text and Icon, so the spacing above it is the
2818
+ whitespace those sections are separated by, in the panel that has them, and
2819
+ this margin in the layer panel, which does not. */
2820
+ .label-saved-style-row {
2821
+ margin-top: 11px;
2822
+ }
2823
+
2824
+ /* The two controls sit in the panel's A and B columns, on the same grid as a
2825
+ split row: Apply style lines up with the fields and menus above it, and
2826
+ Save current with the narrow column beside them. Every style panel is the
2827
+ same width, so one grid does for all three. */
2828
+ .label-saved-style-controls {
2829
+ display: grid;
2830
+ grid-template-columns: 1fr var(--label-split-b-width);
2831
+ column-gap: 10px;
2832
+ margin-top: 4px;
2833
+ }
2834
+
2835
+ /* min-width:0 lets the menu shrink to its column: a grid track is otherwise
2836
+ at least as wide as its contents, and the names in the list can be long. */
2837
+ .label-saved-style-menu {
2323
2838
  position: relative;
2839
+ min-width: 0;
2324
2840
  }
2325
2841
 
2326
- .label-panel-btn {
2842
+ /* Sized, bordered and lettered like the panel's fields rather than like its
2843
+ 19px icon buttons, because that is what these two are: a menu the width of
2844
+ a field, and a button beside it. The type is the fields' own -- these are
2845
+ divs, so the panel's rule for button, select and input does not reach them
2846
+ and they were left at the app's 14px. */
2847
+ .label-saved-style-button {
2327
2848
  box-sizing: border-box;
2328
- user-select: none;
2849
+ display: flex;
2850
+ align-items: center;
2851
+ height: var(--label-field-height);
2852
+ padding: 0 6px;
2853
+ border: 1px solid var(--label-field-border);
2854
+ border-radius: var(--label-field-radius);
2855
+ background-color: white;
2856
+ font-size: 12px;
2857
+ color: #000;
2329
2858
  cursor: pointer;
2330
- border: 1px solid #999;
2859
+ user-select: none;
2860
+ }
2861
+
2862
+ /* "Save current" is 63px of SourceSans3 and 68px of the Arial it falls back
2863
+ to, in a column of 74: too near its edges to read as inset from them, so
2864
+ the label is centred and the padding is only what keeps the widest of the
2865
+ two fonts off the border. */
2866
+ .label-saved-style-save {
2867
+ justify-content: center;
2868
+ padding: 0 2px;
2869
+ }
2870
+
2871
+ /* The panel's own disabled look: live colours, faded as a whole. */
2872
+ .label-saved-style-button.disabled {
2873
+ opacity: 0.45;
2874
+ cursor: default;
2875
+ }
2876
+
2877
+ .label-saved-style-button:hover:not(.disabled),
2878
+ .label-saved-style-button.open {
2879
+ background-color: #f0f0f0;
2880
+ }
2881
+
2882
+ .label-saved-style-btn {
2883
+ padding-right: 20px;
2884
+ }
2885
+
2886
+ /* The label gives way to the chevron rather than running under it. */
2887
+ .label-saved-style-btn span {
2888
+ flex: 1;
2889
+ min-width: 0;
2890
+ overflow: hidden;
2891
+ white-space: nowrap;
2892
+ text-overflow: ellipsis;
2893
+ }
2894
+
2895
+ /* Above the panel's own rows, and free to hang past its bottom edge -- the
2896
+ panel does not clip, and this row is the last thing in it. */
2897
+ .label-saved-style-list {
2898
+ position: absolute;
2899
+ top: 100%;
2900
+ left: 0;
2901
+ /* At least as wide as the menu, and wider if the names need it: the button
2902
+ is only as wide as the space left over beside Save current, and a style
2903
+ name is what the user is trying to read. It can hang over the map --
2904
+ nothing clips it -- but not indefinitely; past that, names ellipsize. */
2905
+ min-width: 100%;
2906
+ width: max-content;
2907
+ max-width: 240px;
2908
+ z-index: 30;
2909
+ margin-top: 2px;
2910
+ padding: 3px 2px;
2911
+ max-height: 40vh;
2912
+ overflow-y: auto;
2913
+ background: white;
2914
+ /* The size of the button it drops from, which is the size of a field. */
2915
+ font-size: 12px;
2916
+ border: 1px solid #bbb;
2917
+ border-radius: var(--label-field-radius);
2918
+ box-shadow: 0 2px 8px rgba(0,0,0,0.18);
2919
+ }
2920
+
2921
+ .label-saved-style-list.drop-up {
2922
+ top: auto;
2923
+ bottom: 100%;
2924
+ margin: 0 0 2px 0;
2925
+ }
2926
+
2927
+ .label-saved-style-item {
2928
+ display: flex;
2929
+ align-items: center;
2930
+ }
2931
+
2932
+ /* The name is the target for applying, and the highlight stops where it does,
2933
+ so that the row reads as two things to click rather than one with a hazard
2934
+ at the end of it. */
2935
+ .label-saved-style-name {
2936
+ flex: 1;
2937
+ min-width: 0;
2938
+ padding: 3px 4px 3px 8px;
2331
2939
  border-radius: 3px;
2332
- background: #eee;
2333
- padding: 0;
2334
- height: 19px;
2335
- width: 19px;
2940
+ color: var(--normal-text);
2941
+ cursor: pointer;
2942
+ overflow: hidden;
2943
+ white-space: nowrap;
2944
+ text-overflow: ellipsis;
2336
2945
  }
2337
2946
 
2338
- .label-panel-btn.disabled {
2947
+ .label-saved-style-name:hover {
2948
+ background: var(--lt-theme-col);
2949
+ }
2950
+
2951
+ /* Hidden by visibility rather than left out, so that a name is the same width
2952
+ whether or not the pointer is on it. */
2953
+ .label-saved-style-delete {
2954
+ flex: none;
2955
+ width: 16px;
2956
+ padding: 2px 4px 2px 0;
2957
+ text-align: center;
2958
+ line-height: 1;
2959
+ color: #999;
2960
+ cursor: pointer;
2961
+ visibility: hidden;
2962
+ }
2963
+
2964
+ .label-saved-style-item:hover .label-saved-style-delete {
2965
+ visibility: visible;
2966
+ }
2967
+
2968
+ .label-saved-style-delete:hover {
2969
+ color: #c00;
2970
+ }
2971
+
2972
+ .label-saved-style-empty {
2973
+ padding: 3px 8px;
2339
2974
  color: #999;
2340
2975
  cursor: default;
2341
- opacity: 0.45;
2342
2976
  }
2343
2977
 
2344
- .label-color-row .label-color-chit {
2345
- display: inline-block;
2346
- width: 19px;
2347
- height: 19px;
2348
- margin-right: 5px;
2349
- padding: 0;
2350
- vertical-align: top;
2351
- border: 1px solid #888;
2352
- cursor: pointer;
2978
+ /* Two controls on one line: a font style and its size, a colour and its
2979
+ opacity, the icon shapes and the icon's size. Both columns are the same
2980
+ width in every row, rather than each row's right column being sized to
2981
+ whatever it happens to hold -- the panel is a stack of these pairs, and
2982
+ sizing them one row at a time leaves every field edge in a slightly
2983
+ different place.
2984
+
2985
+ Bottom-aligned, so that a caption above one control cannot push it out of
2986
+ line with the control beside it: the icon size has a "Size" over it and the
2987
+ shape buttons beside it have nothing, and the two still have to be level. */
2988
+ .label-split-row {
2989
+ display: grid;
2990
+ grid-template-columns: 1fr var(--label-split-b-width);
2991
+ column-gap: 10px;
2992
+ align-items: end;
2353
2993
  }
2354
2994
 
2355
- .label-color-row input[type="text"] {
2356
- width: 79px;
2357
- height: 19px;
2358
- padding-top: 0;
2359
- padding-bottom: 0;
2995
+ .label-split-cell > span,
2996
+ .label-split-cell > .label-style-row-label {
2997
+ display: block;
2998
+ margin-bottom: 2px;
2999
+ color: #555;
3000
+ }
3001
+
3002
+ /* Captions a couple of sizes down from the values they name. They are read
3003
+ once, when the panel is first met, and the narrow column of a split row is
3004
+ only as wide as the longest of them -- "Letter spacing" is what set it --
3005
+ so quieter captions are also a narrower panel. Direct children only: the
3006
+ colour picker's H/S/B captions are spans in a row too. */
3007
+ .label-style-panel .label-style-row > span,
3008
+ .label-style-panel .label-style-selection-row > span,
3009
+ .label-style-panel .label-split-cell > span,
3010
+ .label-style-panel .label-style-row > .label-style-row-label,
3011
+ .label-style-panel .label-split-cell > .label-style-row-label,
3012
+ .label-style-panel .label-icon-size-caption,
3013
+ .label-style-panel .point-style-note {
3014
+ font-size: 12px;
3015
+ }
3016
+
3017
+ .label-split-row .label-split-cell + .label-split-cell {
3018
+ white-space: nowrap;
3019
+ }
3020
+
3021
+ /* Each column's control fills its column: the widths are the grid's to decide
3022
+ now, and a control keeping a width of its own would sit inside its cell
3023
+ rather than lining up with the one above it. */
3024
+ .label-split-cell > select,
3025
+ .label-split-cell > .size-field,
3026
+ .label-split-cell > input[type="text"] {
3027
+ width: 100%;
2360
3028
  box-sizing: border-box;
2361
- vertical-align: top;
2362
3029
  }
2363
3030
 
2364
- .label-color-row .label-color-picker {
2365
- left: auto;
2366
- right: 0;
3031
+ .label-split-cell > .size-field {
3032
+ display: flex;
2367
3033
  }
2368
3034
 
2369
- .label-color-row .label-color-picker input[type="text"] {
2370
- width: 45px;
3035
+ .label-style-panel .size-field input.size-field-input {
3036
+ flex: 1;
3037
+ width: auto;
2371
3038
  }
2372
3039
 
2373
- .label-css-row input {
3040
+ /* A grid track is at least as wide as its contents unless told otherwise, and
3041
+ a text input's contents are twenty characters whatever width it is given --
3042
+ without this the colour field pushes the opacity beside it off the panel. */
3043
+ .label-split-row .label-split-cell {
3044
+ min-width: 0;
3045
+ }
3046
+
3047
+ .label-font-style-row select {
2374
3048
  width: 100%;
2375
3049
  box-sizing: border-box;
2376
3050
  }
2377
3051
 
2378
- .layer-style-panel .layer-color-row {
2379
- position: relative;
3052
+ /* Both take their width from the split row's narrow column; a value reads from
3053
+ the left edge of the field, where the caption above it starts. */
3054
+ .label-style-panel .label-opacity-input,
3055
+ .label-style-panel .label-measure-input {
3056
+ padding: 0 4px;
3057
+ vertical-align: top;
2380
3058
  }
2381
3059
 
2382
- .point-style-section + .point-style-section {
2383
- margin-top: 10px;
2384
- padding-top: 8px;
2385
- border-top: 1px solid #ccc;
3060
+ /* One disabled look for the whole panel. Left to themselves the controls each
3061
+ fade differently -- a select takes the browser's grey, an input with
3062
+ -webkit-appearance: none takes no styling at all, and a widget built from
3063
+ divs takes whatever it was given -- so an inert row ends up in three or four
3064
+ shades. Each control keeps its live colours and the whole thing is faded by
3065
+ the same amount, which also fades its border. */
3066
+ .label-style-panel select:disabled,
3067
+ .label-style-panel input[type="text"]:disabled,
3068
+ .label-style-panel .size-field.disabled,
3069
+ .label-style-panel .label-color-field.disabled,
3070
+ .label-style-panel .label-panel-action-btn.disabled,
3071
+ .label-style-panel .label-btn-group.disabled {
3072
+ opacity: 0.45;
3073
+ color: var(--normal-text);
3074
+ -webkit-text-fill-color: var(--normal-text);
3075
+ background-color: white;
3076
+ border-color: var(--label-field-border);
3077
+ }
3078
+
3079
+ /* Nothing inside an already-faded control fades again: two 0.45s over each
3080
+ other is 0.2, and a widget made of a wrapper and an input -- the size and
3081
+ colour fields -- was coming out half the weight of the plain input beside
3082
+ it. The parts still take their own disabled state, because a disabled input
3083
+ cannot be typed into and a disabled button cannot be pressed; it is only the
3084
+ fading that belongs to the whole. */
3085
+ .label-style-panel .label-btn-group.disabled .label-panel-btn.disabled,
3086
+ .label-style-panel .size-field.disabled .label-panel-btn.disabled,
3087
+ .label-style-panel .size-field.disabled input:disabled,
3088
+ .label-style-panel .label-color-field.disabled input:disabled,
3089
+ .label-style-panel .label-color-field.disabled .label-color-chit.disabled {
3090
+ opacity: 1;
3091
+ color: inherit;
2386
3092
  }
2387
3093
 
2388
- .point-style-section.point-style-first-visible {
2389
- margin-top: 7px;
3094
+ /* Whitespace and a bold heading separate the sections; a rule as well would be
3095
+ a third answer to a question already settled twice. */
3096
+ .label-style-panel .label-style-section + .label-style-section {
3097
+ margin-top: 12px;
2390
3098
  padding-top: 0;
2391
3099
  border-top: 0;
2392
3100
  }
2393
3101
 
2394
- .point-style-note {
2395
- color: #555;
2396
- line-height: 1.25;
3102
+ /* Baselines, not boxes: the heading and the caption beside it are set at
3103
+ different sizes, and centring their boxes leaves the smaller one's baseline
3104
+ a pixel or two high. The switch has no baseline to share, so it centres on
3105
+ the line itself. */
3106
+ .label-style-panel .label-style-section-title {
3107
+ display: flex;
3108
+ align-items: baseline;
3109
+ gap: 8px;
2397
3110
  }
2398
3111
 
2399
- .point-create-labels-row {
2400
- display: flex;
2401
- align-items: center;
2402
- gap: 10px;
3112
+ .label-style-panel .label-style-section-title .label-toggle {
3113
+ align-self: center;
2403
3114
  }
2404
3115
 
2405
- .point-create-circles-row {
2406
- display: flex;
2407
- align-items: baseline;
2408
- gap: 2px;
2409
- color: #555;
3116
+ /* .hidden is one class and the rule above is two, so a heading told to hide
3117
+ would go on being laid out. The circle panel hides its Circles heading once
3118
+ the points are circles: the panel's title says so, and the section is the
3119
+ only thing left in it. */
3120
+ .label-style-panel .label-style-section-title.hidden {
3121
+ display: none;
2410
3122
  }
2411
3123
 
2412
- .point-create-circles-row span {
2413
- display: inline;
2414
- margin: 0;
2415
- color: #555;
3124
+ /* 15.5px is what the dialogs subhead at (.info-box h4); the weight is the
3125
+ sketch's, which wants these read as headings rather than as captions. */
3126
+ .label-style-panel .label-style-section-name {
3127
+ font-size: 15.5px;
3128
+ color: #222;
2416
3129
  }
2417
3130
 
2418
- .point-create-copy-label {
2419
- display: inline-flex;
2420
- align-items: center;
2421
- gap: 4px;
2422
- margin: 0;
3131
+ .label-style-panel .label-style-section-minor .label-style-section-name {
3132
+ font-size: 12px;
3133
+ font-weight: normal;
3134
+ line-height: 1;
2423
3135
  color: #555;
2424
3136
  }
2425
3137
 
2426
- .point-create-copy-label span {
2427
- display: inline;
2428
- margin: 0;
3138
+ /* Where label text is measured (gui-label-measure.mjs). Off the page rather
3139
+ than display:none, which would give it no layout and its text no box. */
3140
+ .label-measure-svg {
3141
+ position: absolute;
3142
+ left: -10000px;
3143
+ top: -10000px;
3144
+ width: 1px;
3145
+ height: 1px;
3146
+ overflow: hidden;
3147
+ }
3148
+
3149
+ /* Closer to the heading above it than a row usually is, because that heading
3150
+ line carries this row's "Size" caption: the gap between a caption and the
3151
+ field it names is 2px everywhere else in the panel. */
3152
+ .text-style-panel .label-icon-shapes-row {
3153
+ margin-top: 2px;
3154
+ }
3155
+
3156
+ /* Pushed to the right and given the width of a split row's narrow column, so
3157
+ that it sits over the size field a row below it. */
3158
+ .text-style-panel .label-icon-size-caption {
3159
+ width: var(--label-split-b-width);
3160
+ margin-left: auto;
3161
+ font-weight: normal;
2429
3162
  color: #555;
2430
3163
  }
2431
3164
 
2432
- .point-symbol-row {
2433
- display: flex;
2434
- align-items: flex-start;
2435
- gap: 12px;
3165
+ /* Darker than the app's default placeholder. These fields show the value the
3166
+ renderer will use when the property is absent -- 0 and auto are true of the
3167
+ label, they are just not stored on it -- so at #aaa they read as a control
3168
+ that is switched off rather than as an answer. */
3169
+ .label-style-panel ::placeholder {
3170
+ color: #777;
2436
3171
  }
2437
3172
 
2438
- .point-symbol-size-row {
3173
+ /* Buttons that answer one question share a border and sit flush, the way the
3174
+ size field and its stepper do: four shapes are four answers to "which
3175
+ symbol", not four separate controls. They divide the width of their cell so
3176
+ that the group lines up with the field above or below it. */
3177
+ .label-style-panel .label-btn-group {
2439
3178
  display: flex;
2440
- align-items: flex-start;
2441
- gap: 12px;
3179
+ box-sizing: border-box;
3180
+ height: var(--label-field-height);
3181
+ border: 1px solid var(--label-field-border);
3182
+ border-radius: var(--label-field-radius);
3183
+ background: white;
3184
+ overflow: hidden;
2442
3185
  }
2443
3186
 
2444
- .point-symbol-stepper-control {
2445
- display: flex;
2446
- flex-direction: column;
2447
- gap: 2px;
3187
+ .label-style-panel .label-btn-group .label-panel-btn {
3188
+ flex: 1;
3189
+ width: auto;
3190
+ height: auto;
3191
+ border: none;
3192
+ border-radius: 0;
3193
+ background: white;
2448
3194
  }
2449
3195
 
2450
- .point-symbol-stepper-control:last-child {
2451
- margin-left: auto;
2452
- align-items: flex-end;
3196
+ .label-style-panel .label-btn-group .label-panel-btn + .label-panel-btn {
3197
+ border-left: 1px solid #e0e0e0;
2453
3198
  }
2454
3199
 
2455
- .point-symbol-color-cell {
3200
+ /* Hover leaves the selected button alone. Every hover fill in the panel is
3201
+ lighter than the selection it would sit on, so pointing at the chosen answer
3202
+ made it look less chosen than its neighbours -- and there is nothing to
3203
+ preview anyway, since clicking it changes nothing. */
3204
+ .label-style-panel .label-btn-group .label-panel-btn:hover:not(.disabled):not(.selected) {
3205
+ background: #f0f0f0;
3206
+ }
3207
+
3208
+ /* A grey fill, not the inverse the panel's other buttons use: the glyph is
3209
+ what identifies the choice, and reversing it out makes the selected shape
3210
+ the hardest one to read. */
3211
+ .label-style-panel .label-btn-group .label-panel-btn.selected {
3212
+ background: #dcdcdc;
3213
+ color: black;
3214
+ }
3215
+
3216
+ /* A switch, because whether the label has a symbol at all is a yes/no that the
3217
+ four shapes below it then qualify. Off is a pale track with the knob left,
3218
+ on a dark one with the knob right. */
3219
+ .label-toggle {
2456
3220
  position: relative;
2457
- white-space: nowrap;
2458
- flex: 1;
3221
+ flex: none;
3222
+ width: 26px;
3223
+ height: 15px;
3224
+ border-radius: 8px;
3225
+ background: #ccc;
3226
+ cursor: pointer;
3227
+ user-select: none;
3228
+ transition: background-color 0.12s;
3229
+ }
3230
+
3231
+ .label-toggle-knob {
3232
+ position: absolute;
3233
+ top: 2px;
3234
+ left: 2px;
3235
+ width: 11px;
3236
+ height: 11px;
3237
+ border-radius: 50%;
3238
+ background: white;
3239
+ box-shadow: 0 1px 2px rgba(0,0,0,0.25);
3240
+ transition: left 0.12s;
3241
+ }
3242
+
3243
+ .label-toggle.on {
3244
+ background: #333;
3245
+ }
3246
+
3247
+ .label-toggle.on .label-toggle-knob {
3248
+ left: 13px;
3249
+ }
3250
+
3251
+ .label-toggle.disabled {
3252
+ opacity: 0.45;
3253
+ cursor: default;
3254
+ }
3255
+
3256
+ .label-style-section-title {
3257
+ margin-top: 11px;
3258
+ font-weight: 600;
3259
+ color: #333;
3260
+ }
3261
+
3262
+ .label-style-section + .label-style-section .label-style-section-title {
3263
+ margin-top: 0;
3264
+ }
3265
+
3266
+ /* The cell a colour field sits in, which is also what its picker hangs from:
3267
+ the picker is wider than the panel, so it opens over the map, and anchoring
3268
+ it to the cell keeps it on the same side of the panel as its field. */
3269
+ .label-color-row {
3270
+ position: relative;
3271
+ }
3272
+
3273
+ .label-panel-btn {
3274
+ box-sizing: border-box;
3275
+ user-select: none;
3276
+ cursor: pointer;
3277
+ border: 1px solid #999;
3278
+ border-radius: 3px;
3279
+ background: #eee;
3280
+ padding: 0;
3281
+ height: 19px;
3282
+ width: 19px;
3283
+ }
3284
+
3285
+ .label-panel-btn.disabled {
3286
+ color: #999;
3287
+ cursor: default;
3288
+ opacity: 0.45;
2459
3289
  }
2460
3290
 
2461
- .point-symbol-row .layer-number-control input[type="text"],
2462
- .point-symbol-width-row input[type="text"] {
2463
- width: 45px;
2464
- height: 19px;
2465
- padding-top: 0;
2466
- padding-bottom: 0;
3291
+ /* A button with a word on it: Clear style, Random fills, Create. The same
3292
+ shape as Save current in the saved styles row, which is the same kind of
3293
+ thing -- a named action, as wide as its name -- and the same height as the
3294
+ fields above it. */
3295
+ .label-panel-action-btn {
2467
3296
  box-sizing: border-box;
3297
+ display: inline-flex;
3298
+ align-items: center;
3299
+ height: var(--label-field-height);
3300
+ padding: 0 8px;
3301
+ border: 1px solid var(--label-field-border);
3302
+ border-radius: var(--label-field-radius);
3303
+ background-color: white;
3304
+ color: var(--normal-text);
3305
+ cursor: pointer;
3306
+ user-select: none;
2468
3307
  }
2469
3308
 
2470
- .point-symbol-width-row input[type="text"] {
2471
- width: 55px;
3309
+ .label-panel-action-btn:hover:not(.disabled) {
3310
+ background-color: #f0f0f0;
2472
3311
  }
2473
3312
 
2474
- .layer-style-control-line {
2475
- display: flex;
2476
- align-items: flex-start;
2477
- gap: 12px;
3313
+ .label-panel-action-btn.disabled {
3314
+ cursor: default;
2478
3315
  }
2479
3316
 
2480
- .layer-color-cell {
2481
- position: relative;
2482
- white-space: nowrap;
3317
+ /* Buttons wrap rather than run off the panel: two of them fit on one line in
3318
+ the polygon panel and would not if either name grew. */
3319
+ .label-panel-button-row {
3320
+ display: flex;
3321
+ flex-wrap: wrap;
3322
+ gap: 4px 6px;
2483
3323
  }
2484
3324
 
2485
- .layer-style-panel .layer-color-row input[type="text"] {
2486
- width: 79px;
2487
- height: 19px;
2488
- padding-top: 0;
2489
- padding-bottom: 0;
3325
+ /* A swatch and its hex value inside one border: the pair is one answer to one
3326
+ question, and a separate button beside a text box reads as two. The swatch
3327
+ keeps its own outline so that white does not disappear into the field. */
3328
+ .label-style-panel .label-color-field {
3329
+ display: flex;
3330
+ align-items: center;
2490
3331
  box-sizing: border-box;
2491
- vertical-align: top;
3332
+ height: var(--label-field-height);
3333
+ padding: 0 3px;
3334
+ border: 1px solid var(--label-field-border);
3335
+ border-radius: var(--label-field-radius);
3336
+ background: white;
2492
3337
  }
2493
3338
 
2494
- .layer-row-controls {
2495
- display: flex;
2496
- gap: 6px;
2497
- align-items: flex-start;
2498
- justify-content: flex-start;
3339
+ .label-style-panel .label-color-field:focus-within {
3340
+ border-color: #d3aa00;
2499
3341
  }
2500
3342
 
2501
- .layer-number-control {
2502
- display: block;
2503
- margin: 0;
3343
+ .label-style-panel .label-color-field .label-color-chit {
3344
+ flex: none;
3345
+ width: 13px;
3346
+ height: 13px;
3347
+ margin-right: 6px;
3348
+ border: 1px solid #bbb;
3349
+ border-radius: 2px;
2504
3350
  }
2505
3351
 
2506
- .layer-number-control > span,
2507
- .label-color-row > span,
2508
- .layer-color-cell > span {
2509
- display: block;
2510
- margin-bottom: 3px;
2511
- color: #555;
3352
+ .label-style-panel .label-color-field input[type="text"] {
3353
+ flex: 1;
3354
+ width: auto;
3355
+ min-width: 0;
3356
+ height: auto;
3357
+ padding: 0;
3358
+ border: none;
3359
+ border-radius: 0;
3360
+ background: transparent;
3361
+ box-shadow: none;
2512
3362
  }
2513
3363
 
2514
- .layer-style-panel .layer-number-control input[type="text"] {
2515
- width: 45px;
2516
- height: 19px;
2517
- padding-top: 0;
2518
- padding-bottom: 0;
2519
- box-sizing: border-box;
3364
+ .label-style-panel .label-color-field input[type="text"]:focus {
3365
+ border: none;
3366
+ box-shadow: none;
3367
+ outline: none;
2520
3368
  }
2521
3369
 
2522
- .layer-stepper-control {
2523
- display: flex;
2524
- align-items: center;
2525
- gap: 2px;
3370
+ .label-style-panel .label-color-field.disabled {
3371
+ opacity: 0.45;
2526
3372
  }
2527
3373
 
2528
- .layer-stepper-control .label-panel-btn {
2529
- text-align: center;
2530
- line-height: 18px;
3374
+ .label-color-row .label-color-picker input[type="text"] {
3375
+ width: 45px;
2531
3376
  }
2532
3377
 
2533
- .layer-stroke-width-value {
2534
- display: inline-block;
2535
- width: 30px;
2536
- min-height: 17px;
2537
- line-height: 17px;
2538
- text-align: center;
2539
- border: 1px solid #999;
2540
- background: white;
2541
- cursor: pointer;
3378
+ .label-css-row input {
3379
+ width: 100%;
2542
3380
  box-sizing: border-box;
2543
3381
  }
2544
3382
 
2545
- .layer-stroke-width-value.editing {
2546
- cursor: text;
3383
+ /* What a section of this panel says when there is nothing in it to set: the
3384
+ layer is drawn some way this panel cannot edit, or the thing the section is
3385
+ for has to be made in the console. Set like a caption, not like a field's
3386
+ value. */
3387
+ .point-style-note {
3388
+ color: #555;
3389
+ line-height: 1.3;
2547
3390
  }
2548
3391
 
2549
- .layer-stroke-width-row .layer-number-control {
2550
- display: block;
3392
+ .point-create-labels-row {
3393
+ display: flex;
3394
+ align-items: center;
3395
+ gap: 10px;
2551
3396
  }
2552
3397
 
2553
- .layer-style-panel .layer-color-row .label-color-picker input[type="text"] {
2554
- width: 45px;
3398
+ .point-create-copy-label {
3399
+ display: inline-flex;
3400
+ align-items: center;
3401
+ gap: 4px;
3402
+ margin: 0;
3403
+ color: #555;
3404
+ font-size: 12px;
3405
+ }
3406
+
3407
+ .point-create-copy-label span {
3408
+ display: inline;
3409
+ margin: 0;
3410
+ color: #555;
2555
3411
  }
2556
3412
 
3413
+ /* Fixed rather than absolute so that a panel inside a scrolling box cannot trap
3414
+ the picker: Frame properties lives in an .alert-box that scrolls its
3415
+ overflow, which clipped the picker into the panel instead of letting it float
3416
+ over it. ColorPicker sets top/left from the field it belongs to when it
3417
+ opens. */
2557
3418
  .label-color-picker {
2558
- position: absolute;
3419
+ position: fixed;
2559
3420
  z-index: 25;
2560
- top: 48px;
2561
- right: 0;
2562
3421
  padding: 8px;
2563
3422
  background: white;
2564
3423
  border: 1px solid #ddd;
@@ -2657,6 +3516,10 @@ body.dragging-color-picker * {
2657
3516
  margin-left: auto;
2658
3517
  }
2659
3518
 
3519
+ .label-style-selection-row {
3520
+ line-height: 1;
3521
+ }
3522
+
2660
3523
  .label-style-selection-row button {
2661
3524
  margin-right: 5px;
2662
3525
  }
@@ -2699,17 +3562,66 @@ body.dragging-color-picker * {
2699
3562
  background: white;
2700
3563
  }
2701
3564
 
3565
+ /* Nine plain squares: a border on each would double the lines in a shape that
3566
+ is nothing but lines, so the cells are filled and the chosen one is dark. */
3567
+ .text-style-panel .label-position-grid {
3568
+ grid-template-columns: repeat(3, 10px);
3569
+ gap: 3px;
3570
+ margin-top: -13px;
3571
+ margin-bottom: 3px;
3572
+ }
3573
+
3574
+ .text-style-panel .label-position-grid .label-panel-btn {
3575
+ width: 10px;
3576
+ height: 10px;
3577
+ border: none;
3578
+ border-radius: 1px;
3579
+ background: #dcdcdc;
3580
+ }
3581
+
3582
+ .text-style-panel .label-position-grid .label-panel-btn:hover:not(.disabled):not(.selected) {
3583
+ background: #c4c4c4;
3584
+ }
3585
+
3586
+ .text-style-panel .label-position-grid .label-panel-btn.selected {
3587
+ background: black;
3588
+ }
3589
+
3590
+ /* The position a dragged label is nearest, which is where it roughly belongs
3591
+ and the cell that puts it back. Between the unlit grey and the lit black,
3592
+ because it is saying something weaker than either: not "here" but "about
3593
+ here, and not exactly". */
3594
+ .text-style-panel .label-position-grid .label-panel-btn.nearest:not(.selected) {
3595
+ background: #a0a0a0;
3596
+ }
3597
+
3598
+ /* The grid and the Fixed/Draggable toggle share the position row, the toggle
3599
+ against the right edge so that it lines up with the fields in the rows
3600
+ above. */
3601
+ .text-style-panel .label-position-row {
3602
+ margin-top: 3px;
3603
+ display: flex;
3604
+ /* align-items: center; */
3605
+ justify-content: space-between;
3606
+ }
3607
+
3608
+ /* Words rather than glyphs, so these segments size to their text instead of
3609
+ splitting the group evenly, and sit in a group narrow enough to read as one
3610
+ control beside the grid. */
3611
+ .text-style-panel .label-drag-mode-buttons .label-panel-btn {
3612
+ display: flex;
3613
+ flex: 0 0 auto;
3614
+ align-items: center;
3615
+ padding: 0 7px;
3616
+ font-size: 12px;
3617
+ }
3618
+
2702
3619
  .label-panel-btn.selected {
2703
3620
  background-color: black;
2704
3621
  border-color: black;
2705
3622
  color: white;
2706
3623
  }
2707
3624
 
2708
- .label-icon-buttons {
2709
- display: flex;
2710
- gap: 2px;
2711
- }
2712
-
2713
3625
  .label-icon-buttons .label-panel-btn {
2714
3626
  display: flex;
2715
3627
  align-items: center;
@@ -2724,7 +3636,6 @@ body.dragging-color-picker * {
2724
3636
  stroke: none;
2725
3637
  }
2726
3638
 
2727
- .label-icon-buttons .label-panel-btn[data-icon="none"] .label-icon-symbol,
2728
3639
  .label-icon-buttons .label-panel-btn[data-icon="ring"] .label-icon-symbol {
2729
3640
  fill: none;
2730
3641
  stroke: currentColor;
@@ -2737,41 +3648,113 @@ body.dragging-color-picker * {
2737
3648
  transform: scale(1.4);
2738
3649
  }
2739
3650
 
2740
- .label-size-row .label-panel-btn,
2741
- .label-icon-size-row .label-panel-btn {
2742
- display: inline-block;
2743
- text-align: center;
2744
- line-height: 18px;
3651
+ .label-align-buttons .label-panel-btn {
3652
+ display: flex;
3653
+ align-items: center;
3654
+ justify-content: center;
2745
3655
  }
2746
3656
 
2747
- .label-size-row .label-panel-btn,
2748
- .label-icon-size-row .label-panel-btn {
2749
- margin-right: 4px;
3657
+ /* Drawn larger than the icon symbols above, which share its viewBox but not
3658
+ its proportions: three rules across the middle are ink over less than half
3659
+ of the box, where a circle or a square fills most of its own, so at the same
3660
+ 13px both the glyph and its weight read smaller than the shapes beside it.
3661
+ 16px also puts the 16-unit viewBox at one unit per pixel, so the half-pixel
3662
+ baselines the rules are drawn on land where they were meant to. */
3663
+ .label-align-symbol {
3664
+ display: block;
3665
+ width: 16px;
3666
+ height: 16px;
3667
+ fill: none;
3668
+ stroke: currentColor;
3669
+ stroke-width: 1.5;
3670
+ stroke-linecap: round;
2750
3671
  }
2751
3672
 
2752
- .label-split-row .label-size-row .label-panel-btn,
2753
- .label-split-row .label-icon-size-row .label-panel-btn {
2754
- margin-right: 2px;
3673
+ /* A size field and its stepper share a border and sit flush, so that the three
3674
+ elements read as one control rather than three. The buttons are always
3675
+ visible: they are the primary affordance of the panel's most-used numbers,
3676
+ and revealing them on hover would either reserve their space anyway or
3677
+ overlap the value being adjusted. */
3678
+ .size-field {
3679
+ display: inline-flex;
3680
+ align-items: stretch;
3681
+ box-sizing: border-box;
3682
+ height: var(--label-field-height, 19px);
3683
+ border: 1px solid var(--label-field-border, #999);
3684
+ border-radius: var(--label-field-radius, 3px);
3685
+ background: #fff;
3686
+ overflow: hidden;
3687
+ vertical-align: top;
2755
3688
  }
2756
3689
 
2757
- .label-split-row .label-size-row .label-panel-btn:last-child,
2758
- .label-split-row .label-icon-size-row .label-panel-btn:last-child {
2759
- margin-right: 0;
3690
+ .size-field.disabled {
3691
+ opacity: 0.45;
2760
3692
  }
2761
3693
 
2762
- .label-size-row .label-size-value,
2763
- .label-icon-size-row .label-icon-size-value {
2764
- display: inline-block;
2765
- width: 28px;
2766
- margin: 0 3px 0 0;
2767
- text-align: center;
3694
+ /* input.size-field-input, not .size-field-input: the panels give their text
3695
+ inputs a border through an attribute selector, and the field's own border is
3696
+ the one that has to show. */
3697
+ .size-field input.size-field-input {
3698
+ width: 40px;
3699
+ min-width: 0;
3700
+ height: auto;
3701
+ margin: 0;
3702
+ padding: 0 3px;
3703
+ border: none;
3704
+ border-radius: 0;
3705
+ box-shadow: none;
3706
+ background: transparent;
2768
3707
  color: black;
2769
3708
  }
2770
3709
 
2771
- .label-split-row .label-size-value,
2772
- .label-split-row .label-icon-size-value {
2773
- width: 24px;
2774
- margin-right: 2px;
3710
+ /* The field shows focus on its outer border, so that the ring cannot be drawn
3711
+ between the input and the stepper it shares an edge with. */
3712
+ .size-field input.size-field-input:focus {
3713
+ outline: none;
3714
+ border: none;
3715
+ box-shadow: none;
3716
+ }
3717
+
3718
+ .size-field:focus-within {
3719
+ border-color: #d3aa00;
3720
+ }
3721
+
3722
+ /* The stepper is inset from the field's border rather than dividing it: the
3723
+ value is the field's content and the arrows are a control within it, which
3724
+ is also how the native spinner reads. */
3725
+ .size-field .size-field-stepper {
3726
+ display: flex;
3727
+ flex-direction: column;
3728
+ margin: 2px 2px 2px 0;
3729
+ border: 1px solid #d6d6d6;
3730
+ border-radius: 2px;
3731
+ background: #f7f7f7;
3732
+ overflow: hidden;
3733
+ }
3734
+
3735
+ /* Half-height buttons, so the pair occupies the width of one: the panel's rows
3736
+ are tight and a size field shares its row with a label. */
3737
+ .size-field .size-field-btn {
3738
+ display: flex;
3739
+ align-items: center;
3740
+ justify-content: center;
3741
+ width: 13px;
3742
+ height: 8px;
3743
+ border: none;
3744
+ border-radius: 0;
3745
+ background: transparent;
3746
+ }
3747
+
3748
+ .size-field .size-field-arrow {
3749
+ display: block;
3750
+ width: 7px;
3751
+ height: 5px;
3752
+ fill: #444;
3753
+ stroke: none;
3754
+ }
3755
+
3756
+ .size-field .size-field-btn:hover:not(.disabled) {
3757
+ background: #e2e2e2;
2775
3758
  }
2776
3759
 
2777
3760
  .nav-menu-item,
@@ -2937,6 +3920,14 @@ body.dragging-color-picker * {
2937
3920
  transform: translateY(0);
2938
3921
  }
2939
3922
 
3923
+ /* The toolbar being hovered comes forward, so that a tooltip drawn above a
3924
+ button is not painted behind the toolbar stacked over it. The tooltip's own
3925
+ z-index cannot do this: each toolbar is transformed, which makes it a
3926
+ stacking context its descendants cannot escape. */
3927
+ .floating-toolbar:hover {
3928
+ z-index: 1;
3929
+ }
3930
+
2940
3931
  .floating-toolbar-content {
2941
3932
  display: flex;
2942
3933
  align-items: center;
@@ -2967,6 +3958,16 @@ body.dragging-color-picker * {
2967
3958
  height: 24px;
2968
3959
  }
2969
3960
 
3961
+ .floating-toolbar-btn.text-btn {
3962
+ font-size: 12px;
3963
+ padding: 0 7px;
3964
+ width: auto;
3965
+ /* The toolbar is a flex row, which would otherwise squeeze a two-word
3966
+ label onto two lines and leave the button taller than its neighbours. */
3967
+ flex: none;
3968
+ white-space: nowrap;
3969
+ }
3970
+
2970
3971
  .floating-toolbar-btn svg * {
2971
3972
  fill: var(--theme-col);
2972
3973
  transition: fill 100ms ease;
@@ -2984,6 +3985,30 @@ body.dragging-color-picker * {
2984
3985
  background-color: #cdebf9;
2985
3986
  }
2986
3987
 
3988
+ /* A toggle button that is currently armed */
3989
+ .floating-toolbar-btn.selected {
3990
+ background-color: #cdebf9;
3991
+ }
3992
+
3993
+ .floating-toolbar-btn.selected svg * {
3994
+ fill: #166689;
3995
+ }
3996
+
3997
+ /* An icon button's blue wash goes with its themed icon, but a text button is
3998
+ grey and takes the grey the panels' action buttons use. A selected button
3999
+ keeps its own background while hovered rather than flashing back to grey. */
4000
+ .floating-toolbar-btn.text-btn:hover:not(.disabled):not(.selected) {
4001
+ background-color: #f0f0f0;
4002
+ }
4003
+
4004
+ .floating-toolbar-btn.text-btn:active:not(.disabled):not(.selected) {
4005
+ background-color: #e5e5e5;
4006
+ }
4007
+
4008
+ .floating-toolbar-btn.text-btn.selected {
4009
+ background-color: #e5e5e5;
4010
+ }
4011
+
2987
4012
  .floating-toolbar-btn.disabled {
2988
4013
  cursor: default;
2989
4014
  }
@@ -2992,8 +4017,83 @@ body.dragging-color-picker * {
2992
4017
  fill: #b8c4ca;
2993
4018
  }
2994
4019
 
2995
- /* Tooltip shown above the button on hover */
2996
- .floating-toolbar-btn[data-tooltip]:hover:not(.disabled)::after {
4020
+ /* A captioned control: a segmented mode switch or a short typed value. The
4021
+ caption is what lets the control be read at a glance, so it is always shown
4022
+ rather than hidden behind a tooltip. */
4023
+ .floating-toolbar-group {
4024
+ position: relative;
4025
+ display: flex;
4026
+ align-items: center;
4027
+ flex: none;
4028
+ gap: 5px;
4029
+ margin: 0 4px;
4030
+ }
4031
+
4032
+ .floating-toolbar-caption {
4033
+ font-size: 12px;
4034
+ color: #808080;
4035
+ white-space: nowrap;
4036
+ user-select: none;
4037
+ }
4038
+
4039
+ /* Both modes stay visible inside a single outlined well, so the lit one reads
4040
+ as the current state rather than as the result of clicking. */
4041
+ .floating-toolbar-segments {
4042
+ display: flex;
4043
+ align-items: center;
4044
+ background-color: #f0f0f0;
4045
+ border-radius: 6px;
4046
+ padding: 2px;
4047
+ gap: 2px;
4048
+ }
4049
+
4050
+ .floating-toolbar-segments .floating-toolbar-btn {
4051
+ height: 22px;
4052
+ border-radius: 4px;
4053
+ }
4054
+
4055
+ /* A segment sits on the well rather than on the toolbar, so its hover has to
4056
+ darken past the well's own grey to register. */
4057
+ .floating-toolbar-segments .floating-toolbar-btn:hover:not(.disabled):not(.selected) {
4058
+ background-color: #e4e4e4;
4059
+ }
4060
+
4061
+ .floating-toolbar-segments .floating-toolbar-btn.selected {
4062
+ background-color: #fff;
4063
+ color: #166689;
4064
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
4065
+ }
4066
+
4067
+ .floating-toolbar-field {
4068
+ cursor: default;
4069
+ }
4070
+
4071
+ .floating-toolbar-field input[type=text] {
4072
+ width: 48px;
4073
+ height: 22px;
4074
+ box-sizing: border-box;
4075
+ font-size: 12px;
4076
+ padding: 0 5px;
4077
+ border: 1px solid #ddd;
4078
+ border-radius: 4px;
4079
+ background-color: #fff;
4080
+ color: var(--dark-text-col);
4081
+ -webkit-appearance: none;
4082
+ appearance: none;
4083
+ outline: none;
4084
+ }
4085
+
4086
+ /* The same flat focus as the panels: the border turns gold and the global
4087
+ input rule's glow is suppressed. */
4088
+ .floating-toolbar-field input[type=text]:focus {
4089
+ border-color: #d3aa00;
4090
+ box-shadow: none;
4091
+ outline: none;
4092
+ }
4093
+
4094
+ /* Tooltip shown above the button (or captioned control) on hover */
4095
+ .floating-toolbar-btn[data-tooltip]:hover:not(.disabled)::after,
4096
+ .floating-toolbar-group[data-tooltip]:hover::after {
2997
4097
  content: attr(data-tooltip);
2998
4098
  position: absolute;
2999
4099
  bottom: calc(100% + 8px);