raintee-maputils 1.0.34 → 1.0.35

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/dist/index.js CHANGED
@@ -6561,245 +6561,543 @@ class RulerControl {
6561
6561
 
6562
6562
  class CustomOptionsControl {
6563
6563
  constructor(args) {
6564
- const { options, onConfirm } = args;
6564
+ const { title, options, onConfirm } = args;
6565
6565
 
6566
- this._container = null;
6567
6566
  this._map = null;
6567
+ this._container = null;
6568
6568
 
6569
6569
  this._options = options || [];
6570
+ this._title = title || '标题';
6570
6571
  this._onConfirm = onConfirm || (() => { });
6571
-
6572
- if (!this._options || !Array.isArray(this._options)) {
6573
- console.error('请传入有效的 options 参数(数组)');
6574
- }
6572
+ this._originalOptions = [...this._options];
6573
+ this._selectedOptions = [];
6575
6574
  }
6576
6575
 
6577
6576
  onAdd(map) {
6578
6577
  this._map = map;
6579
6578
 
6580
- // 创建控件外层容器
6581
6579
  this._container = document.createElement('div');
6582
6580
  this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group custom-options-control';
6583
6581
 
6584
- // 创建主按钮(初始只显示“三维模型”文字,点击展开)
6585
6582
  const mainButton = document.createElement('button');
6583
+ mainButton.textContent = this._title;
6586
6584
  mainButton.style.cssText = `
6587
- width: 100%;
6588
- height: 100%;
6589
- padding: 0.25rem 0.5rem;
6590
- margin: 0;
6591
- border: 1px solid #ccc;
6585
+ padding: 4px 8px;
6592
6586
  background: white;
6593
- border-radius: 4px;
6587
+ border: 1px solid #ccc;
6594
6588
  cursor: pointer;
6595
- font-size: 14px;
6596
- text-align: left;
6597
6589
  `;
6598
- mainButton.textContent = '三维模型';
6599
- mainButton.addEventListener('click', () => this._toggleExpanded());
6600
6590
 
6601
- this._container.appendChild(mainButton);
6602
-
6603
- // 创建隐藏的选项面板(默认不显示)
6604
- this._panel = document.createElement('div');
6605
- this._panel.style.display = 'none';
6606
- this._panel.style.padding = '10px';
6607
- this._container.appendChild(this._panel);
6608
-
6609
- // 创建选项列表和按钮组
6610
- this._renderOptionsPanel();
6591
+ mainButton.addEventListener('click', () => {
6592
+ this._showModal();
6593
+ });
6611
6594
 
6595
+ this._container.appendChild(mainButton);
6612
6596
  return this._container;
6613
6597
  }
6614
6598
 
6615
- _toggleExpanded() {
6616
- const isExpanded = this._panel.style.display !== 'none';
6617
- this._setExpanded(!isExpanded);
6618
- }
6619
-
6620
- _setExpanded(isExpanded) {
6621
- this._panel.style.display = isExpanded ? 'block' : 'none';
6622
- }
6623
-
6624
- _renderOptionsPanel() {
6625
- // 清空面板内容
6626
- this._panel.innerHTML = '';
6627
-
6628
- // 创建复选框选项列表
6629
- this._options.forEach((opt) => {
6630
- const optionDiv = document.createElement('div');
6631
- optionDiv.style.marginBottom = '6px';
6632
-
6633
- const label = document.createElement('label');
6634
- label.style.display = 'flex';
6635
- label.style.alignItems = 'center';
6636
- label.style.cursor = 'pointer';
6599
+ /** 创建遮罩弹窗 */
6600
+ _showModal() {
6601
+ // 创建遮罩层
6602
+ this._overlay = document.createElement('div');
6603
+ this._overlay.className = 'custom-modal-overlay';
6604
+ this._overlay.style.cssText = `
6605
+ position: fixed;
6606
+ top: 0; left: 0; right: 0; bottom: 0;
6607
+ background: rgba(0,0,0,0.5);
6608
+ z-index: 9999;
6609
+ display: flex;
6610
+ justify-content: center;
6611
+ align-items: center;
6612
+ `;
6637
6613
 
6638
- const checkbox = document.createElement('input');
6639
- checkbox.type = 'checkbox';
6640
- checkbox.value = opt.value;
6641
- checkbox.style.marginRight = '8px';
6614
+ // 弹窗容器
6615
+ this._modal = document.createElement('div');
6616
+ this._modal.className = 'custom-modal';
6617
+ this._modal.style.cssText = `
6618
+ width: 360px;
6619
+ max-height: 80vh;
6620
+ overflow: hidden;
6621
+ background: white;
6622
+ border-radius: 8px;
6623
+ padding: 20px;
6624
+ display: flex;
6625
+ flex-direction: column;
6626
+ `;
6642
6627
 
6643
- // 绑定选中状态
6644
- checkbox.checked = this._getSelectedOption(opt.value);
6645
- checkbox.addEventListener('change', () => this._toggleOption(opt.value));
6628
+ // 搜索框
6629
+ const searchInput = document.createElement('input');
6630
+ searchInput.placeholder = '搜索选项...';
6631
+ searchInput.className = 'custom-search-input';
6632
+ searchInput.style.cssText = `
6633
+ width: 100%;
6634
+ padding: 8px;
6635
+ border: 1px solid #ccc;
6636
+ border-radius: 4px;
6637
+ margin-bottom: 12px;
6638
+ `;
6639
+ searchInput.addEventListener('input', (e) => {
6640
+ this._renderList(e.target.value);
6641
+ });
6646
6642
 
6647
- const span = document.createElement('span');
6648
- span.textContent = opt.label;
6643
+ this._modal.appendChild(searchInput);
6649
6644
 
6650
- label.appendChild(checkbox);
6651
- label.appendChild(span);
6652
- optionDiv.appendChild(label);
6653
- this._panel.appendChild(optionDiv);
6654
- });
6645
+ // 列表区域
6646
+ this._listBox = document.createElement('div');
6647
+ this._listBox.style.cssText = `
6648
+ flex: 1;
6649
+ overflow-y: auto;
6650
+ padding-right: 4px;
6651
+ `;
6652
+ this._modal.appendChild(this._listBox);
6655
6653
 
6656
- // 创建按钮组:取消 / 确定
6657
- const buttonGroup = document.createElement('div');
6658
- buttonGroup.style.marginTop = '12px';
6659
- buttonGroup.style.display = 'flex';
6660
- buttonGroup.style.gap = '8px';
6661
- buttonGroup.style.justifyContent = 'flex-end';
6654
+ // 底部按钮
6655
+ const footer = document.createElement('div');
6656
+ footer.style.cssText = `
6657
+ margin-top: 12px;
6658
+ display: flex;
6659
+ justify-content: flex-end;
6660
+ gap: 8px;
6661
+ `;
6662
6662
 
6663
- const cancelButton = document.createElement('button');
6664
- cancelButton.textContent = '取消';
6665
- cancelButton.style.cssText = `
6663
+ const cancelBtn = document.createElement('button');
6664
+ cancelBtn.textContent = '取消';
6665
+ cancelBtn.style.cssText = `
6666
6666
  padding: 6px 12px;
6667
6667
  background: #ccc;
6668
6668
  border: none;
6669
6669
  border-radius: 4px;
6670
- cursor: pointer;
6671
- width: auto;
6672
6670
  `;
6673
- cancelButton.addEventListener('click', () => this._handleCancel());
6671
+ cancelBtn.addEventListener('click', () => this._closeModal());
6674
6672
 
6675
- const confirmButton = document.createElement('button');
6676
- confirmButton.textContent = '确定';
6677
- confirmButton.style.cssText = `
6673
+ const okBtn = document.createElement('button');
6674
+ okBtn.textContent = '确定';
6675
+ okBtn.style.cssText = `
6678
6676
  padding: 6px 12px;
6679
6677
  background: #007cba;
6680
6678
  color: white;
6681
6679
  border: none;
6682
6680
  border-radius: 4px;
6683
- cursor: pointer;
6684
- width: auto;
6685
6681
  `;
6686
- confirmButton.addEventListener('click', () => this._handleConfirm());
6682
+ okBtn.addEventListener('click', () => this._confirm());
6687
6683
 
6688
- buttonGroup.appendChild(cancelButton);
6689
- buttonGroup.appendChild(confirmButton);
6690
- this._panel.appendChild(buttonGroup);
6691
- }
6684
+ footer.appendChild(cancelBtn);
6685
+ footer.appendChild(okBtn);
6686
+ this._modal.appendChild(footer);
6692
6687
 
6693
- _getSelectedOption(value) {
6694
- return this._selectedOptions?.includes(value) || false;
6695
- }
6688
+ this._overlay.appendChild(this._modal);
6689
+ document.body.appendChild(this._overlay);
6696
6690
 
6697
- _toggleOption(value) {
6698
- if (!this._selectedOptions) this._selectedOptions = [];
6691
+ // 初始化列表
6692
+ this._renderList('');
6693
+ }
6699
6694
 
6700
- const idx = this._selectedOptions.indexOf(value);
6701
- if (idx > -1) {
6702
- this._selectedOptions.splice(idx, 1);
6703
- } else {
6704
- this._selectedOptions.push(value);
6695
+ /** 搜索过滤 */
6696
+ _renderList(keyword) {
6697
+ this._listBox.innerHTML = '';
6698
+
6699
+ const list = keyword
6700
+ ? this._originalOptions.filter(
6701
+ (o) =>
6702
+ o.label.toLowerCase().includes(keyword.toLowerCase()) ||
6703
+ String(o.value).toLowerCase().includes(keyword.toLowerCase())
6704
+ )
6705
+ : this._originalOptions;
6706
+
6707
+ if (list.length === 0) {
6708
+ const empty = document.createElement('div');
6709
+ empty.textContent = `未找到相关结果`;
6710
+ empty.style.textAlign = 'center';
6711
+ empty.style.padding = '20px';
6712
+ this._listBox.appendChild(empty);
6713
+ return;
6705
6714
  }
6706
6715
 
6707
- // 可选:重新渲染复选框状态(如果需要动态更新 UI,但目前 onchange 已处理)
6716
+ list.forEach((opt) => {
6717
+ const item = document.createElement('label');
6718
+ item.style.cssText = `
6719
+ display: flex;
6720
+ align-items: center;
6721
+ padding: 6px 0;
6722
+ cursor: pointer;
6723
+ `;
6724
+
6725
+ const checkbox = document.createElement('input');
6726
+ checkbox.type = 'checkbox';
6727
+ checkbox.value = opt.value;
6728
+ checkbox.checked = this._selectedOptions.includes(opt.value);
6729
+ checkbox.style.marginRight = '8px';
6730
+ checkbox.addEventListener('change', () => this._toggle(opt.value));
6731
+
6732
+ const labelSpan = document.createElement('span');
6733
+ labelSpan.innerHTML = this._highlight(opt.label, keyword);
6734
+
6735
+ item.appendChild(checkbox);
6736
+ item.appendChild(labelSpan);
6737
+
6738
+ this._listBox.appendChild(item);
6739
+ });
6708
6740
  }
6709
6741
 
6710
- _handleConfirm() {
6711
- this._setExpanded(false);
6742
+ /** 高亮关键词 */
6743
+ _highlight(text, key) {
6744
+ if (!key) return text;
6745
+ return text.replace(new RegExp(`(${key})`, 'gi'), `<mark>$1</mark>`);
6746
+ }
6747
+
6748
+ /** 选择切换 */
6749
+ _toggle(value) {
6750
+ const index = this._selectedOptions.indexOf(value);
6751
+ if (index >= 0) this._selectedOptions.splice(index, 1);
6752
+ else this._selectedOptions.push(value);
6753
+ }
6712
6754
 
6755
+ /** 点击确定 */
6756
+ _confirm() {
6713
6757
  this._onConfirm({
6714
- selectedOptions: this._selectedOptions || [],
6715
- unselectedOptions: this._options.filter((item) => !this._selectedOptions?.includes(item.value)),
6716
- allOptions: this._options,
6758
+ selectedOptions: this._selectedOptions,
6759
+ unselectedOptions: this._originalOptions.filter(
6760
+ (o) => !this._selectedOptions.includes(o.value)
6761
+ ),
6762
+ allOptions: this._originalOptions
6717
6763
  });
6764
+ this._closeModal();
6718
6765
  }
6719
6766
 
6720
- _handleCancel() {
6721
- this._setExpanded(false);
6767
+ /** 销毁弹窗 */
6768
+ _closeModal() {
6769
+ if (this._overlay) {
6770
+ document.body.removeChild(this._overlay);
6771
+ this._overlay = null;
6772
+ this._modal = null;
6773
+ }
6722
6774
  }
6723
6775
 
6724
6776
  onRemove() {
6725
- if (this._container && this._container.parentNode) {
6726
- this._container.parentNode.removeChild(this._container);
6727
- }
6777
+ if (this._container) this._container.remove();
6778
+ this._closeModal();
6728
6779
  }
6729
6780
  }
6730
6781
 
6731
- // 自定义控件类:ToggleControl
6782
+ // 自定义控件类:ToggleControl(带搜索过滤功能)
6732
6783
  class CustomToggleControl {
6733
6784
  /**
6734
6785
  * 构造函数
6735
6786
  * @param {Object} options
6736
- * @param {string} options.name - 控件名称(用于识别,可选展示)
6737
- * @param {string} options.field - 控制的字段名,对应 map.SourceMap[field]
6738
- * @param {boolean} options.defaultValue - 默认值(当 field 未定义时使用)
6739
- * @param {string} options.svgIcon - SVG 图标字符串,例如 '<svg>...</svg>'
6740
- * @param {Function} [options.onToggle] - 可选的回调函数,状态切换后调用,参数为最新的布尔值
6787
+ * @param {string} options.name - 控件名称
6788
+ * @param {string} options.field - 控制的字段名
6789
+ * @param {boolean} options.defaultValue - 默认值
6790
+ * @param {string} options.svgIcon - SVG 图标字符串
6791
+ * @param {Function} [options.onToggle] - 状态切换回调
6792
+ * @param {Array} [options.options] - 可选项目数组 [{id, name, icon?}]
6793
+ * @param {Function} [options.onOptionSelect] - 选项选择回调
6794
+ * @param {boolean} [options.showSearch] - 是否显示搜索框,默认 true
6741
6795
  */
6742
- constructor({ name, field, defaultValue = false, svgIcon, onToggle }) {
6796
+ constructor({
6797
+ name,
6798
+ field,
6799
+ defaultValue = false,
6800
+ svgIcon,
6801
+ onToggle,
6802
+ options = [],
6803
+ onOptionSelect,
6804
+ showSearch = true
6805
+ }) {
6743
6806
  this.name = name;
6744
6807
  this.field = field;
6745
6808
  this.defaultValue = defaultValue;
6746
6809
  this.svgIcon = svgIcon;
6747
- this.onToggle = onToggle; // ✅ 新增:回调函数
6810
+ this.onToggle = onToggle;
6811
+ this.options = options; // 可选项目
6812
+ this.onOptionSelect = onOptionSelect; // 选项选择回调
6813
+ this.showSearch = showSearch; // 是否显示搜索框
6748
6814
 
6749
- // 控件容器
6815
+ // 控件状态
6750
6816
  this._container = null;
6751
6817
  this._button = null;
6752
- this.isActive = false; // 当前是否为激活状态(高亮/true)
6818
+ this._dropdown = null;
6819
+ this._searchInput = null;
6820
+ this._optionsList = null;
6821
+ this.isActive = false;
6822
+ this.isDropdownOpen = false;
6823
+ this.filteredOptions = [...options]; // 过滤后的选项
6824
+
6825
+ // 绑定事件处理函数
6826
+ this._boundHandleClickOutside = this._handleClickOutside.bind(this);
6753
6827
  }
6754
6828
 
6755
6829
  // Mapbox 要求的 onAdd 方法
6756
6830
  onAdd(map) {
6757
6831
  this.map = map;
6758
6832
 
6759
- // 创建外层容器 div
6833
+ // 创建外层容器
6760
6834
  this._container = document.createElement('div');
6761
- this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group';
6835
+ this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group custom-toggle-control';
6762
6836
 
6763
- // 创建 button 元素
6837
+ // 创建主按钮
6764
6838
  this._button = document.createElement('button');
6765
6839
  this._button.type = 'button';
6766
6840
  this._button.innerHTML = this.svgIcon;
6841
+ this._button.title = this.name;
6767
6842
 
6768
6843
  // 设置按钮样式
6769
- this._button.style.cursor = 'pointer';
6770
- this._button.style.border = 'none';
6771
- this._button.style.background = 'none';
6772
- this._button.style.padding = '0';
6773
- this._button.style.display = 'flex';
6774
- this._button.style.alignItems = 'center';
6775
- this._button.style.justifyContent = 'center';
6844
+ this._setupButtonStyles();
6776
6845
 
6777
6846
  // 初始化状态
6778
6847
  this.updateStatus();
6779
6848
 
6780
- // 绑定点击事件
6781
- this._button.addEventListener('click', () => {
6849
+ // 绑定事件
6850
+ this._button.addEventListener('click', (e) => {
6851
+ e.stopPropagation();
6782
6852
  this.toggle();
6783
6853
  });
6784
6854
 
6785
6855
  this._container.appendChild(this._button);
6786
6856
 
6857
+ // 创建下拉菜单(如果有选项)
6858
+ if (this.options.length > 0) {
6859
+ this._createDropdown();
6860
+ }
6861
+
6787
6862
  return this._container;
6788
6863
  }
6789
6864
 
6790
- // Mapbox 要求的 onRemove 方法
6791
- onRemove() {
6792
- if (this._container && this._container.parentNode) {
6793
- this._container.parentNode.removeChild(this._container);
6865
+ // 设置按钮样式
6866
+ _setupButtonStyles() {
6867
+ Object.assign(this._button.style, {
6868
+ cursor: 'pointer',
6869
+ border: 'none',
6870
+ background: 'none',
6871
+ padding: '8px',
6872
+ display: 'flex',
6873
+ alignItems: 'center',
6874
+ justifyContent: 'center',
6875
+ borderRadius: '4px',
6876
+ transition: 'all 0.2s ease'
6877
+ });
6878
+ }
6879
+
6880
+ // 创建下拉菜单
6881
+ _createDropdown() {
6882
+ // 创建下拉容器
6883
+ this._dropdown = document.createElement('div');
6884
+ this._dropdown.className = 'custom-toggle-dropdown';
6885
+ Object.assign(this._dropdown.style, {
6886
+ position: 'absolute',
6887
+ top: '100%',
6888
+ right: '0',
6889
+ backgroundColor: 'white',
6890
+ border: '1px solid #ccc',
6891
+ borderRadius: '4px',
6892
+ boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
6893
+ zIndex: '1000',
6894
+ minWidth: '200px',
6895
+ maxWidth: '300px',
6896
+ display: 'none',
6897
+ marginTop: '4px'
6898
+ });
6899
+
6900
+ // 创建搜索框(如果需要)
6901
+ if (this.showSearch && this.options.length > 5) {
6902
+ this._createSearchBox();
6794
6903
  }
6795
- this.map = null;
6796
- this._container = null;
6797
- this._button = null;
6904
+
6905
+ // 创建选项列表
6906
+ this._createOptionsList();
6907
+
6908
+ this._container.appendChild(this._dropdown);
6909
+ }
6910
+
6911
+ // 创建搜索框
6912
+ _createSearchBox() {
6913
+ const searchContainer = document.createElement('div');
6914
+ Object.assign(searchContainer.style, {
6915
+ padding: '8px',
6916
+ borderBottom: '1px solid #eee'
6917
+ });
6918
+
6919
+ this._searchInput = document.createElement('input');
6920
+ this._searchInput.type = 'text';
6921
+ this._searchInput.placeholder = '搜索...';
6922
+ this._searchInput.style.cssText = `
6923
+ width: 100%;
6924
+ padding: 6px 8px;
6925
+ border: 1px solid #ddd;
6926
+ border-radius: 4px;
6927
+ fontSize: 12px;
6928
+ outline: none;
6929
+ box-sizing: border-box;
6930
+ `;
6931
+
6932
+ // 绑定搜索事件
6933
+ this._searchInput.addEventListener('input', (e) => {
6934
+ this._filterOptions(e.target.value);
6935
+ });
6936
+
6937
+ // 阻止下拉菜单关闭
6938
+ this._searchInput.addEventListener('click', (e) => {
6939
+ e.stopPropagation();
6940
+ });
6941
+
6942
+ searchContainer.appendChild(this._searchInput);
6943
+ this._dropdown.appendChild(searchContainer);
6798
6944
  }
6799
6945
 
6800
- // 更新控件状态(根据当前 field 值)
6946
+ // 创建选项列表
6947
+ _createOptionsList() {
6948
+ this._optionsList = document.createElement('div');
6949
+ Object.assign(this._optionsList.style, {
6950
+ maxHeight: '200px',
6951
+ overflowY: 'auto',
6952
+ padding: '4px 0'
6953
+ });
6954
+
6955
+ this._renderOptionsList();
6956
+ this._dropdown.appendChild(this._optionsList);
6957
+ }
6958
+
6959
+ // 渲染选项列表
6960
+ _renderOptionsList() {
6961
+ if (!this._optionsList) return;
6962
+
6963
+ this._optionsList.innerHTML = '';
6964
+
6965
+ this.filteredOptions.forEach(option => {
6966
+ const optionItem = document.createElement('div');
6967
+ optionItem.className = 'custom-toggle-option';
6968
+ optionItem.dataset.id = option.id;
6969
+
6970
+ Object.assign(optionItem.style, {
6971
+ padding: '8px 12px',
6972
+ cursor: 'pointer',
6973
+ display: 'flex',
6974
+ alignItems: 'center',
6975
+ gap: '8px',
6976
+ fontSize: '13px',
6977
+ transition: 'background-color 0.2s'
6978
+ });
6979
+
6980
+ // 添加图标(如果有)
6981
+ if (option.icon) {
6982
+ const icon = document.createElement('span');
6983
+ icon.innerHTML = option.icon;
6984
+ icon.style.fontSize = '14px';
6985
+ optionItem.appendChild(icon);
6986
+ }
6987
+
6988
+ // 添加文本
6989
+ const text = document.createElement('span');
6990
+ text.textContent = option.name;
6991
+ optionItem.appendChild(text);
6992
+
6993
+ // 绑定点击事件
6994
+ optionItem.addEventListener('click', (e) => {
6995
+ e.stopPropagation();
6996
+ this._selectOption(option);
6997
+ });
6998
+
6999
+ // 鼠标悬停效果
7000
+ optionItem.addEventListener('mouseenter', () => {
7001
+ optionItem.style.backgroundColor = '#f5f5f5';
7002
+ });
7003
+
7004
+ optionItem.addEventListener('mouseleave', () => {
7005
+ optionItem.style.backgroundColor = 'transparent';
7006
+ });
7007
+
7008
+ this._optionsList.appendChild(optionItem);
7009
+ });
7010
+
7011
+ // 如果没有匹配的选项
7012
+ if (this.filteredOptions.length === 0) {
7013
+ const noResults = document.createElement('div');
7014
+ noResults.textContent = '无匹配项';
7015
+ Object.assign(noResults.style, {
7016
+ padding: '12px',
7017
+ textAlign: 'center',
7018
+ color: '#999',
7019
+ fontSize: '12px'
7020
+ });
7021
+ this._optionsList.appendChild(noResults);
7022
+ }
7023
+ }
7024
+
7025
+ // 过滤选项
7026
+ _filterOptions(searchTerm) {
7027
+ const term = searchTerm.toLowerCase().trim();
7028
+
7029
+ if (!term) {
7030
+ this.filteredOptions = [...this.options];
7031
+ } else {
7032
+ this.filteredOptions = this.options.filter(option =>
7033
+ option.name.toLowerCase().includes(term) ||
7034
+ (option.id && option.id.toString().toLowerCase().includes(term))
7035
+ );
7036
+ }
7037
+
7038
+ this._renderOptionsList();
7039
+ }
7040
+
7041
+ // 选择选项
7042
+ _selectOption(option) {
7043
+ // 调用选项选择回调
7044
+ if (this.onOptionSelect) {
7045
+ this.onOptionSelect(option);
7046
+ }
7047
+
7048
+ // 关闭下拉菜单
7049
+ this.closeDropdown();
7050
+ }
7051
+
7052
+ // 切换下拉菜单显示状态
7053
+ toggle() {
7054
+ if (this.isDropdownOpen) {
7055
+ this.closeDropdown();
7056
+ } else {
7057
+ this.openDropdown();
7058
+ }
7059
+ }
7060
+
7061
+ // 打开下拉菜单
7062
+ openDropdown() {
7063
+ if (!this._dropdown || this.options.length === 0) return;
7064
+
7065
+ this.isDropdownOpen = true;
7066
+ this._dropdown.style.display = 'block';
7067
+
7068
+ // 添加全局点击监听
7069
+ document.addEventListener('click', this._boundHandleClickOutside);
7070
+
7071
+ // 聚焦搜索框
7072
+ if (this._searchInput) {
7073
+ setTimeout(() => {
7074
+ this._searchInput.focus();
7075
+ this._searchInput.value = '';
7076
+ this._filterOptions('');
7077
+ }, 100);
7078
+ }
7079
+ }
7080
+
7081
+ // 关闭下拉菜单
7082
+ closeDropdown() {
7083
+ if (!this._dropdown) return;
7084
+
7085
+ this.isDropdownOpen = false;
7086
+ this._dropdown.style.display = 'none';
7087
+
7088
+ // 移除全局点击监听
7089
+ document.removeEventListener('click', this._boundHandleClickOutside);
7090
+ }
7091
+
7092
+ // 处理外部点击
7093
+ _handleClickOutside(event) {
7094
+ if (!this._container.contains(event.target)) {
7095
+ this.closeDropdown();
7096
+ }
7097
+ }
7098
+
7099
+ // 更新控件状态
6801
7100
  updateStatus() {
6802
- // 从 map.SourceMap 中获取当前 field 的值,如果不存在则使用 defaultValue
6803
7101
  let currentValue = this.defaultValue;
6804
7102
 
6805
7103
  if (
@@ -6810,21 +7108,22 @@ class CustomToggleControl {
6810
7108
  currentValue = this.map.SourceMap[this.field];
6811
7109
  }
6812
7110
 
6813
- this.isActive = currentValue; // true 表示激活,false 表示未激活
7111
+ this.isActive = currentValue;
6814
7112
 
6815
7113
  // 更新按钮样式
6816
7114
  if (this.isActive) {
6817
7115
  this._button.style.opacity = '1';
6818
7116
  this._button.style.filter = 'none';
7117
+ this._button.style.backgroundColor = '#e6f3ff';
6819
7118
  } else {
6820
- this._button.style.opacity = '0.4';
7119
+ this._button.style.opacity = '0.7';
6821
7120
  this._button.style.filter = 'grayscale(100%)';
7121
+ this._button.style.backgroundColor = 'transparent';
6822
7122
  }
6823
7123
  }
6824
7124
 
6825
- // 切换状态
6826
- toggle() {
6827
- // 获取当前值
7125
+ // 切换状态(保持原有功能)
7126
+ toggleState() {
6828
7127
  let currentValue = this.defaultValue;
6829
7128
 
6830
7129
  if (
@@ -6835,22 +7134,34 @@ class CustomToggleControl {
6835
7134
  currentValue = this.map.SourceMap[this.field];
6836
7135
  }
6837
7136
 
6838
- // 切换值
6839
7137
  const newValue = !currentValue;
6840
7138
 
6841
- // 更新到 map.SourceMap 中
6842
7139
  if (this.map && this.map.SourceMap) {
6843
7140
  this.map.SourceMap[this.field] = newValue;
6844
7141
  }
6845
7142
 
6846
- // 更新内部状态
6847
7143
  this.isActive = newValue;
6848
-
6849
- // 更新 UI 样式
6850
7144
  this.updateStatus();
6851
7145
 
6852
- // 调用回调函数(如果传入了 onToggle),并传入最新的值
6853
- this.onToggle?.(newValue); // 安全调用,仅当函数存在时才执行
7146
+ if (this.onToggle) {
7147
+ this.onToggle(newValue);
7148
+ }
7149
+ }
7150
+
7151
+ // Mapbox 要求的 onRemove 方法
7152
+ onRemove() {
7153
+ this.closeDropdown();
7154
+
7155
+ if (this._container && this._container.parentNode) {
7156
+ this._container.parentNode.removeChild(this._container);
7157
+ }
7158
+
7159
+ this.map = null;
7160
+ this._container = null;
7161
+ this._button = null;
7162
+ this._dropdown = null;
7163
+ this._searchInput = null;
7164
+ this._optionsList = null;
6854
7165
  }
6855
7166
  }
6856
7167