@mercurjs/docs 2.3.8-canary.2 → 2.3.8-canary.3

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.
@@ -0,0 +1,169 @@
1
+ ---
2
+ title: "Layouts and layers"
3
+ sidebarTitle: "Layouts and layers"
4
+ description: "How extensions from plugins and your project stack, how a page exposes its sections, and how to hide, reorder, replace or wrap what a page renders."
5
+ ---
6
+
7
+ Every panel extension comes from an ordered list of **layers**:
8
+
9
+ 1. `@mercurjs/admin` or `@mercurjs/vendor` itself,
10
+ 2. each plugin in `medusa-config` `plugins` order, from its built `.medusa/server/src/<admin|vendor>/index.mjs`,
11
+ 3. your Medusa project's `src/admin` or `src/vendor` folder (next to `medusa-config.ts`),
12
+ 4. the panel app's own `src` folder.
13
+
14
+ A later layer wins. A route at a path an earlier layer declared replaces it, a widget with the same `id`
15
+ replaces it, a translation key overrides it, and a layout preference overrides it field by field. Every
16
+ layer uses the same folders: `routes/`, `widgets/`, `custom-fields/`, `layouts/`, `i18n/` and a
17
+ `_layouts.ts` file.
18
+
19
+ ## Pages expose named entries
20
+
21
+ A detail or list page renders a `LayoutComposer`. It names the zone widgets target, the layout it uses,
22
+ and gives each section a stable entry id:
23
+
24
+ ```tsx
25
+ import { CORE_LAYOUT_IDS } from "@mercurjs/dashboard-sdk"
26
+ import { LayoutComposer, detailPageDefaultEntries } from "@mercurjs/dashboard-shared"
27
+
28
+ <LayoutComposer
29
+ widgetsZonePrefix="product.detail"
30
+ preferredLayoutId={CORE_LAYOUT_IDS.TWO_COLUMN}
31
+ data={product}
32
+ sections={{
33
+ main: (
34
+ <>
35
+ <LayoutComposer.Entry id="ProductGeneralSection">
36
+ <ProductGeneralSection product={product} />
37
+ </LayoutComposer.Entry>
38
+ {detailPageDefaultEntries(product)}
39
+ </>
40
+ ),
41
+ side: (
42
+ <LayoutComposer.Entry id="ProductOrganizationSection">
43
+ <ProductOrganizationSection product={product} />
44
+ </LayoutComposer.Entry>
45
+ ),
46
+ }}
47
+ />
48
+ ```
49
+
50
+ The page's own sections become entries `core:ProductGeneralSection`, `core:MetadataSection` and so on;
51
+ widgets become `widget:<id>`. Use the same component on your own pages and the build emits their zones and
52
+ entry ids into `extension-targets.d.ts`, so the next layer gets autocompletion for them.
53
+
54
+ ## Place a widget
55
+
56
+ `zone` is the page prefix or one of its sections. `section` and `order` place the widget among the page's
57
+ entries; core entries sit at order `0`, so a negative order renders before them and no order renders after.
58
+
59
+ ```tsx
60
+ export const config = defineWidgetConfig({
61
+ zone: "product.detail",
62
+ id: "seller-offers",
63
+ section: "side",
64
+ order: -1,
65
+ })
66
+ ```
67
+
68
+ `replace` takes the slot of a page entry. The widget receives `renderDefault` to render what it replaced:
69
+
70
+ ```tsx
71
+ export const config = defineWidgetConfig({
72
+ zone: "product.detail",
73
+ id: "general-with-seller",
74
+ replace: "core:ProductGeneralSection",
75
+ })
76
+
77
+ export default function GeneralWithSeller({ data, renderDefault }) {
78
+ return (
79
+ <>
80
+ {renderDefault?.()}
81
+ <SellerBadge product={data} />
82
+ </>
83
+ )
84
+ }
85
+ ```
86
+
87
+ The legacy `product.detail.side.before` and `.after` zones still work and map to the first and last
88
+ position of that section.
89
+
90
+ ## Hide, reorder and move entries: `_layouts.ts`
91
+
92
+ A layer's `_layouts.ts` holds per-zone preferences that apply to everyone. The sidebars are zones too
93
+ (`nav.main`, `nav.settings`) with `nav:<path>` entries, which replaces `_navigation.ts`.
94
+
95
+ ```ts
96
+ import { defineLayoutPreferences } from "@mercurjs/dashboard-sdk"
97
+ import { Tag } from "@medusajs/icons"
98
+
99
+ export default defineLayoutPreferences({
100
+ "product.detail": {
101
+ entries: {
102
+ "core:ProductMediaSection": { hidden: true },
103
+ "widget:seller-offers": { section: "main", order: 1 },
104
+ },
105
+ },
106
+ "nav.main": {
107
+ groups: [{ id: "finance", label: "Finance", rank: 5 }],
108
+ entries: {
109
+ "nav:/products": { label: "Catalog", icon: Tag, order: -1 },
110
+ "nav:/payouts": { section: "finance" },
111
+ "nav:/price-lists": { parent: null },
112
+ "nav:/settings/commissions": { hidden: true },
113
+ },
114
+ },
115
+ })
116
+ ```
117
+
118
+ ## Override a page
119
+
120
+ A `page.tsx` at a path an earlier layer already serves replaces it. The component receives `renderDefault`,
121
+ which renders the page it replaced:
122
+
123
+ ```tsx
124
+ export default function ProductDetail({ renderDefault }) {
125
+ return (
126
+ <>
127
+ <ApprovalBanner />
128
+ {renderDefault?.()}
129
+ </>
130
+ )
131
+ }
132
+ ```
133
+
134
+ ## Block the panel behind a flow
135
+
136
+ There is no app-wrapper seam, as in Medusa. A flow every member must complete before using the panel
137
+ (terms acceptance, a verification step) is a page plus a redirect: a `routes/<flow>/page.tsx` with
138
+ `config.public = true` so it renders in the bare public layout, and a widget on the `topbar` zone that
139
+ checks the status with `useQuery` and `navigate`s to the page while the flow is outstanding. The API keeps
140
+ enforcing the rule with a 403 for everything else.
141
+
142
+ ## Register a layout
143
+
144
+ A file under `layouts/` registers a layout other pages can pick. Augment `LayoutSectionRegistry` so the
145
+ `sections` prop is typed.
146
+
147
+ ```tsx
148
+ import { defineLayoutConfig } from "@mercurjs/dashboard-sdk"
149
+
150
+ export default defineLayoutConfig({
151
+ id: "erp:three-column",
152
+ sections: [{ id: "main" }, { id: "left" }, { id: "right" }],
153
+ Component: ({ sections }) => (
154
+ <div className="grid grid-cols-3 gap-3">
155
+ {sections.left}
156
+ {sections.main}
157
+ {sections.right}
158
+ </div>
159
+ ),
160
+ })
161
+ ```
162
+
163
+ ```ts
164
+ declare module "@mercurjs/dashboard-sdk" {
165
+ interface LayoutSectionRegistry {
166
+ "erp:three-column": "main" | "left" | "right"
167
+ }
168
+ }
169
+ ```
package/llms.txt CHANGED
@@ -206,6 +206,7 @@ package (`node_modules/@mercurjs/docs/`).
206
206
  - [Reference](content/references/overview.mdx) — Technical reference for Mercur's HTTP APIs, panel extensions, and configuration.
207
207
  - [Create a new page](content/references/panel-extensions/create-page.mdx) — Add a page to the admin or vendor panel with file-based routing, then register it in the sidebar.
208
208
  - [Custom Fields](content/references/panel-extensions/custom-fields.mdx) — Add fields, detail rows, section actions, and list columns to a built-in model.
209
+ - [Layouts and layers](content/references/panel-extensions/layouts.mdx) — How extensions from plugins and your project stack, how a page exposes its sections, and how to hide, reorder, replace or wrap what a page renders.
209
210
  - [Panel extensions](content/references/panel-extensions/overview.mdx) — Customize the admin and vendor panels with pages, widgets, custom fields, and navigation, without forking them.
210
211
  - [Widgets](content/references/panel-extensions/widgets.mdx) — Render a React component in a fixed slot on a built-in admin or vendor page.
211
212
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mercurjs/docs",
3
- "version": "2.3.8-canary.2",
3
+ "version": "2.3.8-canary.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/mercurjs/mercur",