mapshaper 0.7.11 → 0.7.13

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.
@@ -6097,6 +6097,7 @@
6097
6097
  // gui.container.addClass('queued-files');
6098
6098
  El('#import-options').show();
6099
6099
  gui.container.classed('queued-files', queuedFiles.length > 0);
6100
+ updateSidebarTabZIndex();
6100
6101
  El('#path-import-options').classed('hidden', !filesMayContainPaths(queuedFiles));
6101
6102
  showQueuedFiles();
6102
6103
  updateGeoPackageLayerSelectionMenu();
@@ -6104,9 +6105,15 @@
6104
6105
 
6105
6106
  function hideImportMenu() {
6106
6107
  // gui.container.removeClass('queued-files');
6108
+ gui.container.removeClass('sidebar-tabs-over-popup');
6107
6109
  El('#import-options').hide();
6108
6110
  }
6109
6111
 
6112
+ function updateSidebarTabZIndex() {
6113
+ gui.container.classed('sidebar-tabs-over-popup',
6114
+ queuedFiles.length === 0 && !gui.sidebarPanelIsOpen());
6115
+ }
6116
+
6110
6117
  function getFileNames(files) {
6111
6118
  return Array.from(files).map(function(f) {return f.name;});
6112
6119
  }
@@ -6726,13 +6733,11 @@
6726
6733
 
6727
6734
  function message() {
6728
6735
  var msg = GUI.formatMessageArgs(arguments);
6729
- if (gui.notify) {
6730
- gui.notify({severity: 'info', body: msg});
6731
- } else {
6736
+ if (!gui.notify) {
6732
6737
  // Fallback for early messages before MessageControl is constructed
6733
6738
  gui.message(msg);
6734
- internal.logArgs(arguments);
6735
6739
  }
6740
+ internal.logArgs(arguments);
6736
6741
  }
6737
6742
 
6738
6743
  // CLI warnings used to surface as modal alerts, which interrupt the user
@@ -7929,7 +7934,7 @@
7929
7934
  function Console(gui) {
7930
7935
  var model = gui.model;
7931
7936
  var CURSOR = '$ ';
7932
- var PROMPT = 'Enter mapshaper commands or type "tips" for examples and console help';
7937
+ var PROMPT = 'Enter mapshaper commands or type "tips" for console help.';
7933
7938
  var el = gui.container.findChild('.console').hide();
7934
7939
  var content = el.findChild('.console-buffer');
7935
7940
  var log = El('div').appendTo(content);
@@ -7950,6 +7955,7 @@
7950
7955
  .on('keydown', function(e) {
7951
7956
  if (e.key == 'Enter' || e.key == ' ') {
7952
7957
  e.preventDefault();
7958
+ e.stopPropagation();
7953
7959
  toggle();
7954
7960
  }
7955
7961
  });
@@ -7965,7 +7971,7 @@
7965
7971
  this.runCommand = function(str) {
7966
7972
  str = str.trim();
7967
7973
  if (!str) return;
7968
- turnOn();
7974
+ gui.setSidebarPanel('console');
7969
7975
  submit(str);
7970
7976
  };
7971
7977
 
@@ -7983,10 +7989,17 @@
7983
7989
  });
7984
7990
 
7985
7991
  function toggle() {
7986
- if (_isOpen) turnOff();
7987
- else turnOn();
7992
+ gui.toggleSidebarPanel('console');
7988
7993
  }
7989
7994
 
7995
+ gui.on('sidebar', function(e) {
7996
+ if (e.name == 'console') {
7997
+ turnOn();
7998
+ } else if (e.prev == 'console') {
7999
+ turnOff();
8000
+ }
8001
+ });
8002
+
7990
8003
  function getHistory() {
7991
8004
  return GUI.getSavedValue('console_history') || [];
7992
8005
  }
@@ -8006,10 +8019,30 @@
8006
8019
  scrollDown();
8007
8020
  }
8008
8021
 
8022
+ function toLogNode(node, cname) {
8023
+ var msg = El('div').appendTo(log);
8024
+ if (cname) {
8025
+ msg.addClass(cname);
8026
+ }
8027
+ msg.node().appendChild(node);
8028
+ scrollDown();
8029
+ }
8030
+
8031
+ function getStructuredConsoleMessage(args) {
8032
+ var arr = utils$1.toArray(args);
8033
+ var msg;
8034
+ // print() sends its arguments as one array argument.
8035
+ if (arr.length == 1 && Array.isArray(arr[0])) {
8036
+ arr = arr[0];
8037
+ }
8038
+ msg = arr[arr.length - 1];
8039
+ return msg && msg.type && /^mapshaper-console-/.test(msg.type) ? msg : null;
8040
+ }
8041
+
8009
8042
  function turnOn() {
8010
8043
  // if (!_isOpen && !model.isEmpty()) {
8011
8044
  if (!_isOpen) {
8012
- btn.addClass('active');
8045
+ btn.addClass('active').attr('aria-expanded', 'true');
8013
8046
  _isOpen = true;
8014
8047
  // Route logging output to the in-app console while it's open, so a
8015
8048
  // user typing CLI commands here sees the results inline -- the same
@@ -8021,9 +8054,7 @@
8021
8054
  // gui instances with the console open. E.g. console could close
8022
8055
  // when an instance loses focus.
8023
8056
  internal.setLoggingFunctions(consoleMessage, consoleError, consoleStop, consoleWarn);
8024
- gui.container.addClass('console-open');
8025
8057
  el.show();
8026
- gui.dispatchEvent('resize');
8027
8058
  input.node().focus();
8028
8059
  history = getHistory();
8029
8060
  }
@@ -8031,7 +8062,7 @@
8031
8062
 
8032
8063
  function turnOff() {
8033
8064
  if (_isOpen) {
8034
- btn.removeClass('active');
8065
+ btn.removeClass('active').attr('aria-expanded', 'false');
8035
8066
  _isOpen = false;
8036
8067
  if (GUI.isActiveInstance(gui)) {
8037
8068
  setLoggingForGUI(gui); // reset stop, message and error functions
@@ -8039,8 +8070,6 @@
8039
8070
  el.hide();
8040
8071
  input.node().blur();
8041
8072
  saveHistory();
8042
- gui.container.removeClass('console-open');
8043
- gui.dispatchEvent('resize');
8044
8073
  }
8045
8074
  }
8046
8075
 
@@ -8100,7 +8129,7 @@
8100
8129
  if (gui.getMode()) {
8101
8130
  gui.clearMode(); // esc closes any open panels
8102
8131
  } else {
8103
- turnOff();
8132
+ gui.setSidebarPanel(null);
8104
8133
  }
8105
8134
  capture = true;
8106
8135
 
@@ -8136,8 +8165,8 @@
8136
8165
  } else if (kc == 40) {
8137
8166
  forward();
8138
8167
  } else if (kc == 32 && (!typing || (inputText === '' && typingInConsole))) {
8139
- // space bar closes if nothing has been typed
8140
- turnOff();
8168
+ // space bar toggles the sidebar if nothing has been typed
8169
+ gui.toggleSidebar();
8141
8170
  } else if (!typing && e.target != input.node() && !metaKey(e)) {
8142
8171
  // typing returns focus, unless a meta key is down (to allow Cmd-C copy)
8143
8172
  // or user is typing in a different input area somewhere
@@ -8154,9 +8183,9 @@
8154
8183
 
8155
8184
  // various shortcuts (while not typing in an input field or editable el)
8156
8185
  } else if (!typing) {
8157
- if (kc == 32) { // space bar opens console
8186
+ if (kc == 32) { // space bar toggles the sidebar
8158
8187
  capture = true;
8159
- turnOn();
8188
+ gui.toggleSidebar();
8160
8189
  // } else if (kc == 73) { // letter i opens inspector
8161
8190
  // gui.dispatchEvent('interaction_toggle');
8162
8191
  } else if (kc == 72) { // letter h resets map extent
@@ -8537,9 +8566,15 @@
8537
8566
  }
8538
8567
 
8539
8568
  function consoleMessage() {
8540
- var msg = GUI.formatMessageArgs(arguments);
8569
+ var structured = getStructuredConsoleMessage(arguments);
8570
+ var msg;
8541
8571
  if (internal.loggingEnabled()) {
8542
- toLog(msg, 'console-message');
8572
+ if (structured) {
8573
+ toLogNode(renderStructuredConsoleMessage(structured), 'console-message');
8574
+ } else {
8575
+ msg = GUI.formatMessageArgs(arguments);
8576
+ toLog(msg, 'console-message');
8577
+ }
8543
8578
  }
8544
8579
  }
8545
8580
 
@@ -8564,6 +8599,137 @@
8564
8599
  printExample("Clear the console", "$ clear");
8565
8600
  }
8566
8601
 
8602
+ function renderStructuredConsoleMessage(msg) {
8603
+ if (msg.type == 'mapshaper-console-help') {
8604
+ return renderHelpMessage(msg.lines);
8605
+ }
8606
+ if (msg.type == 'mapshaper-console-info') {
8607
+ return renderInfoMessage(msg.layers);
8608
+ }
8609
+ return document.createTextNode('');
8610
+ }
8611
+
8612
+ function renderHelpMessage(lines) {
8613
+ var container = document.createElement('div');
8614
+ var rows = [];
8615
+ container.className = 'console-help';
8616
+ (lines || []).forEach(function(line) {
8617
+ if (Array.isArray(line)) {
8618
+ rows.push(line);
8619
+ } else {
8620
+ flushRows();
8621
+ appendHelpLine(container, line);
8622
+ }
8623
+ });
8624
+ flushRows();
8625
+ return container;
8626
+
8627
+ function flushRows() {
8628
+ if (rows.length > 0) {
8629
+ container.appendChild(renderKeyValueTable(rows, 'console-help-table'));
8630
+ rows = [];
8631
+ }
8632
+ }
8633
+ }
8634
+
8635
+ function appendHelpLine(container, line) {
8636
+ var div = document.createElement('div');
8637
+ div.className = line ? 'console-help-line' : 'console-help-spacer';
8638
+ div.textContent = line || '';
8639
+ container.appendChild(div);
8640
+ }
8641
+
8642
+ function renderInfoMessage(layers) {
8643
+ var container = document.createElement('div');
8644
+ container.className = 'console-info';
8645
+ (layers || []).forEach(function(info) {
8646
+ var section = document.createElement('div');
8647
+ var title = document.createElement('div');
8648
+ section.className = 'console-info-layer';
8649
+ title.className = 'console-info-title';
8650
+ title.textContent = 'Layer: ' + (info.layer_name || '[unnamed layer]');
8651
+ section.appendChild(title);
8652
+ section.appendChild(renderKeyValueTable(getInfoRows(info), 'console-info-table'));
8653
+ section.appendChild(renderAttributeInfoTable(info.attribute_data));
8654
+ container.appendChild(section);
8655
+ });
8656
+ return container;
8657
+ }
8658
+
8659
+ function getInfoRows(info) {
8660
+ var rows = [
8661
+ ['Type', info.geometry_type || 'tabular data'],
8662
+ ['Records', utils$1.format('%,d', info.feature_count)]
8663
+ ];
8664
+ if (info.null_shape_count > 0) {
8665
+ rows.push(['Nulls', utils$1.format("%'d", info.null_shape_count)]);
8666
+ }
8667
+ if (info.geometry_type && info.feature_count > info.null_shape_count) {
8668
+ rows.push(['Bounds', info.bbox.join(',')]);
8669
+ rows.push(['CRS', info.proj4]);
8670
+ }
8671
+ rows.push(['Source', info.source_file || 'n/a']);
8672
+ return rows;
8673
+ }
8674
+
8675
+ function renderAttributeInfoTable(fields) {
8676
+ var wrapper = document.createElement('div');
8677
+ var title = document.createElement('div');
8678
+ wrapper.className = 'console-attribute-info';
8679
+ title.className = 'console-info-subtitle';
8680
+ title.textContent = 'Attribute data';
8681
+ wrapper.appendChild(title);
8682
+ if (!fields) {
8683
+ var none = document.createElement('div');
8684
+ none.className = 'console-info-empty';
8685
+ none.textContent = '[none]';
8686
+ wrapper.appendChild(none);
8687
+ return wrapper;
8688
+ }
8689
+ wrapper.appendChild(renderDataTable(
8690
+ [['Field', 'First value']].concat(fields.map(function(o) {
8691
+ var valKey = 'first_value' in o ? 'first_value' : 'value';
8692
+ return [o.field, formatInfoValue(o[valKey])];
8693
+ })),
8694
+ 'console-attribute-table'
8695
+ ));
8696
+ return wrapper;
8697
+ }
8698
+
8699
+ function renderKeyValueTable(rows, className) {
8700
+ return renderDataTable(rows, className + ' console-key-value-table');
8701
+ }
8702
+
8703
+ function renderDataTable(rows, className) {
8704
+ var table = document.createElement('table');
8705
+ var tbody = document.createElement('tbody');
8706
+ table.className = className;
8707
+ rows.forEach(function(row, i) {
8708
+ var tr = document.createElement('tr');
8709
+ row.forEach(function(val) {
8710
+ var cell = document.createElement(i === 0 && rows.length > 1 && className == 'console-attribute-table' ? 'th' : 'td');
8711
+ cell.textContent = val == null ? '' : String(val);
8712
+ tr.appendChild(cell);
8713
+ });
8714
+ tbody.appendChild(tr);
8715
+ });
8716
+ table.appendChild(tbody);
8717
+ return table;
8718
+ }
8719
+
8720
+ function formatInfoValue(val) {
8721
+ if (val === null || val === undefined) return '';
8722
+ if (utils$1.isString(val)) {
8723
+ return "'" + val.replace(/[\r\t\n]/g, function(c) {
8724
+ return c == '\n' ? '\\n' : c == '\r' ? '\\r' : '\\t';
8725
+ }) + "'";
8726
+ }
8727
+ if (utils$1.isNumber(val) || val === true || val === false) {
8728
+ return String(val);
8729
+ }
8730
+ return JSON.stringify(val);
8731
+ }
8732
+
8567
8733
  function saveRuntimeContext() {
8568
8734
  var json = stringifyRuntimeStateContext(gui);
8569
8735
  saveBlobToLocalFile2('mapshaper-runtime-context.json', new Blob([json], {type: 'application/json'}));
@@ -9150,6 +9316,7 @@
9150
9316
  }
9151
9317
 
9152
9318
  var openMenu;
9319
+ var openMenuId;
9153
9320
 
9154
9321
  document.addEventListener('mousedown', function(e) {
9155
9322
  if (e.target.classList.contains('contextmenu-item')) {
@@ -9158,17 +9325,23 @@
9158
9325
  closeOpenMenu();
9159
9326
  });
9160
9327
 
9161
- function closeOpenMenu() {
9328
+ function closeOpenMenu(immediate) {
9162
9329
  if (openMenu) {
9163
- openMenu.close();
9330
+ openMenu.close(immediate);
9164
9331
  openMenu = null;
9332
+ openMenuId = null;
9165
9333
  }
9166
9334
  }
9167
9335
 
9168
9336
  function openContextMenu(e, lyr, parent) {
9169
9337
  var menu = new ContextMenu(parent);
9170
- closeOpenMenu();
9338
+ if (e.contextMenuId && e.contextMenuId == openMenuId) {
9339
+ closeOpenMenu(true);
9340
+ return;
9341
+ }
9342
+ closeOpenMenu(true);
9171
9343
  menu.open(e, lyr);
9344
+ openMenuId = e.contextMenuId || null;
9172
9345
  }
9173
9346
 
9174
9347
  function ContextMenu(parentArg) {
@@ -9185,9 +9358,14 @@
9185
9358
 
9186
9359
  this.close = close;
9187
9360
 
9188
- function close() {
9361
+ function close(immediate) {
9189
9362
  var count = _openCount;
9190
9363
  if (!_open) return;
9364
+ if (immediate) {
9365
+ menu.hide();
9366
+ _open = false;
9367
+ return;
9368
+ }
9191
9369
  setTimeout(function() {
9192
9370
  if (count == _openCount) {
9193
9371
  menu.hide();
@@ -9348,8 +9526,10 @@
9348
9526
  function LayerControl(gui) {
9349
9527
  var model = gui.model;
9350
9528
  var map = gui.map;
9351
- var el = gui.container.findChild(".layer-control").on('click', GUI.handleDirectEvent(gui.clearMode));
9529
+ var el = gui.container.findChild(".layer-control");
9352
9530
  var btn = gui.container.findChild('.layer-control-btn');
9531
+ var headerBtn = btn.findChild('.active-layer-label');
9532
+ var tab = gui.container.findChild('.layer-tab');
9353
9533
  var isOpen = false;
9354
9534
  var cache = new DomCache();
9355
9535
  var pinAll = el.findChild('.pin-all'); // button for toggling layer visibility
@@ -9359,7 +9539,34 @@
9359
9539
  var dragging = false;
9360
9540
  var layerOrderSlug;
9361
9541
 
9362
- gui.addMode('layer_menu', turnOn, turnOff, btn.findChild('.header-btn'));
9542
+ headerBtn.on('click', function() {
9543
+ toggle();
9544
+ }).on('keydown', function(e) {
9545
+ if (e.key == 'Enter' || e.key == ' ') {
9546
+ e.preventDefault();
9547
+ e.stopPropagation();
9548
+ toggle();
9549
+ }
9550
+ });
9551
+ tab.on('click', toggle).on('keydown', function(e) {
9552
+ if (e.key == 'Enter' || e.key == ' ') {
9553
+ e.preventDefault();
9554
+ e.stopPropagation();
9555
+ toggle();
9556
+ }
9557
+ });
9558
+
9559
+ function toggle() {
9560
+ gui.toggleSidebarPanel('layers');
9561
+ }
9562
+
9563
+ gui.on('sidebar', function(e) {
9564
+ if (e.name == 'layers') {
9565
+ turnOn();
9566
+ } else if (e.prev == 'layers') {
9567
+ turnOff();
9568
+ }
9569
+ });
9363
9570
 
9364
9571
  // kludge to show menu button after initial import dialog is dismissed
9365
9572
  gui.on('mode', function(e) {
@@ -9445,53 +9652,33 @@
9445
9652
  }
9446
9653
 
9447
9654
  function turnOn() {
9655
+ if (isOpen) return;
9448
9656
  isOpen = true;
9449
- el.findChild('div.info-box-scrolled').css('max-height', El('body').height() - 80);
9657
+ tab.addClass('active').attr('aria-expanded', 'true');
9450
9658
  render();
9451
9659
  el.show();
9452
9660
  }
9453
9661
 
9454
9662
  function turnOff() {
9663
+ if (!isOpen) return;
9455
9664
  stopDragging();
9456
9665
  isOpen = false;
9666
+ tab.removeClass('active').attr('aria-expanded', 'false');
9457
9667
  el.hide();
9458
9668
  }
9459
9669
 
9460
9670
  function updateMenuBtn() {
9461
9671
  var lyr = model.getActiveLayer()?.layer;
9462
9672
  var lyrName = lyr?.name || '';
9463
- var menuTitle = lyrName || lyr && '[unnamed layer]' || '[no data]';
9464
9673
  var pageTitle = lyrName || 'mapshaper';
9465
- btn.classed('active', 'true').findChild('.layer-name').html(menuTitle + "  ▼");
9674
+ btn.classed('active', !!lyr);
9675
+ headerBtn.text(lyr ? 'Active: ' + formatLayerNameForDisplay(lyr.name) : '');
9466
9676
  window.document.title = pageTitle;
9467
9677
  }
9468
9678
 
9469
9679
  function render() {
9470
9680
  renderLayerList();
9471
- renderSourceFileList();
9472
- }
9473
-
9474
- function renderSourceFileList() {
9475
9681
  el.findChild('.no-layer-note').classed('hidden', model.getActiveLayer());
9476
- el.findChild('.source-file-section').classed('hidden', !model.getActiveLayer());
9477
- var list = el.findChild('.file-list');
9478
- var files = [];
9479
- list.empty();
9480
- model.forEachLayer(function(lyr, dataset) {
9481
- var file = internal.getLayerSourceFile(lyr, dataset);
9482
- if (!file || files.includes(file)) return;
9483
- files.push(file);
9484
- var warnings = getWarnings(lyr, dataset);
9485
- var html = '<div class="layer-item">';
9486
- html += rowHTML('name', file);
9487
- if (warnings) {
9488
- // html += rowHTML('problems', warnings, 'layer-problems');
9489
- html += rowHTML('', warnings, 'layer-problems');
9490
- }
9491
- html += '</div>';
9492
- list.appendChild(El('div').html(html).firstChild());
9493
- });
9494
-
9495
9682
  }
9496
9683
 
9497
9684
  function renderLayerList() {
@@ -9552,10 +9739,10 @@
9552
9739
  html = '<!-- ' + lyr.menu_id + '--><div class="' + classes + '">';
9553
9740
  html += rowHTML('name', '<span class="layer-name colored-text dot-underline">' + formatLayerNameForDisplay(lyr.name) + '</span>', 'row1');
9554
9741
  html += rowHTML('contents', describeLyr(lyr, dataset));
9555
- // html += '<img class="close-btn" draggable="false" src="images/close.png">';
9742
+ html += '<span class="more-btn layer-btn" role="button" tabindex="0" aria-label="More layer options"></span>';
9556
9743
  if (opts.pinnable) {
9557
- html += '<img class="eye-btn black-eye" draggable="false" src="images/eye.png">';
9558
- html += '<img class="eye-btn green-eye" draggable="false" src="images/eye2.png">';
9744
+ html += '<img class="eye-btn black-eye layer-btn" draggable="false" src="images/eye.png">';
9745
+ html += '<img class="eye-btn green-eye layer-btn" draggable="false" src="images/eye2.png">';
9559
9746
  }
9560
9747
  html += '</div>';
9561
9748
  return html;
@@ -9563,8 +9750,10 @@
9563
9750
 
9564
9751
  function initMouseEvents(entry, id, pinnable) {
9565
9752
  entry.on('mouseover', init);
9753
+ entry.on('focusin', init);
9566
9754
  function init() {
9567
9755
  entry.removeEventListener('mouseover', init);
9756
+ entry.removeEventListener('focusin', init);
9568
9757
  initMouseEvents2(entry, id, pinnable);
9569
9758
  }
9570
9759
  }
@@ -9580,10 +9769,6 @@
9580
9769
  }
9581
9770
  // start dragging when button is first pressed
9582
9771
  if (e.buttons && !dragTargetId) {
9583
- // don't start dragging if pointer is over the close button
9584
- // (before, clicking this button wqs finicky -- the mouse had to remain
9585
- // perfectly still between mousedown and mouseup)
9586
- if (El(e.target).hasClass('close-btn')) return;
9587
9772
  dragTargetId = id;
9588
9773
  entry.addClass('drag-target');
9589
9774
  }
@@ -9608,6 +9793,7 @@
9608
9793
  }
9609
9794
 
9610
9795
  function initMouseEvents2(entry, id, pinnable) {
9796
+ var moreBtn = entry.findChild('.more-btn');
9611
9797
  initLayerDragging(entry, id);
9612
9798
 
9613
9799
  function deleteLayer() {
@@ -9632,7 +9818,7 @@
9632
9818
  }
9633
9819
  }
9634
9820
 
9635
- function selectLayer(closeMenu) {
9821
+ function selectLayer() {
9636
9822
  var target = findLayerById(id);
9637
9823
  // don't select if user is typing or dragging
9638
9824
  if (GUI.textIsSelected() || dragging) return;
@@ -9641,17 +9827,24 @@
9641
9827
  if (!map.isActiveLayer(target.layer)) {
9642
9828
  model.selectLayer(target.layer, target.dataset);
9643
9829
  }
9644
- // close menu after a delay
9645
- if (closeMenu === true) setTimeout(function() {
9646
- gui.clearMode();
9647
- }, 230);
9648
9830
  }
9649
9831
 
9650
- // init delete button
9651
- // GUI.onClick(entry.findChild('img.close-btn'), function(e) {
9652
- // e.stopPropagation();
9653
- // deleteLayer();
9654
- // });
9832
+ function openLayerMenu(e) {
9833
+ var menuEvent = e;
9834
+ e.stopPropagation();
9835
+ if (!isFinite(e.pageX) || !isFinite(e.pageY)) {
9836
+ var rect = moreBtn.node().getBoundingClientRect();
9837
+ menuEvent = {
9838
+ pageX: rect.right,
9839
+ pageY: rect.top + rect.height / 2
9840
+ };
9841
+ }
9842
+ menuEvent.deleteLayer = deleteLayer;
9843
+ menuEvent.selectLayer = selectLayer;
9844
+ menuEvent.contextMenuId = 'layer-' + id;
9845
+ openContextMenu(menuEvent, null, null);
9846
+ }
9847
+
9655
9848
 
9656
9849
  if (pinnable) {
9657
9850
  // init pin button
@@ -9694,18 +9887,22 @@
9694
9887
  renameLayer(target, str);
9695
9888
  });
9696
9889
 
9890
+ moreBtn.on('mousedown', function(e) {
9891
+ e.stopPropagation();
9892
+ });
9893
+ GUI.onClick(moreBtn, openLayerMenu);
9894
+ moreBtn.on('keydown', function(e) {
9895
+ if (e.key == 'Enter' || e.key == ' ') {
9896
+ e.preventDefault();
9897
+ openLayerMenu(e);
9898
+ }
9899
+ });
9900
+
9697
9901
  // init click-to-select
9698
9902
  GUI.onClick(entry, function() {
9699
- selectLayer(true);
9903
+ selectLayer();
9700
9904
  });
9701
9905
 
9702
- GUI.onContextClick(entry, function(e) {
9703
- e.deleteLayer = deleteLayer;
9704
- e.selectLayer = selectLayer;
9705
- // contextMenu.open(e);
9706
- // openContextMenu(e, null, entry.node())
9707
- openContextMenu(e, null, null);
9708
- });
9709
9906
  }
9710
9907
 
9711
9908
  function describeLyr(lyr, dataset) {
@@ -9752,30 +9949,6 @@
9752
9949
  }
9753
9950
  }
9754
9951
 
9755
- function getWarnings(lyr, dataset) {
9756
- var file = internal.getLayerSourceFile(lyr, dataset);
9757
- var missing = [];
9758
- var msg;
9759
- // show missing file warning for first layer in dataset
9760
- // (assuming it represents the content of the original file)
9761
- if (utils$1.endsWith(file, '.shp') && lyr == dataset.layers[0]) {
9762
- if (!lyr.data) {
9763
- missing.push('.dbf');
9764
- }
9765
- if (!dataset.info.wkt1 && !dataset.info.crs) {
9766
- missing.push('.prj');
9767
- }
9768
- }
9769
- if (missing.length) {
9770
- msg = 'missing ' + missing.join(' and ') + ' data';
9771
- }
9772
- return msg;
9773
- }
9774
-
9775
- function describeSrc(lyr, dataset) {
9776
- return internal.getLayerSourceFile(lyr, dataset);
9777
- }
9778
-
9779
9952
  function isPinnable(lyr) {
9780
9953
  return internal.layerIsGeometric(lyr) || internal.layerHasFurniture(lyr);
9781
9954
  }
@@ -9972,7 +10145,7 @@
9972
10145
  function getRestoreDataNote(gui) {
9973
10146
  var stats = getUndoPayloadStats(gui);
9974
10147
  var bytes = stats ? stats.ownBytes || 0 : 0;
9975
- return 'estimated on-disk restore data: ' + formatBytes(bytes);
10148
+ return 'restore data stored on-disk: ' + formatBytes(bytes);
9976
10149
  }
9977
10150
 
9978
10151
  function getUndoPayloadStats(gui) {
@@ -11993,8 +12166,8 @@
11993
12166
  // DELETE key
11994
12167
  // delete pinned feature
11995
12168
  // to help protect against inadvertent deletion, don't delete
11996
- // when console is open or a popup menu is open
11997
- if (!gui.getMode() && !gui.consoleIsOpen()) {
12169
+ // when a sidebar panel or popup menu is open
12170
+ if (!gui.getMode() && !gui.sidebarPanelIsOpen()) {
11998
12171
  internal.deleteFeatureById(targetLayer, pinnedId());
11999
12172
  self.clearSelection();
12000
12173
  gui.model.updated({flags: 'filter'}); // signal map to update
@@ -18328,6 +18501,10 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
18328
18501
 
18329
18502
 
18330
18503
  gui.state = {};
18504
+ var sidebarPanel = null;
18505
+ var lastSidebarPanel = 'console';
18506
+ var sidebarWidth = GUI.getSavedValue('sidebar_width') || 0;
18507
+ var sidebarResizeFrame = null;
18331
18508
 
18332
18509
  var msgCount = 0;
18333
18510
  var clearMsg;
@@ -18368,10 +18545,50 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
18368
18545
  // if (gui.progressMessage) gui.progressMessage.hide();
18369
18546
  };
18370
18547
 
18548
+ if (sidebarWidth) {
18549
+ setSidebarWidth(sidebarWidth);
18550
+ }
18551
+
18552
+ gui.getSidebarPanel = function() {
18553
+ return sidebarPanel;
18554
+ };
18555
+
18556
+ gui.setSidebarPanel = function(name) {
18557
+ var prev = sidebarPanel;
18558
+ sidebarPanel = name || null;
18559
+ if (sidebarPanel && gui.getMode()) {
18560
+ gui.clearMode();
18561
+ }
18562
+ if (sidebarPanel == prev) return;
18563
+ if (sidebarPanel) {
18564
+ lastSidebarPanel = sidebarPanel;
18565
+ }
18566
+ gui.container
18567
+ .classed('sidebar-open', !!sidebarPanel)
18568
+ .classed('layers-open', sidebarPanel == 'layers')
18569
+ .classed('console-open', sidebarPanel == 'console');
18570
+ gui.dispatchEvent('sidebar', {name: sidebarPanel, prev: prev});
18571
+ gui.dispatchEvent('resize');
18572
+ };
18573
+
18574
+ gui.toggleSidebarPanel = function(name) {
18575
+ gui.setSidebarPanel(sidebarPanel == name ? null : name);
18576
+ };
18577
+
18578
+ gui.toggleSidebar = function() {
18579
+ gui.setSidebarPanel(sidebarPanel ? null : lastSidebarPanel);
18580
+ };
18581
+
18582
+ gui.sidebarPanelIsOpen = function() {
18583
+ return !!sidebarPanel;
18584
+ };
18585
+
18371
18586
  gui.consoleIsOpen = function() {
18372
- return gui.container.hasClass('console-open');
18587
+ return sidebarPanel == 'console';
18373
18588
  };
18374
18589
 
18590
+ initSidebarResizing();
18591
+
18375
18592
  gui.getRuntimeStateContext = function() {
18376
18593
  return getRuntimeStateContext(gui);
18377
18594
  };
@@ -18413,6 +18630,50 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
18413
18630
  }
18414
18631
 
18415
18632
  return gui;
18633
+
18634
+ function initSidebarResizing() {
18635
+ var handle = gui.container.findChild('.sidebar-resize-handle');
18636
+ if (!handle) return;
18637
+ handle.on('mousedown', function(e) {
18638
+ if (!gui.sidebarPanelIsOpen()) return;
18639
+ e.preventDefault();
18640
+ e.stopPropagation();
18641
+ gui.container.addClass('sidebar-resizing');
18642
+ window.addEventListener('mousemove', onMove);
18643
+ window.addEventListener('mouseup', onRelease);
18644
+ });
18645
+
18646
+ function onMove(e) {
18647
+ setSidebarWidth(e.pageX);
18648
+ scheduleSidebarResize();
18649
+ }
18650
+
18651
+ function onRelease() {
18652
+ window.removeEventListener('mousemove', onMove);
18653
+ window.removeEventListener('mouseup', onRelease);
18654
+ gui.container.removeClass('sidebar-resizing');
18655
+ GUI.setSavedValue('sidebar_width', sidebarWidth);
18656
+ scheduleSidebarResize();
18657
+ }
18658
+ }
18659
+
18660
+ function setSidebarWidth(width) {
18661
+ sidebarWidth = clampSidebarWidth(width);
18662
+ gui.container.node().style.setProperty('--left-sidebar-width', sidebarWidth + 'px');
18663
+ }
18664
+
18665
+ function clampSidebarWidth(width) {
18666
+ var max = Math.min(720, Math.round(window.innerWidth * 0.6));
18667
+ return Math.max(220, Math.min(max, Math.round(width)));
18668
+ }
18669
+
18670
+ function scheduleSidebarResize() {
18671
+ if (sidebarResizeFrame) return;
18672
+ sidebarResizeFrame = requestAnimationFrame(function() {
18673
+ sidebarResizeFrame = null;
18674
+ gui.dispatchEvent('resize');
18675
+ });
18676
+ }
18416
18677
  }
18417
18678
 
18418
18679
  function createUndoTestApi(gui) {