feat: polish
This commit is contained in:
parent
2e6177fe74
commit
4bd927bb4e
137 changed files with 6357 additions and 137560 deletions
152
docs/ADDING_A_FEATURE.md
Normal file
152
docs/ADDING_A_FEATURE.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
# Adding a feature
|
||||
|
||||
Walk-through for building a new side-panel feature. We'll use a hypothetical
|
||||
"NOTAMs" panel (no-fly-zone overlays) as the example.
|
||||
|
||||
## 1. Pick a feature folder
|
||||
|
||||
```
|
||||
src/lib/features/notams/
|
||||
├── index.ts
|
||||
├── store.ts
|
||||
├── types.ts
|
||||
├── NotamsPanel.svelte
|
||||
└── NotamRenderer.svelte # if you draw on the map
|
||||
```
|
||||
|
||||
Re-export the public surface from `index.ts`.
|
||||
|
||||
## 2. Define the domain
|
||||
|
||||
If the feature introduces a durable type (something saved to a server or
|
||||
localStorage), put the type in `src/lib/domain/notam.ts` and re-export from
|
||||
`src/lib/domain/index.ts`. Ephemeral state types can live in the feature's
|
||||
`types.ts`.
|
||||
|
||||
```ts
|
||||
// src/lib/domain/notam.ts
|
||||
export interface Notam {
|
||||
id: string;
|
||||
code: string;
|
||||
polygon: LatLngTuple[];
|
||||
validFrom: string;
|
||||
validTo: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Add state
|
||||
|
||||
Feature-scoped stores go in the feature folder. Use `persisted()` if the user
|
||||
should not lose the state on reload, plain `writable()` otherwise.
|
||||
|
||||
```ts
|
||||
// src/lib/features/notams/store.ts
|
||||
import { persisted } from '$state';
|
||||
import type { Notam } from '$domain';
|
||||
|
||||
export const notamsStore = persisted<Notam[]>('notams', []);
|
||||
```
|
||||
|
||||
## 4. Add API (if needed)
|
||||
|
||||
```ts
|
||||
// src/lib/api/notams.ts
|
||||
import { api } from './client';
|
||||
import type { Notam } from '$domain';
|
||||
export const notamsApi = { list: () => api.get<Notam[]>('/notams/') };
|
||||
```
|
||||
|
||||
And export from `src/lib/api/index.ts`.
|
||||
|
||||
## 5. Build the panel
|
||||
|
||||
Re-use UI primitives. Wrap everything in `CollapsibleCard` so it lands in the
|
||||
side panel.
|
||||
|
||||
```svelte
|
||||
<!-- src/lib/features/notams/NotamsPanel.svelte -->
|
||||
<script lang="ts">
|
||||
import { CollapsibleCard } from '$ui';
|
||||
import { t } from '$i18n';
|
||||
import { notamsStore } from './store';
|
||||
</script>
|
||||
|
||||
<CollapsibleCard title={$t('notams.title')}>
|
||||
{#each $notamsStore as notam (notam.id)}
|
||||
<div>{notam.code}</div>
|
||||
{/each}
|
||||
</CollapsibleCard>
|
||||
```
|
||||
|
||||
## 6. Add i18n keys
|
||||
|
||||
Append to `src/lib/i18n/locales/ru.json` and `en.json`. The lookup is
|
||||
dot-separated:
|
||||
|
||||
```json
|
||||
{
|
||||
"notams": {
|
||||
"title": "NOTAMs",
|
||||
"empty": "Нет активных NOTAMs"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. Draw on the map (optional)
|
||||
|
||||
Every on-map drawing goes through a Scene. One scene per feature, or per sub-
|
||||
entity if the feature manages many independent overlays (like workspaces).
|
||||
|
||||
```svelte
|
||||
<!-- src/lib/features/notams/NotamRenderer.svelte -->
|
||||
<script lang="ts">
|
||||
import { getMap } from '$map';
|
||||
import { notamsStore } from './store';
|
||||
|
||||
const map = getMap();
|
||||
if (!map) throw new Error('NotamRenderer requires a <Map /> ancestor');
|
||||
|
||||
$effect(() => {
|
||||
const scene = map.scene('notams');
|
||||
scene.clear();
|
||||
for (const n of $notamsStore) {
|
||||
// scene.addLine/addCircle/etc.
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
Add `<NotamRenderer />` as a child of `<MapView />` in the predict route.
|
||||
|
||||
## 8. Wire into the route
|
||||
|
||||
```svelte
|
||||
<!-- src/routes/predict/+page.svelte -->
|
||||
<MapView>
|
||||
<WorkspaceRenderer />
|
||||
<NotamRenderer />
|
||||
<PanelContainer position="right">
|
||||
<TabBar tabs={[...] } bind:active={rightTab} />
|
||||
{#if rightTab === 'notams'}
|
||||
<NotamsPanel />
|
||||
{/if}
|
||||
</PanelContainer>
|
||||
</MapView>
|
||||
```
|
||||
|
||||
## 9. Add a settings entry (optional)
|
||||
|
||||
If the feature should be toggleable, extend `src/lib/features/settings/store.ts`
|
||||
and `schema.ts`. Keep the field declarative (kind + labelKey + path) — the
|
||||
SettingsPanel renders it automatically.
|
||||
|
||||
## 10. Checklist before you open a PR
|
||||
|
||||
- [ ] `npm run check` passes
|
||||
- [ ] `npm run build` passes
|
||||
- [ ] All user-visible strings routed through `$t`
|
||||
- [ ] Feature only depends on shared modules (`$domain`, `$state`, `$api`,
|
||||
`$ui`, `$map`) — not on other features' internals
|
||||
- [ ] `index.ts` exports only what the outside world needs
|
||||
- [ ] Panel matches existing visual language (CollapsibleCard, Bootstrap sm
|
||||
form controls, same spacing)
|
||||
Loading…
Add table
Add a link
Reference in a new issue