Skip to content

Commit fe37b1e

Browse files
morehawesCopilot
andauthored
feat: Vue component buttons/panels and flexible navbar (#4)
Add Vue component support for custom buttons and panels in the Navigator config API. The navbar now supports start/middle/end positioning for buttons, with custom Vue components rendered directly in the toolbar. Source changes: - top.vue: navbar renders custom component buttons in start/middle/end slots - panels.vue: panels render Vue components with props and DOM render fallback - index.js: export getEmitter and useUI for plugin authors Documentation: - Rewrite docs/dev/7.features.md with a complete Recordings plugin example demonstrating the plugin system, config-driven UI, and map layer management - Add Recordings plugin source files in docs/dev/feature/ for copy-paste reuse - Expand docs/dev/8.extending.md with Vue component examples, plugin system details, and programmatic panel control via useUI - Update docs/dev/1.config.md with buttons, panels, and plugins config reference - Add RTL language support note to docs/dev/5.locale.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4200574 commit fe37b1e

File tree

13 files changed

+1299
-186
lines changed

13 files changed

+1299
-186
lines changed

TASKS.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,20 @@ Improve the panel api documented in @docs/dev/1.config.md with to support for Vu
1313

1414
# Menu
1515

16-
# Panel Nvigation
16+
Update the app top navigation bar to be more flexible. Currently @src/components/ui/top.vue the navbar has a left/middle/right positioned icon buttons. I want to make this more programtic, so that developers can modify/add/remove/replace icon buttons in the navbar as vue components. This can also be used to completely change the navbar dynamically, for example a consuming app may want to add their own custom navbar. I would also like to make this more friendly for reording, to support both RLS and LTR languages (a future consideration) for example. So the concept of start/end instead of left/right. The top nav should be able to accept any number of buttons, with the first aligned to the start, and the last aligned to the end (use bootstrap best practises). 3+ buttons will be added to the middle, evenly spaced. Update the documentation @docs/dev/8.extending.md to reflect these changes, and provide an example of how you would add a centered icon button to the navbar using the new API. Add a github-style alert to the @docs/dev/5.locale.md about the future support for RTL languages, and how the new navbar API will support this when it arrives.
17+
18+
# Testing
1719

1820
# Extending
1921

20-
Let's improve the developer docs, specifically the @docs/dev/7.features.md . I want you to rewrite the "Adding Features to Navigator" section to highight the integration methods documented in @docs/dev/8.extending.md . By way of example, create a new Recordings feature that integrates as a vue plugin. The full plugin code should be included in the documentation, like with the green theme sass example @docs/dev/6.theme.md so that developers could copy and paste the code to create their own feature. As you build this example,
22+
Let's improve the developer docs, specifically the @docs/dev/7.features.md . I want you to rewrite the "Adding Features to Navigator" section to highight the integration methods documented in @docs/dev/8.extending.md . By way of example, create a new Recordings feature that integrates as a vue plugin. The full plugin code should be included in the documentation, like with the green theme sass example @docs/dev/6.theme.md so that developers could copy and paste the code to create their own feature. The recordings plugin should be a complete, but minimal feature. It should have:
23+
24+
- A centre-aligned Record Button in the top navbar that toggles recording on/off. The button starts/pauses recording, and changes label and color to indicate the recording state (e.g. red when recording, grey when paused). When pause is presses, this should open a simple Recordings panel.
25+
26+
- The Recordings panel should display simple information about the current recording track, such as duration and distance. It should also have "Pause/Resume", "Discard", "Save" button that saves the recording data to localStorage (using @src/composables/useStorage.js) If there is an active recording, it should also be saved to local storage so that it can be resumed if the user accidentally refreshes the page.
27+
28+
- When a recording is active, the map should display a blue (primary brand sass colour @docs/dev/6.theme.md) line showing the path of the recording. This line should update in real time as the recording progresses. When the recording is paused, the line should turn grey.
29+
30+
- The recordings panel should show a list of past recordings saved in localStorage, with the option to show/delete/download(as GPX) them. Each recording should display its duration, distance, and a timestamp of when it was recorded.
31+
32+
Remember to include the full plugin code in the documentation, and to update the "Adding Features to Navigator" section to highlight this new integration method.
-201 Bytes
Loading
40 Bytes
Loading

docs/dev/1.config.md

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -193,22 +193,70 @@ See [Map](./3.map.md) for the full map API including view persistence and progra
193193

194194
Adds custom buttons to the top navigation bar. Each button can optionally open a panel tab with custom content.
195195

196+
A button can render using the built-in icon button (specify `icon` and `label`) or a custom Vue component (specify `component`):
197+
198+
**Vue component button:**
199+
196200
```js
201+
import HomeButton from './components/HomeButton.vue';
202+
197203
Navigator.create({
198-
id: "my-map",
204+
id: 'my-map',
199205
buttons: [
200206
{
201-
id: "measure",
202-
icon: "ruler",
203-
label: "Measure",
204-
position: "end",
207+
id: 'home',
208+
position: 'middle',
209+
component: HomeButton,
210+
props: { label: 'Home' },
211+
},
212+
],
213+
}).mount();
214+
```
215+
216+
The component is rendered inside Navigator's Vue app tree, so it has access to dependency injection (`inject('navigatorId')`) and `getMapInstance()`.
217+
218+
**Icon button with panel (Vue component):**
219+
220+
```js
221+
import MeasurePanel from './components/MeasurePanel.vue';
222+
223+
Navigator.create({
224+
id: 'my-map',
225+
buttons: [
226+
{
227+
id: 'measure',
228+
icon: 'ruler',
229+
label: 'Measure',
230+
position: 'end',
231+
panel: {
232+
title: 'Measurement Tool',
233+
component: MeasurePanel,
234+
},
235+
},
236+
],
237+
}).mount();
238+
```
239+
240+
The Vue component is rendered inside Navigator's Vue app tree, so it has full access to Vue's dependency injection. Use `inject('navigatorId')` to get the instance ID and `getMapInstance(id)` to access the map.
241+
242+
**DOM render function:**
243+
244+
```js
245+
Navigator.create({
246+
id: 'my-map',
247+
buttons: [
248+
{
249+
id: 'measure',
250+
icon: 'ruler',
251+
label: 'Measure',
252+
position: 'end',
205253
onClick: ({ map, instanceId }) => {
206254
/* ... */
207255
},
208256
panel: {
209-
title: "Measurement Tool",
257+
title: 'Measurement Tool',
210258
render: (container, { map, instanceId }) => {
211-
container.innerHTML = "<p>Draw on the map to measure.</p>";
259+
container.innerHTML = '<p>Draw on the map to measure.</p>';
212260
},
213261
},
214262
},
@@ -226,6 +274,27 @@ See [Extending — Config-Driven Buttons and Panels](./8.extending.md#config-dri
226274

227275
Adds custom tabs to the side panel without requiring a toolbar button. Use this when you want panel content accessible from the tab bar but don't need a top-bar button.
228276

277+
**Vue component (preferred):**
278+
279+
```js
280+
import InfoPanel from './components/InfoPanel.vue';
281+
282+
Navigator.create({
283+
id: 'my-map',
284+
panels: [
285+
{
286+
id: 'info',
287+
icon: 'info-circle',
288+
title: 'Info',
289+
component: InfoPanel,
290+
props: { showDetails: true },
291+
},
292+
],
293+
}).mount();
294+
```
295+
296+
**DOM render function:**
297+
229298
```js
230299
Navigator.create({
231300
id: 'my-map',
@@ -247,7 +316,11 @@ Navigator.create({
247316
| `id` | `string` || Unique panel identifier |
248317
| `icon` | `string` || Icon name from the SVG sprite |
249318
| `title` | `string` || Tab label |
250-
| `render` | `function` || Called with `(container, { map, instanceId })` to populate content |
319+
| `component` | `Component` | | Vue component to render (preferred) |
320+
| `props` | `object` | | Props to pass to the Vue component |
321+
| `render` | `function` | | Called with `(container, { map, instanceId })` to populate content |
322+
323+
> Provide either `component` or `render`. If both are present, `component` takes precedence.
251324
252325
> **Buttons vs Panels:** Use `buttons` when you want a toolbar button (optionally with a panel). Use `panels` when you only need a panel tab. Both can be combined in the same config.
253326

docs/dev/5.locale.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ Custom `messages` are merged on top of the built-in translations — only the ke
5858
> [!TIP]
5959
> More languages are coming soon! To contribute a translation, see [Contributing a translation](#contributing-a-translation) below.
6060
61+
> [!NOTE]
62+
> **RTL language support** is planned for a future release. The navigation bar already uses directional positioning (`'start'` / `'end'` rather than left / right) and Bootstrap 5 logical utilities, so the layout will adapt automatically when RTL support lands. See [Extending — Custom Buttons](../dev/8.extending.md#custom-buttons) for the toolbar positioning API.
63+
6164
| Code | Language |
6265
| ---- | -------- |
6366
| `en` | English |

0 commit comments

Comments
 (0)