fse-theme-default 0.4.0 → 0.5.0

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/README.md CHANGED
@@ -33,7 +33,8 @@ A child theme overrides any file under `src/` by creating the same path in
33
33
  its own `src/` — the parent's pages then use the child's version:
34
34
 
35
35
  - `src/layouts/Layout.astro` — the whole shell,
36
- - `src/components/SidebarLinks.astro` — app-specific sidebar entries (empty here),
36
+ - `src/components/SidebarLinks.astro` — app-specific sidebar entries (renders
37
+ the `sidebar-links` slot; overriding it replaces every contribution),
37
38
  - `src/components/Logo.astro` — the logo in the sidebar, mobile header and
38
39
  auth pages (or replace just `src/assets/logo.svg`),
39
40
  - `src/components/Head.astro` — extra `<head>` tags on every page (favicon,
@@ -41,7 +42,23 @@ its own `src/` — the parent's pages then use the child's version:
41
42
  - `src/styles/global.css` — the Tailwind root (import `src/styles/theme.css`
42
43
  and override tokens after it).
43
44
 
44
- `@parent/...` imports the parent's original file (e.g. to wrap it).
45
+ `@parent/...` imports the parent's original file (e.g. to wrap it);
46
+ `@theme/...` the most specific theme's version of a file.
47
+
48
+ ### Slots
49
+
50
+ Where several child themes and modules add to one place, the theme has a
51
+ slot: every `src/slots/<slot>/<Name>.astro` of the child theme and of the
52
+ app's modules is rendered there (this theme's first, modules next, the
53
+ child last; a same-named file of a more specific layer replaces the other):
54
+
55
+ - `sidebar-links` — sidebar entries between "Home" and the model links,
56
+ - `settings-sections` — sections of the settings page (data through
57
+ `page_context("settings", ...)`),
58
+ - `head` — extra `<head>` tags,
59
+ - `dashboard` — widgets below the home page's model cards,
60
+ - `record-panels` — panels below a generated record form (check
61
+ `meta.table`).
45
62
 
46
63
  ## Development
47
64
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fse-theme-default",
3
- "version": "0.4.0",
4
- "description": "Default theme for full_stack_engine apps — a complete Astro project: app shell, design system, auth pages, emails and the generic metadata-driven CRUD pages. Extend it with a child theme.",
3
+ "version": "0.5.0",
4
+ "description": "Default theme for full_stack_engine apps \u2014 a complete Astro project: app shell, design system, auth pages, emails and the generic metadata-driven CRUD pages. Extend it with a child theme.",
5
5
  "type": "module",
6
6
  "license": "MIT OR Apache-2.0",
7
7
  "repository": {
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "peerDependencies": {
27
27
  "astro": ">=7.0.0",
28
- "fse-ssr": ">=0.2.0"
28
+ "fse-ssr": ">=0.3.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@astrojs/check": "^0.9.9",
@@ -2,8 +2,12 @@
2
2
  /**
3
3
  * Extension point: extra <head> tags for every page (favicon, fonts, meta
4
4
  * tags). A child theme overrides this file; its version replaces this one,
5
- * so it should link its own favicon too.
5
+ * so it should link its own favicon too. Tags that only add (a module's
6
+ * stylesheet, a meta tag) go into the `head` slot instead:
7
+ * `src/slots/head/*.astro`.
6
8
  */
9
+ import Slot from "fse-ssr/Slot.astro";
7
10
  ---
8
11
 
9
12
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
13
+ <Slot name="head" />
@@ -2,11 +2,14 @@
2
2
  /**
3
3
  * Extension point: the app's own sections of the settings page, rendered
4
4
  * between "account security" and "delete account". The default shows the
5
- * account's name. A child theme overrides this file to put its own forms
6
- * here (a profile, preferences, ...) — the data comes from the app through
7
- * `FrameworkApp::page_context("settings", ...)`, and its POST handlers
8
- * re-render the page with `auth_module::settings_context`.
5
+ * account's name, followed by the `settings-sections` slot: every
6
+ * `src/slots/settings-sections/*.astro` of a child theme or a module (a
7
+ * profile, preferences, ...). Their data comes through
8
+ * `page_context("settings", ...)` (app or module), and POST handlers
9
+ * re-render the page with `auth_module::settings_context`. Overriding this
10
+ * file replaces the default and every contribution.
9
11
  */
12
+ import Slot from "fse-ssr/Slot.astro";
10
13
  import { ssr } from "fse-ssr/ssr";
11
14
  import type { SettingsPage } from "../types/pages";
12
15
 
@@ -25,3 +28,5 @@ const { t, first_name, last_name } = ssr<SettingsPage>();
25
28
  </div>
26
29
  </div>
27
30
  )}
31
+
32
+ <Slot name="settings-sections" />
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  /**
3
3
  * The app shell's navigation. Everything is data-driven: the framework
4
- * injects `nav` (the models' sidebar entries — label, icon and order come
5
- * from `#[model(nav(...), public_nav(...))]` and the app's translations)
6
- * and `user`; links that aren't a model come from the SidebarLinks
7
- * extension point.
4
+ * injects `nav_groups` (the models' sidebar entries — label, icon and order
5
+ * come from `#[model(nav(...), public_nav(...))]` and the app's
6
+ * translations — grouped by module, each module's under its title) and
7
+ * `user`; links that aren't a model come from the SidebarLinks extension
8
+ * point.
8
9
  */
9
10
  import { ssr } from "fse-ssr/ssr";
10
11
  import Logo from "./Logo.astro";
@@ -13,7 +14,7 @@ import NavLink from "./NavLink.astro";
13
14
  import SidebarLinks from "./SidebarLinks.astro";
14
15
  import type { ShellContext } from "../types";
15
16
 
16
- const { t, nav, user, lang_prefix } = ssr<ShellContext>();
17
+ const { t, nav_groups, user, lang_prefix } = ssr<ShellContext>();
17
18
  ---
18
19
 
19
20
  <div
@@ -72,10 +73,19 @@ const { t, nav, user, lang_prefix } = ssr<ShellContext>();
72
73
 
73
74
  <SidebarLinks />
74
75
 
75
- {nav.map((item) => (
76
- <NavLink href={`${lang_prefix}${item.href}`} label={item.label}>
77
- <NavIcon name={item.icon} />
78
- </NavLink>
76
+ {nav_groups.map((group) => (
77
+ <div class="space-y-0.5">
78
+ {group.label && (
79
+ <p class="sidebar-label px-3 pt-4 pb-1 text-xs font-semibold uppercase tracking-wide text-secondary-400 truncate">
80
+ {group.label}
81
+ </p>
82
+ )}
83
+ {group.entries.map((item) => (
84
+ <NavLink href={`${lang_prefix}${item.href}`} label={item.label}>
85
+ <NavIcon name={item.icon} />
86
+ </NavLink>
87
+ ))}
88
+ </div>
79
89
  ))}
80
90
  </nav>
81
91
 
@@ -1,10 +1,17 @@
1
1
  ---
2
2
  /**
3
3
  * Extension point: app-specific sidebar entries, rendered between "Home"
4
- * and the generated model links. Empty in the default theme — a child theme
5
- * overrides this file (same path under its src/) to add its own links:
4
+ * and the generated model links. Filled through the `sidebar-links` slot:
5
+ * every `src/slots/sidebar-links/*.astro` of a child theme or a module is
6
+ * rendered here, so several can add links side by side:
6
7
  *
7
- * import NavLink from "@parent/components/NavLink.astro";
8
+ * // src/slots/sidebar-links/Products.astro
9
+ * import NavLink from "@theme/components/NavLink.astro";
8
10
  * <NavLink href="/products" label={t.nav.products}>…icon…</NavLink>
11
+ *
12
+ * Overriding this file instead replaces every contribution.
9
13
  */
14
+ import Slot from "fse-ssr/Slot.astro";
10
15
  ---
16
+
17
+ <Slot name="sidebar-links" />
@@ -7,13 +7,19 @@
7
7
  * row) the same form renders disabled and without a save button. An
8
8
  * existing row also shows its allowed row actions and links to its nested
9
9
  * models and link tables; a nested model links back to its parent row.
10
+ * Below the form: `related` — the rows of other models (and modules)
11
+ * that point at this one through a foreign key, one panel each — then the
12
+ * `record-panels` slot: extra panels from a child theme or modules
13
+ * (`src/slots/record-panels/*.astro`, which check `meta.table` to pick the
14
+ * models they extend).
10
15
  */
16
+ import Slot from "fse-ssr/Slot.astro";
11
17
  import { ssr } from "fse-ssr/ssr";
12
18
  import type { SsrValue } from "fse-ssr/ssr";
13
19
  import Layout from "../../layouts/Layout.astro";
14
20
  import type { ModelFormPage } from "../../types";
15
21
 
16
- const { t, lang_prefix, meta, row, errors, is_new, can_edit, can_delete, actions, parent } =
22
+ const { t, lang_prefix, meta, row, errors, is_new, can_edit, can_delete, actions, parent, related } =
17
23
  ssr<ModelFormPage>();
18
24
  const title = t.models[meta.table].title ?? meta.table;
19
25
  const formErrors = t.form_errors as Record<string, SsrValue<string>>;
@@ -155,6 +161,32 @@ const inputClass =
155
161
  ))}
156
162
  </div>
157
163
  )}
164
+
165
+ {related.map((panel) => (
166
+ <section class="mt-8">
167
+ <h2 class="mb-2 text-base font-semibold text-secondary-900">
168
+ {t.models[panel.table].title ?? panel.table}
169
+ {panel.label_column && (
170
+ <span class="font-normal text-secondary-500"> · {t.models[panel.table].fields[panel.label_column] ?? panel.label_column}</span>
171
+ )}
172
+ <span class="ml-1 text-sm font-normal text-secondary-500">({panel.total})</span>
173
+ </h2>
174
+ <ul class="divide-y divide-secondary-100 rounded-lg border border-secondary-100 bg-white">
175
+ {panel.rows.map((item) => (
176
+ <li>
177
+ <a class="block px-4 py-2 text-sm text-secondary-800 hover:bg-secondary-50" href={`${lang_prefix}${panel.base_path}/${item.id}`}>
178
+ {item.title}
179
+ </a>
180
+ </li>
181
+ ))}
182
+ </ul>
183
+ {panel.more && (
184
+ <p class="mt-1 text-xs text-secondary-500">{t.model_ui.more_related ?? "Showing the first 10."}</p>
185
+ )}
186
+ </section>
187
+ ))}
188
+
189
+ <Slot name="record-panels" />
158
190
  </main>
159
191
 
160
192
  <script>
@@ -1,8 +1,11 @@
1
1
  ---
2
2
  /**
3
- * Default home page: a dashboard linking every model the user may manage.
4
- * Apps usually replace it (a child theme's own src/pages/index.astro).
3
+ * Default home page: a dashboard linking every model the user may manage,
4
+ * then the `dashboard` slot (widgets from a child theme or modules:
5
+ * `src/slots/dashboard/*.astro`). Apps usually replace it (a child theme's
6
+ * own src/pages/index.astro).
5
7
  */
8
+ import Slot from "fse-ssr/Slot.astro";
6
9
  import { ssr } from "fse-ssr/ssr";
7
10
  import Card from "../components/Card.astro";
8
11
  import Header from "../components/Header.astro";
@@ -37,5 +40,7 @@ const { t, nav, user, lang_prefix } = ssr<ShellContext>();
37
40
  ))}
38
41
  </div>
39
42
  )}
43
+
44
+ <Slot name="dashboard" />
40
45
  </main>
41
46
  </Layout>
package/src/types.ts CHANGED
@@ -116,6 +116,22 @@ export interface ModelFormPage {
116
116
  actions: string[];
117
117
  /** The parent row, for a nested model (else null). */
118
118
  parent: ParentContext | null;
119
+ /** Rows of other models (and modules) pointing at this one. */
120
+ related: RelatedPanel[];
121
+ }
122
+
123
+ /**
124
+ * One model's rows that point at a record through a foreign key — the
125
+ * first ten, with `more` when there are others.
126
+ */
127
+ export interface RelatedPanel {
128
+ table: string;
129
+ base_path: string;
130
+ /** Set when the model points here through several columns. */
131
+ label_column: string | null;
132
+ total: number;
133
+ more: boolean;
134
+ rows: { id: number; title: string }[];
119
135
  }
120
136
 
121
137
  /** `fse/links` — the rows of another model linked to one row. */
@@ -142,6 +158,19 @@ export interface NavItem {
142
158
  icon: string;
143
159
  /** The public list's entry rather than the admin page. */
144
160
  public: boolean;
161
+ /** The cargo package defining the model (the app's or a module's). */
162
+ krate: string;
163
+ }
164
+
165
+ /**
166
+ * `nav` grouped by origin: the app's own models first (`module` and
167
+ * `label` null), then one group per enabled module, labelled
168
+ * `t.modules.<package>.title`.
169
+ */
170
+ export interface NavGroup {
171
+ module: string | null;
172
+ label: string | null;
173
+ entries: NavItem[];
145
174
  }
146
175
 
147
176
  /** The signed-in user as injected by the framework (absent when signed out). */
@@ -155,6 +184,7 @@ export interface SessionUser {
155
184
  /** Context keys every page receives, whatever renders it. */
156
185
  export interface ShellContext {
157
186
  nav: NavItem[];
187
+ nav_groups: NavGroup[];
158
188
  user?: SessionUser;
159
189
  lang_prefix: string;
160
190
  }
package/theme.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "fse-theme-default",
3
- "version": "0.2.1",
3
+ "version": "0.5.0",
4
4
  "description": "Default full_stack_engine theme: app shell, design system, auth pages, emails and the generic model CRUD pages."
5
5
  }