@mercurjs/docs 2.3.8-canary.1 → 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
|
|