raintee-maputils 1.0.41 → 1.0.43

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
@@ -6501,7 +6501,7 @@ class RulerControl {
6501
6501
  this.removeDragEvents = null;
6502
6502
  if (!this.options.invisible) {
6503
6503
  this.button = controlButton({
6504
- title: 'Ruler',
6504
+ title: options.title || 'Ruler',
6505
6505
  icon: icons.ruler(),
6506
6506
  onClick: () => this.onControlButtonClick(),
6507
6507
  });
@@ -6738,6 +6738,579 @@ class RulerControl {
6738
6738
  }
6739
6739
  }
6740
6740
 
6741
+ /**
6742
+ * CustomSearchSelectControl - 紧凑型 Mapbox 控件
6743
+ * 优化UI布局,提高空间利用率
6744
+ */
6745
+ class CustomSearchSelectControl {
6746
+ /**
6747
+ * 构造函数
6748
+ * @param {Object} options 配置选项
6749
+ * @param {Array} options.list 选项列表
6750
+ * @param {Function} options.onSelect 选中回调函数
6751
+ * @param {string} options.placeholder 搜索框占位符
6752
+ * @param {string} options.title 弹窗标题
6753
+ * @param {boolean} options.showSearch 是否显示搜索框
6754
+ * @param {boolean} options.compactMode 是否启用紧凑模式
6755
+ */
6756
+ constructor(options = {}) {
6757
+ this.options = {
6758
+ list: [],
6759
+ onSelect: () => { },
6760
+ placeholder: '搜索...',
6761
+ title: '选择',
6762
+ showSearch: true,
6763
+ compactMode: true, // 新增:紧凑模式
6764
+ maxHeight: '60vh', // 新增:最大高度
6765
+ width: '60vw', // 新增:弹窗宽度
6766
+ ...options
6767
+ };
6768
+
6769
+ this._map = null;
6770
+ this._container = null;
6771
+ this._modal = null;
6772
+ this._overlay = null;
6773
+ this._searchInput = null;
6774
+ this._optionsList = null;
6775
+ this._filteredList = [...this.options.list];
6776
+ }
6777
+
6778
+ /**
6779
+ * 添加到地图
6780
+ * @param {Object} map Mapbox 地图实例
6781
+ * @returns {HTMLElement} 控件容器
6782
+ */
6783
+ onAdd(map) {
6784
+ this._map = map;
6785
+
6786
+ // 创建控件按钮
6787
+ this._container = document.createElement('div');
6788
+ this._container.className = 'mapboxgl-ctrl vector-layer-ctrl';
6789
+ this._container.style.cssText = `
6790
+ position: relative;
6791
+ `;
6792
+ this._container.innerHTML = `
6793
+ <button class="vector-layer-btn" title="${this.options.title}">
6794
+ <svg viewBox="0 0 24 24" width="18" height="18">
6795
+ <path d="M9,2V7.5L12,10.5L15,7.5V2H9ZM2,9H7.5L10.5,12L7.5,15H2V9ZM22,9H16.5L13.5,12L16.5,15H22V9ZM9,22V16.5L12,13.5L15,16.5V22H9Z"
6796
+ fill="currentColor"/>
6797
+ </svg>
6798
+ </button>
6799
+ `;
6800
+
6801
+ // 添加点击事件
6802
+ const button = this._container.querySelector('.vector-layer-btn');
6803
+ button.addEventListener('click', () => this.openModal());
6804
+
6805
+ return this._container;
6806
+ }
6807
+
6808
+ /**
6809
+ * 从地图移除
6810
+ */
6811
+ onRemove() {
6812
+ this.closeModal();
6813
+ if (this._container) {
6814
+ this._container.parentNode.removeChild(this._container);
6815
+ }
6816
+ this._map = undefined;
6817
+ }
6818
+
6819
+ /**
6820
+ * 打开弹窗
6821
+ */
6822
+ openModal() {
6823
+ if (this._modal) return;
6824
+
6825
+ // 创建遮罩层
6826
+ this._overlay = document.createElement('div');
6827
+ this._overlay.className = 'vector-layer-overlay';
6828
+ this._overlay.style.cssText = `
6829
+ position: fixed;
6830
+ top: 0;
6831
+ left: 0;
6832
+ width: 100%;
6833
+ height: 100%;
6834
+ background: rgba(0, 0, 0, 0.5);
6835
+ z-index: 9999;
6836
+ display: flex;
6837
+ justify-content: center;
6838
+ backdrop-filter: blur(2px);
6839
+ `;
6840
+ this._overlay.addEventListener('click', (e) => {
6841
+ if (e.target === this._overlay) this.closeModal();
6842
+ });
6843
+
6844
+ // 创建弹窗
6845
+ this._modal = document.createElement('div');
6846
+ this._modal.className = 'vector-layer-modal';
6847
+ this._modal.style.cssText = `
6848
+ background: white;
6849
+ border-radius: 8px;
6850
+ padding: ${this.options.compactMode ? '16px' : '20px'};
6851
+ width: ${this.options.width};
6852
+ max-width: 90vw;
6853
+ max-height: ${this.options.maxHeight};
6854
+ display: flex;
6855
+ flex-direction: column;
6856
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
6857
+ animation: vector-layer-modal-appear 0.2s ease-out;
6858
+ border: 1px solid #e0e0e0;
6859
+ `;
6860
+
6861
+ // 弹窗标题
6862
+ const title = document.createElement('h3');
6863
+ title.className = 'vector-layer-title';
6864
+ title.textContent = this.options.title;
6865
+ title.style.cssText = `
6866
+ margin: 0 0 ${this.options.compactMode ? '12px' : '16px'} 0;
6867
+ font-size: ${this.options.compactMode ? '15px' : '16px'};
6868
+ font-weight: 600;
6869
+ color: #2c3e50;
6870
+ line-height: 1.3;
6871
+ `;
6872
+ this._modal.appendChild(title);
6873
+
6874
+ // 搜索框
6875
+ if (this.options.showSearch) {
6876
+ const searchContainer = document.createElement('div');
6877
+ searchContainer.className = 'vector-layer-search';
6878
+ searchContainer.style.cssText = `
6879
+ margin-bottom: ${this.options.compactMode ? '12px' : '16px'};
6880
+ position: relative;
6881
+ `;
6882
+
6883
+ this._searchInput = document.createElement('input');
6884
+ this._searchInput.type = 'text';
6885
+ this._searchInput.placeholder = this.options.placeholder;
6886
+ this._searchInput.className = 'vector-layer-search-input';
6887
+ this._searchInput.style.cssText = `
6888
+ width: 100%;
6889
+ padding: ${this.options.compactMode ? '8px 12px 8px 36px' : '10px 16px 10px 40px'};
6890
+ border: 1px solid #dcdfe6;
6891
+ border-radius: 6px;
6892
+ font-size: 13px;
6893
+ outline: none;
6894
+ transition: all 0.2s;
6895
+ box-sizing: border-box;
6896
+ background-color: #f8f9fa;
6897
+ `;
6898
+ this._searchInput.addEventListener('input', () => this.filterOptions());
6899
+ this._searchInput.addEventListener('keydown', (e) => {
6900
+ if (e.key === 'Escape') this.closeModal();
6901
+ });
6902
+
6903
+ // 搜索图标
6904
+ const searchIcon = document.createElement('div');
6905
+ searchIcon.innerHTML = `
6906
+ <svg viewBox="0 0 24 24" width="16" height="16"
6907
+ style="position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: #888;">
6908
+ <path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
6909
+ fill="currentColor"/>
6910
+ </svg>
6911
+ `;
6912
+
6913
+ searchContainer.appendChild(searchIcon);
6914
+ searchContainer.appendChild(this._searchInput);
6915
+ this._modal.appendChild(searchContainer);
6916
+ }
6917
+
6918
+ // 选项列表容器
6919
+ const listContainer = document.createElement('div');
6920
+ listContainer.className = 'vector-layer-list-container';
6921
+ listContainer.style.cssText = `
6922
+ flex: 1;
6923
+ overflow: hidden;
6924
+ min-height: ${this.options.compactMode ? '120px' : '160px'};
6925
+ border-radius: 6px;
6926
+ border: 1px solid #e0e0e0;
6927
+ `;
6928
+
6929
+ this._optionsList = document.createElement('div');
6930
+ this._optionsList.className = 'vector-layer-options';
6931
+ this._optionsList.style.cssText = `
6932
+ max-height: calc(${this.options.maxHeight} - ${this.options.compactMode ? '140px' : '180px'});
6933
+ overflow-y: auto;
6934
+ scroll-behavior: smooth;
6935
+ `;
6936
+
6937
+ // 填充选项
6938
+ this.renderOptions();
6939
+
6940
+ listContainer.appendChild(this._optionsList);
6941
+ this._modal.appendChild(listContainer);
6942
+
6943
+ // 操作按钮
6944
+ const buttonContainer = document.createElement('div');
6945
+ buttonContainer.className = 'vector-layer-actions';
6946
+ buttonContainer.style.cssText = `
6947
+ display: flex;
6948
+ justify-content: flex-end;
6949
+ gap: 8px;
6950
+ margin-top: ${this.options.compactMode ? '12px' : '16px'};
6951
+ padding-top: ${this.options.compactMode ? '12px' : '16px'};
6952
+ border-top: 1px solid #f0f0f0;
6953
+ `;
6954
+
6955
+ const cancelBtn = document.createElement('button');
6956
+ cancelBtn.textContent = '取消';
6957
+ cancelBtn.className = 'vector-layer-cancel';
6958
+ cancelBtn.style.cssText = `
6959
+ padding: 6px 16px;
6960
+ border: 1px solid #dcdfe6;
6961
+ border-radius: 4px;
6962
+ background: white;
6963
+ color: #606266;
6964
+ cursor: pointer;
6965
+ font-size: 13px;
6966
+ transition: all 0.2s;
6967
+ font-weight: 500;
6968
+ `;
6969
+ cancelBtn.addEventListener('click', () => this.closeModal());
6970
+ cancelBtn.addEventListener('mouseenter', () => {
6971
+ cancelBtn.style.backgroundColor = '#f5f7fa';
6972
+ cancelBtn.style.borderColor = '#c0c4cc';
6973
+ });
6974
+ cancelBtn.addEventListener('mouseleave', () => {
6975
+ cancelBtn.style.backgroundColor = 'white';
6976
+ cancelBtn.style.borderColor = '#dcdfe6';
6977
+ });
6978
+
6979
+ buttonContainer.appendChild(cancelBtn);
6980
+ this._modal.appendChild(buttonContainer);
6981
+
6982
+ this._overlay.appendChild(this._modal);
6983
+ document.body.appendChild(this._overlay);
6984
+
6985
+ // 阻止地图事件冒泡
6986
+ this._modal.addEventListener('click', (e) => e.stopPropagation());
6987
+ this._modal.addEventListener('mousedown', (e) => e.stopPropagation());
6988
+ this._modal.addEventListener('mouseup', (e) => e.stopPropagation());
6989
+
6990
+ // 聚焦搜索框
6991
+ if (this._searchInput) {
6992
+ setTimeout(() => this._searchInput.focus(), 50);
6993
+ }
6994
+
6995
+ // 添加动画样式
6996
+ this.addStyles();
6997
+ }
6998
+
6999
+ /**
7000
+ * 渲染选项列表
7001
+ */
7002
+ renderOptions() {
7003
+ this._optionsList.innerHTML = '';
7004
+
7005
+ if (this._filteredList.length === 0) {
7006
+ const emptyItem = document.createElement('div');
7007
+ emptyItem.className = 'vector-layer-option empty';
7008
+ emptyItem.textContent = '无匹配结果';
7009
+ emptyItem.style.cssText = `
7010
+ padding: ${this.options.compactMode ? '20px 16px' : '24px 16px'};
7011
+ text-align: center;
7012
+ color: #999;
7013
+ font-size: 13px;
7014
+ font-style: italic;
7015
+ `;
7016
+ this._optionsList.appendChild(emptyItem);
7017
+ return;
7018
+ }
7019
+
7020
+ this._filteredList.forEach((item, index) => {
7021
+ const option = document.createElement('div');
7022
+ option.className = 'vector-layer-option';
7023
+ option.dataset.value = item.value || item.label;
7024
+ option.dataset.index = index;
7025
+ option.style.cssText = `
7026
+ padding: ${this.options.compactMode ? '8px 12px' : '10px 16px'};
7027
+ cursor: pointer;
7028
+ border-bottom: 1px solid #f5f5f5;
7029
+ font-size: 13px;
7030
+ transition: all 0.15s;
7031
+ display: flex;
7032
+ align-items: center;
7033
+ gap: 8px;
7034
+ min-height: ${this.options.compactMode ? '36px' : '40px'};
7035
+ box-sizing: border-box;
7036
+ `;
7037
+
7038
+ option.innerHTML = `
7039
+ ${item.icon ? `<span class="option-icon" style="flex-shrink: 0; width: 16px; height: 16px;">${item.icon}</span>` : ''}
7040
+ <span class="option-label" style="flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${item.label}</span>
7041
+ ${item.description ? `<span class="option-desc" style="font-size: 12px; color: #888; flex-shrink: 0;">${item.description}</span>` : ''}
7042
+ `;
7043
+
7044
+ option.addEventListener('click', () => this.selectOption(item));
7045
+ option.addEventListener('mouseenter', () => {
7046
+ option.style.backgroundColor = '#f0f7ff';
7047
+ option.style.borderLeftColor = '#1890ff';
7048
+ });
7049
+ option.addEventListener('mouseleave', () => {
7050
+ option.style.backgroundColor = '';
7051
+ option.style.borderLeftColor = '';
7052
+ });
7053
+
7054
+ this._optionsList.appendChild(option);
7055
+ });
7056
+
7057
+ // 移除最后一个选项的下边框
7058
+ const lastOption = this._optionsList.lastChild;
7059
+ if (lastOption && !lastOption.classList.contains('empty')) {
7060
+ lastOption.style.borderBottom = 'none';
7061
+ }
7062
+ }
7063
+
7064
+ /**
7065
+ * 筛选选项
7066
+ */
7067
+ filterOptions() {
7068
+ const searchText = this._searchInput ? this._searchInput.value.toLowerCase() : '';
7069
+
7070
+ if (!searchText) {
7071
+ this._filteredList = [...this.options.list];
7072
+ } else {
7073
+ this._filteredList = this.options.list.filter(item =>
7074
+ item.label.toLowerCase().includes(searchText) ||
7075
+ (item.value && item.value.toLowerCase().includes(searchText)) ||
7076
+ (item.description && item.description.toLowerCase().includes(searchText))
7077
+ );
7078
+ }
7079
+
7080
+ this.renderOptions();
7081
+ }
7082
+
7083
+ /**
7084
+ * 选中选项
7085
+ * @param {Object} item 选中的选项
7086
+ */
7087
+ selectOption(item) {
7088
+ try {
7089
+ // 执行回调
7090
+ this.options.onSelect(this._map, item);
7091
+
7092
+ // 添加选中动画
7093
+ const option = this._optionsList.querySelector(`[data-value="${item.value || item.label}"]`);
7094
+ if (option) {
7095
+ const originalBg = option.style.backgroundColor;
7096
+ const originalColor = option.style.color;
7097
+ option.style.backgroundColor = '#1890ff';
7098
+ option.style.color = 'white';
7099
+ setTimeout(() => {
7100
+ option.style.backgroundColor = originalBg;
7101
+ option.style.color = originalColor;
7102
+ }, 150);
7103
+ setTimeout(() => {
7104
+ this.closeModal();
7105
+ }, 300);
7106
+ } else {
7107
+ this.closeModal();
7108
+ }
7109
+ } catch (error) {
7110
+ console.error('选中回调执行失败:', error);
7111
+ this.closeModal();
7112
+ }
7113
+ }
7114
+
7115
+ /**
7116
+ * 关闭弹窗
7117
+ */
7118
+ closeModal() {
7119
+ if (this._overlay) {
7120
+ this._overlay.style.opacity = '0';
7121
+ this._overlay.style.transition = 'opacity 0.15s ease-out';
7122
+
7123
+ setTimeout(() => {
7124
+ if (this._overlay && this._overlay.parentNode) {
7125
+ document.body.removeChild(this._overlay);
7126
+ }
7127
+ this._overlay = null;
7128
+ this._modal = null;
7129
+ this._searchInput = null;
7130
+ this._optionsList = null;
7131
+ }, 150);
7132
+ }
7133
+ }
7134
+
7135
+ /**
7136
+ * 更新选项列表
7137
+ * @param {Array} newList 新的选项列表
7138
+ */
7139
+ updateList(newList) {
7140
+ this.options.list = newList;
7141
+ this._filteredList = [...newList];
7142
+ if (this._modal) {
7143
+ this.renderOptions();
7144
+ }
7145
+ }
7146
+
7147
+ /**
7148
+ * 添加CSS样式
7149
+ */
7150
+ addStyles() {
7151
+ if (document.querySelector('#vector-layer-styles')) return;
7152
+
7153
+ const style = document.createElement('style');
7154
+ style.id = 'vector-layer-styles';
7155
+ style.textContent = `
7156
+ @keyframes vector-layer-modal-appear {
7157
+ from {
7158
+ opacity: 0;
7159
+ transform: translateY(-8px) scale(0.98);
7160
+ }
7161
+ to {
7162
+ opacity: 1;
7163
+ transform: translateY(0) scale(1);
7164
+ }
7165
+ }
7166
+
7167
+ .vector-layer-ctrl button {
7168
+ background: white;
7169
+ border: 1px solid #d9d9d9;
7170
+ border-radius: 4px;
7171
+ width: 28px;
7172
+ height: 28px;
7173
+ display: flex;
7174
+ align-items: center;
7175
+ justify-content: center;
7176
+ cursor: pointer;
7177
+ color: #555;
7178
+ transition: all 0.2s;
7179
+ padding: 0;
7180
+ margin: 0;
7181
+ }
7182
+
7183
+ .vector-layer-ctrl button:hover {
7184
+ background: #f5f5f5;
7185
+ border-color: #40a9ff;
7186
+ color: #40a9ff;
7187
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
7188
+ }
7189
+
7190
+ .vector-layer-search-input:focus {
7191
+ border-color: #40a9ff;
7192
+ box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
7193
+ background-color: white;
7194
+ }
7195
+
7196
+ .vector-layer-option {
7197
+ position: relative;
7198
+ border-left: 3px solid transparent;
7199
+ }
7200
+
7201
+ .vector-layer-option:hover {
7202
+ border-left-color: #1890ff;
7203
+ }
7204
+
7205
+ .vector-layer-option:active {
7206
+ background-color: #e6f7ff !important;
7207
+ }
7208
+
7209
+ .vector-layer-options::-webkit-scrollbar {
7210
+ width: 4px;
7211
+ }
7212
+
7213
+ .vector-layer-options::-webkit-scrollbar-track {
7214
+ background: #f5f5f5;
7215
+ border-radius: 2px;
7216
+ }
7217
+
7218
+ .vector-layer-options::-webkit-scrollbar-thumb {
7219
+ background: #bfbfbf;
7220
+ border-radius: 2px;
7221
+ }
7222
+
7223
+ .vector-layer-options::-webkit-scrollbar-thumb:hover {
7224
+ background: #8c8c8c;
7225
+ }
7226
+
7227
+ .vector-layer-modal {
7228
+ overflow: hidden;
7229
+ }
7230
+
7231
+ /* 紧凑模式下的额外样式 */
7232
+ .vector-layer-modal.compact {
7233
+ padding: 12px;
7234
+ }
7235
+
7236
+ .vector-layer-modal.compact .vector-layer-title {
7237
+ font-size: 14px;
7238
+ margin-bottom: 10px;
7239
+ }
7240
+
7241
+ .vector-layer-modal.compact .vector-layer-search-input {
7242
+ padding: 6px 10px 6px 32px;
7243
+ font-size: 12px;
7244
+ }
7245
+
7246
+ .vector-layer-modal.compact .vector-layer-option {
7247
+ padding: 6px 10px;
7248
+ font-size: 12px;
7249
+ min-height: 32px;
7250
+ }
7251
+
7252
+ /* 暗色模式支持 */
7253
+ @media (prefers-color-scheme: dark) {
7254
+ .vector-layer-modal {
7255
+ background: #2d2d2d;
7256
+ border-color: #404040;
7257
+ color: #e0e0e0;
7258
+ }
7259
+
7260
+ .vector-layer-title {
7261
+ color: #e0e0e0;
7262
+ }
7263
+
7264
+ .vector-layer-search-input {
7265
+ background-color: #3a3a3a;
7266
+ border-color: #555;
7267
+ color: #e0e0e0;
7268
+ }
7269
+
7270
+ .vector-layer-search-input:focus {
7271
+ border-color: #1890ff;
7272
+ }
7273
+
7274
+ .vector-layer-list-container {
7275
+ border-color: #404040;
7276
+ }
7277
+
7278
+ .vector-layer-option {
7279
+ border-bottom-color: #404040;
7280
+ }
7281
+
7282
+ .vector-layer-option:hover {
7283
+ background-color: #3a3a3a;
7284
+ }
7285
+
7286
+ .option-desc {
7287
+ color: #aaa;
7288
+ }
7289
+
7290
+ .vector-layer-actions {
7291
+ border-top-color: #404040;
7292
+ }
7293
+
7294
+ .vector-layer-cancel {
7295
+ background: #3a3a3a;
7296
+ border-color: #555;
7297
+ color: #e0e0e0;
7298
+ }
7299
+
7300
+ .vector-layer-options::-webkit-scrollbar-track {
7301
+ background: #3a3a3a;
7302
+ }
7303
+
7304
+ .vector-layer-options::-webkit-scrollbar-thumb {
7305
+ background: #666;
7306
+ }
7307
+ }
7308
+ `;
7309
+
7310
+ document.head.appendChild(style);
7311
+ }
7312
+ }
7313
+
6741
7314
  class CustomOptionsControl {
6742
7315
  constructor(args) {
6743
7316
  const { title, options, onConfirm, icon } = args;
@@ -7973,5 +8546,5 @@ const useDrawCache = () => {
7973
8546
  }
7974
8547
  };
7975
8548
 
7976
- export { CustomOptionsControl, CustomToggleControl, DrawCacheFeatureManager, RainteeConstants, RainteeGISUtil, RainteeSourceMapTool, RasterLayerControl, RulerControl, TerrainToggleControl, useDrawCache };
8549
+ export { CustomOptionsControl, CustomSearchSelectControl, CustomToggleControl, DrawCacheFeatureManager, RainteeConstants, RainteeGISUtil, RainteeSourceMapTool, RasterLayerControl, RulerControl, TerrainToggleControl, useDrawCache };
7977
8550
  //# sourceMappingURL=index.js.map