Skip to content

Commit 5f58c14

Browse files
committed
refactor: replace AppControlPanel components with unified ListView implementation
- Consolidate LayersView, QuickAccessView, and SearchView into single AppControlListView component - Add controlPanelDataService for centralized data management
1 parent bf83db0 commit 5f58c14

File tree

9 files changed

+581
-531
lines changed

9 files changed

+581
-531
lines changed
Lines changed: 393 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,393 @@
1+
//@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3
2+
3+
<template>
4+
<ListView
5+
:items="listItems"
6+
:itemTemplateSelector="templateSelector"
7+
@itemTap="onItemTap"
8+
separatorColor="#00000000"
9+
>
10+
<template #default="{ item }">
11+
<Label text="Unknown item type" />
12+
</template>
13+
14+
<template #action-buttons-group="{ item }">
15+
<GridLayout
16+
columns="*, *, *, *"
17+
rows="90"
18+
class="action-buttons-block"
19+
>
20+
<StackLayout
21+
v-for="(button, index) in item.buttons"
22+
:key="button.id"
23+
:col="index"
24+
class="btn"
25+
@tap="onActionButtonTap(button.id)"
26+
>
27+
<Label class="fa icon" :text="$filters.fonticon(button.icon)" horizontalAlignment="center" />
28+
<Label class="text" :text="button.text" horizontalAlignment="center" />
29+
</StackLayout>
30+
</GridLayout>
31+
</template>
32+
33+
<template #navigation-item="{ item }">
34+
<GridLayout
35+
class="navigation-item"
36+
columns="42, *"
37+
rows="42"
38+
@tap="onNavigationItemTap(item.id)"
39+
>
40+
<Label class="fa icon" :text="$filters.fonticon(item.icon)" col="0" row="0" />
41+
<Label class="navigation-item-label" :text="item.text" col="1" row="0" />
42+
</GridLayout>
43+
</template>
44+
45+
<template #select-fields-group="{ item }">
46+
<GridLayout
47+
columns="*, 8, *"
48+
rows="28, 52"
49+
class="select-fields-block"
50+
>
51+
<!-- Highlighter column -->
52+
<Label
53+
v-if="item.fields[0].visible"
54+
col="0"
55+
row="0"
56+
class="select-label"
57+
:text="item.fields[0].label"
58+
/>
59+
<SelectField
60+
v-if="item.fields[0].visible"
61+
col="0"
62+
row="1"
63+
:items="item.fields[0].items"
64+
:selectedIndex="item.fields[0].items.indexOf(item.fields[0].selectedValue)"
65+
title="Select Highlighter"
66+
@change="onHighlighterSelected"
67+
/>
68+
69+
<!-- Base layer column -->
70+
<Label
71+
v-if="item.fields[1].visible"
72+
col="2"
73+
row="0"
74+
class="select-label"
75+
:text="item.fields[1].label"
76+
/>
77+
<SelectField
78+
v-if="item.fields[1].visible"
79+
col="2"
80+
row="1"
81+
:items="item.fields[1].items"
82+
:selectedIndex="item.fields[1].selectedIndex"
83+
idField="layerId"
84+
textField="name"
85+
title="Select Base Layer"
86+
@change="onBaseLayerSelected"
87+
/>
88+
</GridLayout>
89+
</template>
90+
91+
<template #portal-icons-group="{ item }">
92+
<GridLayout
93+
columns="*, *, *, *, *, *, *, *, *"
94+
rows="52"
95+
class="portal-icons-block"
96+
>
97+
<template v-for="(portal, index) in item.portals" :key="portal.layerId">
98+
<SVGView
99+
v-if="index <= 8"
100+
class="overlay-portal"
101+
:class="{ 'overlay-portal--active': portal.active === true }"
102+
:col="index"
103+
@tap="onOverlayPortalToggle($event, portal.index)"
104+
:src="'~/assets/icons/portals/portal_L'+index+'_'+String(portal.active)+'.svg'"
105+
stretch="aspectFit"
106+
/>
107+
</template>
108+
</GridLayout>
109+
</template>
110+
111+
<template #switch-pair="{ item }">
112+
<GridLayout
113+
columns="*, 8, *"
114+
rows="62"
115+
class="switch-pair-block"
116+
>
117+
<GridLayout
118+
v-for="(switchItem, colIndex) in item.items"
119+
:key="switchItem.layerId"
120+
:col="colIndex * 2"
121+
class="btn-primary"
122+
columns="*, 50"
123+
rows="50"
124+
>
125+
<Label
126+
class="overlay-item-label"
127+
:text="switchItem.name"
128+
@tap="onOverlayToggle(switchItem.index, 'label')"
129+
col="0"
130+
row="0"
131+
/>
132+
<Switch
133+
class="switch"
134+
:checked="switchItem.active"
135+
@checkedChange="args => onOverlayToggle(switchItem.index, args.value)"
136+
col="1"
137+
row="0"
138+
/>
139+
</GridLayout>
140+
</GridLayout>
141+
</template>
142+
143+
<template #switch-single="{ item }">
144+
<GridLayout
145+
class="switch-single-block"
146+
columns="*, 50"
147+
rows="50"
148+
>
149+
<Label
150+
class="overlay-item-label"
151+
:text="item.item.name"
152+
@tap="onOverlayToggle(item.item.index, 'label')"
153+
col="0"
154+
row="0"
155+
/>
156+
<Switch
157+
class="switch"
158+
:checked="item.item.active"
159+
@checkedChange="args => onOverlayToggle(item.item.index, args.value)"
160+
col="1"
161+
row="0"
162+
/>
163+
</GridLayout>
164+
</template>
165+
</ListView>
166+
</template>
167+
168+
<script>
169+
import SelectField from "@/components/base/SelectField.vue";
170+
import { mapState } from 'vuex';
171+
import { $navigateTo } from 'nativescript-vue';
172+
import SettingsView from '@/components/Settings/SettingsView';
173+
import PluginsView from '@/components/Settings/PluginsView';
174+
175+
export default {
176+
name: 'AppControlListView',
177+
178+
components: {
179+
SelectField
180+
},
181+
182+
props: {
183+
listItems: {
184+
type: Array,
185+
required: true
186+
}
187+
},
188+
189+
data() {
190+
return {
191+
settingsScreen: SettingsView,
192+
pluginsScreen: PluginsView,
193+
}
194+
},
195+
196+
computed: {
197+
...mapState({
198+
isDebugActive: state => state.ui.isDebugActive
199+
})
200+
},
201+
202+
methods: {
203+
// Template selector function - determines which template to use
204+
templateSelector(data) {
205+
const validTypes = [
206+
'action-buttons-group',
207+
'navigation-item',
208+
'select-fields-group',
209+
'portal-icons-group',
210+
'switch-pair',
211+
'switch-single'
212+
];
213+
const itemType = data.item.type;
214+
return validTypes.includes(itemType) ? itemType : 'default';
215+
},
216+
217+
onItemTap(args) {
218+
// Handle general item tap if needed
219+
},
220+
221+
onActionButtonTap(buttonId) {
222+
switch(buttonId) {
223+
case 'settings':
224+
this.openSettings();
225+
break;
226+
case 'plugins':
227+
this.openPlugins();
228+
break;
229+
case 'debug':
230+
this.toggleDebugMode();
231+
break;
232+
case 'reload':
233+
this.reloadWebView();
234+
break;
235+
}
236+
},
237+
238+
onNavigationItemTap(paneName) {
239+
this.switchToPane(paneName);
240+
},
241+
242+
onBaseLayerSelected(args) {
243+
const id = args.selectedId;
244+
if (id !== undefined && id !== this.$store.state.map.baseLayerSelected) {
245+
this.$store.dispatch('map/setActiveBaseLayer', id);
246+
}
247+
},
248+
249+
onOverlayPortalToggle(e, index) {
250+
const currentLayer = this.$store.state.map.overlayLayers[index];
251+
const active = !currentLayer.active;
252+
e.object.src = '~/assets/icons/portals/portal_L'+index+'_'+String(active)+'.svg';
253+
this.$store.dispatch('map/setOverlayLayerProperty', {index, active});
254+
},
255+
256+
onOverlayToggle(index, value) {
257+
if (typeof value === 'boolean') {
258+
// Direct value from switch event
259+
this.$store.dispatch('map/setOverlayLayerProperty', {index, active: value});
260+
} else {
261+
// Label tap - toggle current state
262+
const currentLayer = this.$store.state.map.overlayLayers[index];
263+
if (currentLayer) {
264+
this.$store.dispatch('map/setOverlayLayerProperty', {index, active: !currentLayer.active});
265+
}
266+
}
267+
},
268+
269+
onHighlighterSelected(args) {
270+
if (args.item) {
271+
this.$store.dispatch('map/setActiveHighlighter', args.item);
272+
}
273+
},
274+
275+
// Action button methods
276+
openSettings() {
277+
$navigateTo(this.settingsScreen, {
278+
animated: true,
279+
transition: {
280+
name: 'slideLeft',
281+
duration: 300
282+
}
283+
});
284+
},
285+
286+
openPlugins() {
287+
$navigateTo(this.pluginsScreen, {
288+
animated: true,
289+
transition: {
290+
name: 'slideLeft',
291+
duration: 300
292+
}
293+
});
294+
},
295+
296+
toggleDebugMode() {
297+
this.$store.dispatch('ui/toggleDebugMode');
298+
},
299+
300+
reloadWebView() {
301+
this.$store.dispatch('ui/reloadWebView');
302+
},
303+
304+
switchToPane(name) {
305+
this.$store.dispatch('navigation/setCurrentPane', name);
306+
}
307+
}
308+
};
309+
</script>
310+
311+
<style scoped lang="scss">
312+
@import '@/app';
313+
314+
.action-buttons-block {
315+
margin-bottom: $spacing-m;
316+
}
317+
318+
.btn {
319+
font-size: $font-size;
320+
text-align: center;
321+
padding: 0 $spacing-s;
322+
}
323+
324+
.btn .icon {
325+
margin: 0 0 $spacing-xs 0;
326+
width: 54;
327+
height: 54;
328+
font-size: $font-size-headline;
329+
border-radius: $radius-large;
330+
color: $on-primary;
331+
background-color: $surface-bright;
332+
box-shadow: 0 2 4 rgba(0, 0, 0, 0.05);
333+
}
334+
335+
.btn .text {
336+
color: $text;
337+
}
338+
339+
.navigation-item {
340+
margin-bottom: $spacing-xs;
341+
}
342+
343+
.icon {
344+
font-size: 18;
345+
width: 32;
346+
vertical-align: center;
347+
text-align: center;
348+
}
349+
350+
.navigation-item-label {
351+
font-size: $font-size;
352+
padding-left: 15;
353+
}
354+
355+
.select-fields-block {
356+
margin-bottom: $spacing-m;
357+
}
358+
359+
.select-label {
360+
font-size: $font-small-size;
361+
}
362+
363+
.portal-icons-block {
364+
margin-bottom: $spacing-m;
365+
}
366+
367+
.overlay-portal {
368+
margin: $spacing-xs;
369+
border-radius: $radius-large;
370+
horizontal-align: center;
371+
372+
&--active {
373+
background-color: $surface-bright;
374+
box-shadow: 0 2 5 rgba(0, 0, 0, 0.1);
375+
}
376+
}
377+
378+
.switch-pair-block {
379+
margin-bottom: $spacing-xs;
380+
}
381+
382+
.switch-single-block {
383+
margin-bottom: $spacing-xs;
384+
}
385+
386+
.overlay-item-label {
387+
font-size: $font-size;
388+
}
389+
390+
.switch {
391+
width: 50;
392+
}
393+
</style>

0 commit comments

Comments
 (0)