@smartnet360/svelte-components 0.0.74 → 0.0.76

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.
@@ -31,6 +31,10 @@
31
31
  className?: string;
32
32
  /** Child content */
33
33
  children?: import('svelte').Snippet;
34
+ /** Optional offset from map edge (e.g., '12px') */
35
+ edgeOffset?: string;
36
+ /** Width of the map control (e.g., '360px') */
37
+ controlWidth?: string;
34
38
  }
35
39
 
36
40
  let {
@@ -41,9 +45,12 @@
41
45
  collapsible = true,
42
46
  initiallyCollapsed = true,
43
47
  className = '',
44
- children
48
+ children,
49
+ edgeOffset = '12px',
50
+ controlWidth = '420px'
45
51
  }: Props = $props();
46
52
 
53
+
47
54
  const mapStore = tryUseMapbox();
48
55
 
49
56
  if (!mapStore) {
@@ -112,6 +119,8 @@
112
119
  <div
113
120
  bind:this={controlElement}
114
121
  class="mapboxgl-ctrl mapboxgl-ctrl-group map-control-container {className}"
122
+ style="--edge-offset: {edgeOffset}; --map-control-width: {controlWidth};"
123
+ data-edge-offset={edgeOffset}
115
124
  >
116
125
  {#if title || icon}
117
126
  <div class="map-control-header" title={title}>
@@ -154,10 +163,11 @@
154
163
  <style>
155
164
  .map-control-container {
156
165
  background: white;
157
- border-radius: 4px;
158
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
166
+ border-radius: 0.6rem; /* modern, slightly larger radius */
167
+ box-shadow: 0 6px 18px rgba(14, 30, 37, 0.08);
159
168
  overflow: hidden;
160
- max-width: 400px;
169
+ max-width: var(--map-control-width, 420px);
170
+ margin: var(--edge-offset, 12px); /* this ensures a default offset from the map edge */
161
171
  font-family: system-ui, -apple-system, sans-serif;
162
172
  }
163
173
 
@@ -165,7 +175,7 @@
165
175
  display: flex;
166
176
  align-items: center;
167
177
  justify-content: space-between;
168
- padding: 8px 12px;
178
+ padding: 10px 14px;
169
179
  background: #f8f9fa;
170
180
  border-bottom: 1px solid #dee2e6;
171
181
  font-weight: 600;
@@ -205,7 +215,7 @@
205
215
 
206
216
  .map-control-content {
207
217
  padding: 12px;
208
- max-height: 400px;
218
+ max-height: var(--map-control-max-height, 420px);
209
219
  overflow-y: auto;
210
220
  font-size: 13px;
211
221
  }
@@ -15,6 +15,10 @@ interface Props {
15
15
  className?: string;
16
16
  /** Child content */
17
17
  children?: import('svelte').Snippet;
18
+ /** Optional offset from map edge (e.g., '12px') */
19
+ edgeOffset?: string;
20
+ /** Width of the map control (e.g., '360px') */
21
+ controlWidth?: string;
18
22
  }
19
23
  declare const MapControl: import("svelte").Component<Props, {}, "">;
20
24
  type MapControl = ReturnType<typeof MapControl>;
@@ -22,321 +22,255 @@
22
22
  }
23
23
  </script>
24
24
 
25
- <div class="settings-panel">
25
+ <div class="card border-0 shadow-sm rounded-2">
26
+ <div class="card-body bg-light p-3">
27
+ <div class="row align-items-center g-2 mb-3">
28
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Base Radius</div>
29
+ <div class="col-3 text-end">
30
+ <span class="badge bg-white text-muted border">{store.baseRadius}m</span>
31
+ </div>
32
+ <div class="col-5">
33
+ <input
34
+ id="cell-radius-slider"
35
+ type="range"
36
+ class="form-range w-100"
37
+ min="100"
38
+ max="2000"
39
+ step="50"
40
+ bind:value={store.baseRadius}
41
+ />
42
+ </div>
43
+ </div>
26
44
 
27
- <!-- Base radius slider -->
28
- <div class="control-row">
29
- <label for="cell-radius-slider" class="control-label">
30
- Base Radius: <strong>{store.baseRadius}m</strong>
31
- </label>
32
- <input
33
- id="cell-radius-slider"
34
- type="range"
35
- class="form-range"
36
- min="100"
37
- max="2000"
38
- step="50"
39
- bind:value={store.baseRadius}
40
- />
41
- </div>
45
+ <div class="row align-items-center g-2 mb-3">
46
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Line Width</div>
47
+ <div class="col-3 text-end">
48
+ <span class="badge bg-white text-muted border">{store.lineWidth}px</span>
49
+ </div>
50
+ <div class="col-5">
51
+ <input
52
+ id="cell-line-width-slider"
53
+ type="range"
54
+ class="form-range w-100"
55
+ min="1"
56
+ max="5"
57
+ step="0.5"
58
+ bind:value={store.lineWidth}
59
+ />
60
+ </div>
61
+ </div>
42
62
 
43
- <!-- Line width slider -->
44
- <div class="control-row">
45
- <label for="cell-line-width-slider" class="control-label">
46
- Line Width: <strong>{store.lineWidth}px</strong>
47
- </label>
48
- <input
49
- id="cell-line-width-slider"
50
- type="range"
51
- class="form-range"
52
- min="1"
53
- max="5"
54
- step="0.5"
55
- bind:value={store.lineWidth}
56
- />
57
- </div>
63
+ <div class="row align-items-center g-2 mb-3">
64
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Fill Opacity</div>
65
+ <div class="col-3 text-end">
66
+ <span class="badge bg-white text-muted border">{Math.round(store.fillOpacity * 100)}%</span>
67
+ </div>
68
+ <div class="col-5">
69
+ <input
70
+ id="cell-opacity-slider"
71
+ type="range"
72
+ class="form-range w-100"
73
+ min="0"
74
+ max="1"
75
+ step="0.1"
76
+ bind:value={store.fillOpacity}
77
+ />
78
+ </div>
79
+ </div>
58
80
 
59
- <!-- Fill opacity slider -->
60
- <div class="control-row">
61
- <label for="cell-opacity-slider" class="control-label">
62
- Fill Opacity: <strong>{Math.round(store.fillOpacity * 100)}%</strong>
63
- </label>
64
- <input
65
- id="cell-opacity-slider"
66
- type="range"
67
- class="form-range"
68
- min="0"
69
- max="1"
70
- step="0.1"
71
- bind:value={store.fillOpacity}
72
- />
73
- </div>
74
-
75
- <!-- Cell Labels Section -->
76
- <hr class="section-divider" />
81
+ <div class="border-top my-3"></div>
77
82
 
78
-
79
- <!-- Show labels toggle -->
80
- <div class="control-row">
81
- <label for="cell-labels-toggle" class="control-label">
82
- Show Cell Labels
83
- </label>
84
- <div class="form-check form-switch">
85
- <input
86
- id="cell-labels-toggle"
87
- type="checkbox"
88
- class="form-check-input"
89
- role="switch"
90
- checked={store.showLabels}
91
- onchange={(e) => store.setShowLabels(e.currentTarget.checked)}
92
- />
93
- </div>
94
- </div>
95
-
96
- {#if store.showLabels}
97
- <!-- 4G/5G Primary Label Field -->
98
- <div class="control-row">
99
- <label for="label-primary-4g5g" class="control-label">
100
- 4G/5G Primary
101
- </label>
102
- <select
103
- id="label-primary-4g5g"
104
- class="form-select form-select-sm"
105
- value={store.primaryLabelField4G5G}
106
- onchange={(e) => store.setPrimaryLabelField4G5G(e.currentTarget.value as keyof Cell)}
107
- >
108
- {#each labelFieldOptions4G5G as field}
109
- <option value={field}>{getFieldLabel(field)}</option>
110
- {/each}
111
- </select>
112
- </div>
113
-
114
- <!-- 4G/5G Secondary Label Field -->
115
- <div class="control-row">
116
- <label for="label-secondary-4g5g" class="control-label">
117
- 4G/5G Secondary
118
- </label>
119
- <select
120
- id="label-secondary-4g5g"
121
- class="form-select form-select-sm"
122
- value={store.secondaryLabelField4G5G}
123
- onchange={(e) => store.setSecondaryLabelField4G5G(e.currentTarget.value as keyof Cell | 'none')}
124
- >
125
- <option value="none">None</option>
126
- {#each labelFieldOptions4G5G as field}
127
- <option value={field}>{getFieldLabel(field)}</option>
128
- {/each}
129
- </select>
130
- </div>
131
-
132
- <!-- 2G Primary Label Field -->
133
- <div class="control-row">
134
- <label for="label-primary-2g" class="control-label">
135
- 2G Primary
136
- </label>
137
- <select
138
- id="label-primary-2g"
139
- class="form-select form-select-sm"
140
- value={store.primaryLabelField2G}
141
- onchange={(e) => store.setPrimaryLabelField2G(e.currentTarget.value as keyof Cell)}
142
- >
143
- {#each labelFieldOptions2G as field}
144
- <option value={field}>{getFieldLabel(field)}</option>
145
- {/each}
146
- </select>
147
- </div>
148
-
149
- <!-- 2G Secondary Label Field -->
150
- <div class="control-row">
151
- <label for="label-secondary-2g" class="control-label">
152
- 2G Secondary
153
- </label>
154
- <select
155
- id="label-secondary-2g"
156
- class="form-select form-select-sm"
157
- value={store.secondaryLabelField2G}
158
- onchange={(e) => store.setSecondaryLabelField2G(e.currentTarget.value as keyof Cell | 'none')}
159
- >
160
- <option value="none">None</option>
161
- {#each labelFieldOptions2G as field}
162
- <option value={field}>{getFieldLabel(field)}</option>
163
- {/each}
164
- </select>
165
- </div>
166
-
167
- <!-- Label Size -->
168
- <div class="control-row">
169
- <label for="label-size-slider" class="control-label">
170
- Label Size: <strong>{store.labelSize}px</strong>
171
- </label>
172
- <input
173
- id="label-size-slider"
174
- type="range"
175
- class="form-range"
176
- min="8"
177
- max="20"
178
- step="1"
179
- value={store.labelSize}
180
- oninput={(e) => store.setLabelSize(Number(e.currentTarget.value))}
181
- />
182
- </div>
183
-
184
- <!-- Label Color -->
185
- <div class="control-row">
186
- <label for="label-color-picker" class="control-label">
187
- Label Color
188
- </label>
189
- <input
190
- id="label-color-picker"
191
- type="color"
192
- class="form-control form-control-color"
193
- value={store.labelColor}
194
- oninput={(e) => store.setLabelColor(e.currentTarget.value)}
195
- />
196
- </div>
197
-
198
- <!-- Label Offset -->
199
- <div class="control-row">
200
- <label for="label-offset-slider" class="control-label">
201
- Label Offset: <strong>{store.labelOffset}%</strong>
202
- </label>
203
- <input
204
- id="label-offset-slider"
205
- type="range"
206
- class="form-range"
207
- min="100"
208
- max="1000"
209
- step="50"
210
- value={store.labelOffset}
211
- oninput={(e) => store.setLabelOffset(Number(e.currentTarget.value))}
212
- />
213
- </div>
214
-
215
- <!-- Label Halo Color -->
216
- <div class="control-row">
217
- <label for="label-halo-color-picker" class="control-label">
218
- Halo Color
219
- </label>
220
- <input
221
- id="label-halo-color-picker"
222
- type="color"
223
- class="form-control form-control-color"
224
- value={store.labelHaloColor}
225
- oninput={(e) => store.setLabelHaloColor(e.currentTarget.value)}
226
- />
227
- </div>
228
-
229
- <!-- Label Halo Width -->
230
- <div class="control-row">
231
- <label for="label-halo-width-slider" class="control-label">
232
- Halo Width: <strong>{store.labelHaloWidth}px</strong>
233
- </label>
234
- <input
235
- id="label-halo-width-slider"
236
- type="range"
237
- class="form-range"
238
- min="0"
239
- max="3"
240
- step="0.5"
241
- value={store.labelHaloWidth}
242
- oninput={(e) => store.setLabelHaloWidth(Number(e.currentTarget.value))}
243
- />
83
+ <div class="row align-items-center g-2 mb-3">
84
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Show Cell Labels</div>
85
+ <div class="col-3"></div>
86
+ <div class="col-5">
87
+ <div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
88
+ <input
89
+ id="cell-labels-toggle"
90
+ type="checkbox"
91
+ class="form-check-input"
92
+ role="switch"
93
+ checked={store.showLabels}
94
+ onchange={(e) => store.setShowLabels(e.currentTarget.checked)}
95
+ />
96
+ </div>
97
+ </div>
244
98
  </div>
245
-
246
- <!-- Min Label Zoom -->
247
- <div class="control-row">
248
- <label for="min-label-zoom-slider" class="control-label">
249
- Min Zoom: <strong>{store.minLabelZoom}</strong>
250
- </label>
251
- <input
252
- id="min-label-zoom-slider"
253
- type="range"
254
- class="form-range"
255
- min="10"
256
- max="18"
257
- step="1"
258
- value={store.minLabelZoom}
259
- oninput={(e) => store.setMinLabelZoom(Number(e.currentTarget.value))}
260
- />
261
- </div>
262
-
263
- <!-- Azimuth Tolerance -->
264
- <!-- <div class="control-row">
265
- <label for="azimuth-tolerance-slider" class="control-label">
266
- Azimuth Tolerance: <strong>±{store.azimuthTolerance}°</strong>
267
- </label>
268
- <input
269
- id="azimuth-tolerance-slider"
270
- type="range"
271
- class="form-range"
272
- min="1"
273
- max="45"
274
- step="1"
275
- value={store.azimuthTolerance}
276
- oninput={(e) => store.setAzimuthTolerance(Number(e.currentTarget.value))}
277
- />
278
- </div> -->
279
- {/if}
280
- </div>
281
99
 
282
- <style>
283
- .settings-panel {
284
- width: 100%;
285
- padding: 0.75rem;
286
- border: 1px solid rgba(0, 0, 0, 0.1);
287
- border-radius: 0.375rem;
288
- background-color: rgba(255, 255, 255, 0.02);
289
- }
290
-
291
- .section-divider {
292
- margin: 1rem 0;
293
- opacity: 0.3;
294
- }
100
+ {#if store.showLabels}
101
+ <div class="row align-items-center g-2 mb-3">
102
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">4G/5G Primary</div>
103
+ <div class="col-3"></div>
104
+ <div class="col-5">
105
+ <select
106
+ id="label-primary-4g5g"
107
+ class="form-select form-select-sm w-100"
108
+ value={store.primaryLabelField4G5G}
109
+ onchange={(e) => store.setPrimaryLabelField4G5G(e.currentTarget.value as keyof Cell)}
110
+ >
111
+ {#each labelFieldOptions4G5G as field}
112
+ <option value={field}>{getFieldLabel(field)}</option>
113
+ {/each}
114
+ </select>
115
+ </div>
116
+ </div>
295
117
 
296
- .control-row {
297
- display: flex;
298
- align-items: center;
299
- justify-content: space-between;
300
- gap: 0.75rem;
301
- margin-bottom: 0.875rem;
302
- }
118
+ <div class="row align-items-center g-2 mb-3">
119
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">4G/5G Secondary</div>
120
+ <div class="col-3"></div>
121
+ <div class="col-5">
122
+ <select
123
+ id="label-secondary-4g5g"
124
+ class="form-select form-select-sm w-100"
125
+ value={store.secondaryLabelField4G5G}
126
+ onchange={(e) => store.setSecondaryLabelField4G5G(e.currentTarget.value as keyof Cell | 'none')}
127
+ >
128
+ <option value="none">None</option>
129
+ {#each labelFieldOptions4G5G as field}
130
+ <option value={field}>{getFieldLabel(field)}</option>
131
+ {/each}
132
+ </select>
133
+ </div>
134
+ </div>
303
135
 
304
- .control-row:last-child {
305
- margin-bottom: 0;
306
- }
136
+ <div class="row align-items-center g-2 mb-3">
137
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">2G Primary</div>
138
+ <div class="col-3"></div>
139
+ <div class="col-5">
140
+ <select
141
+ id="label-primary-2g"
142
+ class="form-select form-select-sm w-100"
143
+ value={store.primaryLabelField2G}
144
+ onchange={(e) => store.setPrimaryLabelField2G(e.currentTarget.value as keyof Cell)}
145
+ >
146
+ {#each labelFieldOptions2G as field}
147
+ <option value={field}>{getFieldLabel(field)}</option>
148
+ {/each}
149
+ </select>
150
+ </div>
151
+ </div>
307
152
 
308
- .control-label {
309
- font-size: 0.8125rem;
310
- font-weight: 500;
311
- margin: 0;
312
- white-space: nowrap;
313
- flex-shrink: 0;
314
- min-width: 110px;
315
- }
153
+ <div class="row align-items-center g-2 mb-3">
154
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">2G Secondary</div>
155
+ <div class="col-3"></div>
156
+ <div class="col-5">
157
+ <select
158
+ id="label-secondary-2g"
159
+ class="form-select form-select-sm w-100"
160
+ value={store.secondaryLabelField2G}
161
+ onchange={(e) => store.setSecondaryLabelField2G(e.currentTarget.value as keyof Cell | 'none')}
162
+ >
163
+ <option value="none">None</option>
164
+ {#each labelFieldOptions2G as field}
165
+ <option value={field}>{getFieldLabel(field)}</option>
166
+ {/each}
167
+ </select>
168
+ </div>
169
+ </div>
316
170
 
317
- .form-range,
318
- .form-select {
319
- width: 140px;
320
- min-width: 140px;
321
- max-width: 140px;
322
- flex: 0 0 140px;
323
- }
324
-
325
- .form-control-color {
326
- width: 45px;
327
- height: 28px;
328
- padding: 2px;
329
- border-radius: 4px;
330
- flex-shrink: 0;
331
- margin-right: auto;
332
- }
171
+ <div class="row align-items-center g-2 mb-3">
172
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Label Size</div>
173
+ <div class="col-3 text-end">
174
+ <span class="badge bg-white text-muted border">{store.labelSize}px</span>
175
+ </div>
176
+ <div class="col-5">
177
+ <input
178
+ id="label-size-slider"
179
+ type="range"
180
+ class="form-range w-100"
181
+ min="8"
182
+ max="20"
183
+ step="1"
184
+ value={store.labelSize}
185
+ oninput={(e) => store.setLabelSize(Number(e.currentTarget.value))}
186
+ />
187
+ </div>
188
+ </div>
333
189
 
334
- .form-check {
335
- margin: 0;
336
- margin-right: auto;
337
- }
190
+ <div class="row align-items-center g-2 mb-3">
191
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Label Color</div>
192
+ <div class="col-3"></div>
193
+ <div class="col-5 d-flex justify-content-end">
194
+ <input
195
+ id="label-color-picker"
196
+ type="color"
197
+ class="form-control form-control-color"
198
+ value={store.labelColor}
199
+ oninput={(e) => store.setLabelColor(e.currentTarget.value)}
200
+ />
201
+ </div>
202
+ </div>
338
203
 
339
- .form-switch .form-check-input {
340
- cursor: pointer;
341
- }
342
- </style>
204
+ <div class="row align-items-center g-2 mb-3">
205
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Label Offset</div>
206
+ <div class="col-3 text-end">
207
+ <span class="badge bg-white text-muted border">{store.labelOffset}%</span>
208
+ </div>
209
+ <div class="col-5">
210
+ <input
211
+ id="label-offset-slider"
212
+ type="range"
213
+ class="form-range w-100"
214
+ min="100"
215
+ max="1000"
216
+ step="50"
217
+ value={store.labelOffset}
218
+ oninput={(e) => store.setLabelOffset(Number(e.currentTarget.value))}
219
+ />
220
+ </div>
221
+ </div>
222
+
223
+ <div class="row align-items-center g-2 mb-3">
224
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Halo Color</div>
225
+ <div class="col-3"></div>
226
+ <div class="col-5 d-flex justify-content-end">
227
+ <input
228
+ id="label-halo-color-picker"
229
+ type="color"
230
+ class="form-control form-control-color"
231
+ value={store.labelHaloColor}
232
+ oninput={(e) => store.setLabelHaloColor(e.currentTarget.value)}
233
+ />
234
+ </div>
235
+ </div>
236
+
237
+ <div class="row align-items-center g-2 mb-3">
238
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Halo Width</div>
239
+ <div class="col-3 text-end">
240
+ <span class="badge bg-white text-muted border">{store.labelHaloWidth}px</span>
241
+ </div>
242
+ <div class="col-5">
243
+ <input
244
+ id="label-halo-width-slider"
245
+ type="range"
246
+ class="form-range w-100"
247
+ min="0"
248
+ max="3"
249
+ step="0.5"
250
+ value={store.labelHaloWidth}
251
+ oninput={(e) => store.setLabelHaloWidth(Number(e.currentTarget.value))}
252
+ />
253
+ </div>
254
+ </div>
255
+
256
+ <div class="row align-items-center g-2">
257
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Min Zoom</div>
258
+ <div class="col-3 text-end">
259
+ <span class="badge bg-white text-muted border">{store.minLabelZoom}</span>
260
+ </div>
261
+ <div class="col-5">
262
+ <input
263
+ id="min-label-zoom-slider"
264
+ type="range"
265
+ class="form-range w-100"
266
+ min="10"
267
+ max="18"
268
+ step="1"
269
+ value={store.minLabelZoom}
270
+ oninput={(e) => store.setMinLabelZoom(Number(e.currentTarget.value))}
271
+ />
272
+ </div>
273
+ </div>
274
+ {/if}
275
+ </div>
276
+ </div>