@rakun-kit/manager-react 1.5.7 → 1.5.9
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 +300 -296
- package/dist/cjs/components/editor/themes/editor-theme.css +91 -91
- package/dist/cjs/components/ui/sortable.js +4 -4
- package/dist/cjs/router/dashboard/[contentType]/[edit]/ContentTypeEdit.js +6 -2
- package/dist/cjs/router/dashboard/[contentType]/[edit]/ContentTypeEdit.js.map +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.js +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.js.map +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/RelationField/NewRelation.js +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/RelationField/NewRelation.js.map +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/RelationField/index.js.map +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/seoBindings.js +36 -0
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/seoBindings.js.map +1 -0
- package/dist/cjs/state/theme-script.js +12 -12
- package/dist/docs/index.md +197 -191
- package/dist/esm/components/editor/themes/editor-theme.css +91 -91
- package/dist/esm/components/ui/sortable.js +4 -4
- package/dist/esm/i18n/useTranslations.d.ts.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/ContentTypeEdit.d.ts +2 -0
- package/dist/esm/router/dashboard/[contentType]/[edit]/ContentTypeEdit.d.ts.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/ContentTypeEdit.js +6 -2
- package/dist/esm/router/dashboard/[contentType]/[edit]/ContentTypeEdit.js.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.d.ts.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.js +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.js.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/RelationField/NewRelation.d.ts.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/RelationField/NewRelation.js +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/RelationField/NewRelation.js.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/RelationField/index.d.ts +1 -0
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/RelationField/index.d.ts.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/RelationField/index.js.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/seoBindings.d.ts +6 -0
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/seoBindings.d.ts.map +1 -0
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/seoBindings.js +32 -0
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/seoBindings.js.map +1 -0
- package/dist/esm/router/dashboard/[contentType]/[edit]/_hooks/useRouteLayoutData.d.ts.map +1 -1
- package/dist/esm/state/theme-script.js +12 -12
- package/dist/styles.css +7 -7
- package/dist/styles.css.d.ts +3 -3
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +217 -217
package/dist/docs/index.md
CHANGED
|
@@ -1,194 +1,200 @@
|
|
|
1
|
-
# `@rakun-kit/manager-react` AI usage manual
|
|
2
|
-
|
|
3
|
-
This package is the publishable React manager, its transport clients,
|
|
4
|
-
navigation abstraction, locale runtime, plugin API and CSS. Most Next.js apps
|
|
5
|
-
should use `@rakun-kit/next/manager`; use this package directly for custom
|
|
6
|
-
React hosts.
|
|
7
|
-
|
|
8
|
-
Read the core manual at
|
|
9
|
-
`node_modules/@rakun-kit/core/dist/docs/index.md` before changing manager/server
|
|
10
|
-
contracts.
|
|
11
|
-
|
|
12
|
-
## Install and mount
|
|
13
|
-
|
|
14
|
-
```sh
|
|
15
|
-
bun add @rakun-kit/manager-react react react-dom
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
```tsx
|
|
19
|
-
import { ManagerBrowserApp, createHttpManagerClient } from '@rakun-kit/manager-react'
|
|
20
|
-
import '@rakun-kit/manager-react/styles.css'
|
|
21
|
-
|
|
22
|
-
const client = createHttpManagerClient({ baseUrl: '/api/rakun' })
|
|
23
|
-
|
|
24
|
-
export function ManagerPage() {
|
|
25
|
-
return (
|
|
26
|
-
<ManagerBrowserApp client={client} pathname={window.location.pathname} basePath="/backend" />
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Import the stylesheet exactly once. `ManagerBrowserApp` owns browser-history
|
|
32
|
-
navigation. Use `ManagerRuntimeApp` when the host supplies navigation.
|
|
33
|
-
|
|
34
|
-
## Clients and navigation
|
|
35
|
-
|
|
36
|
-
- HTTP: `createHttpManagerClient` from `/client/http`.
|
|
37
|
-
- tRPC: `createTrpcManagerClient` from `/client/trpc`.
|
|
38
|
-
- Custom transport: `createManagerClient` from `/client/request`.
|
|
39
|
-
- Router bridge: `createPathManagerNavigation` from `/state/navigation`.
|
|
40
|
-
|
|
41
|
-
Use operation names and payloads from Rakun contracts; do not recreate endpoint
|
|
42
|
-
types in host code. Keep `baseUrl` (API) distinct from `basePath` (manager UI).
|
|
43
|
-
|
|
44
|
-
## Link field picker
|
|
45
|
-
|
|
46
|
-
The `f.link()` editor always shows Title and Destination inputs. A typed or
|
|
47
|
-
pasted URL is stored as `{ href, title }`. Opening the destination picker
|
|
48
|
-
exposes the home page and configured page-route types with their content type
|
|
49
|
-
icons; selecting an item fills an empty title from its manager label and stores
|
|
50
|
-
`{ routeId, contentTypeId, title }` so core can resolve localized paths.
|
|
51
|
-
Existing string and untitled reference values remain editable and are upgraded
|
|
52
|
-
to the titled shape when changed.
|
|
53
|
-
|
|
54
|
-
Compound arrays such as `f.array(f.link())` render each value as a reorderable
|
|
55
|
-
card in the Info editor. Use the drag handle to change their persisted order;
|
|
56
|
-
current nested input values are preserved while an item is moved.
|
|
57
|
-
|
|
58
|
-
## Media uploads
|
|
59
|
-
|
|
60
|
-
The manager preserves Unicode original file names when uploading media. Names
|
|
61
|
-
with accents, non-Latin scripts, or emoji are encoded into ASCII for the binary
|
|
62
|
-
upload headers and restored by core before metadata is saved.
|
|
63
|
-
|
|
64
|
-
When optimization is enabled, right-click an image and select `Reimport with
|
|
65
|
-
current optimization` to regenerate its optimized source, preview, and
|
|
66
|
-
responsive sizes. The action keeps the existing media ID, title, alt text,
|
|
67
|
-
folder, and content references. You can also multi-select images and use the
|
|
68
|
-
selection toolbar reimport action. Optimization is enabled by default in the
|
|
69
|
-
media library; generated previews are stored as inline `data:image/...`
|
|
70
|
-
strings on the media record for LQIP use.
|
|
71
|
-
|
|
72
|
-
Gallery and file-field image previews use `@rakun-kit/react` `Image` with a
|
|
73
|
-
layout-appropriate `sizes` value so responsive variants are selected when
|
|
74
|
-
available.
|
|
75
|
-
|
|
76
|
-
## Manager plugins
|
|
77
|
-
|
|
78
|
-
```tsx
|
|
79
|
-
import {
|
|
80
|
-
defineRakunManagerPlugin,
|
|
81
|
-
ManagerBrowserApp,
|
|
82
|
-
} from '@rakun-kit/manager-react'
|
|
83
|
-
|
|
84
|
-
const plugin = defineRakunManagerPlugin({
|
|
85
|
-
id: '@acme/analytics',
|
|
86
|
-
routes: [{
|
|
87
|
-
id: 'dashboard',
|
|
88
|
-
path: '/analytics',
|
|
89
|
-
component: AnalyticsScreen,
|
|
90
|
-
permissions: ['plugin.analytics.view'],
|
|
91
|
-
}],
|
|
92
|
-
sidebar: [{
|
|
93
|
-
id: 'analytics',
|
|
94
|
-
title: 'analytics.title',
|
|
95
|
-
routeId: 'dashboard',
|
|
96
|
-
position: 'primary',
|
|
97
|
-
group: 'Plugins',
|
|
98
|
-
}],
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
<ManagerBrowserApp plugins={[plugin]} {...props} />
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Plugins can contribute routes, sidebar entries, custom field editors and
|
|
105
|
-
RichText extensions. Use exported plugin types from
|
|
106
|
-
`@rakun-kit/manager-react/plugins`. Plugin ids, route ids, field editor ids,
|
|
107
|
-
Lexical node types and RichText plugin ids must be unique.
|
|
108
|
-
|
|
109
|
-
In Next.js, plugin registration belongs in a `'use client'` wrapper around
|
|
110
|
-
`RakunManagerClientPage`; pass the wrapper to `RakunManagerPage` as
|
|
111
|
-
`managerComponent`.
|
|
112
|
-
|
|
113
|
-
## Languages and copy
|
|
114
|
-
|
|
115
|
-
English is built in. Additional server-provided UI packs come from
|
|
116
|
-
`@rakun-kit/manager-locales` and are configured in core `managerLanguages`.
|
|
117
|
-
Content editing locale and manager UI locale are separate states.
|
|
118
|
-
|
|
119
|
-
Built-in manager copy must use the static manager translation catalog. Project
|
|
120
|
-
labels may use arbitrary project keys. Fields resolve `field.<fieldName>` and
|
|
121
|
-
layout slots resolve `layoutModule.<layoutKey>` with fallbacks.
|
|
122
|
-
|
|
123
|
-
## Content and template tabs
|
|
124
|
-
|
|
125
|
-
For a routeable content type with `iterator`, Content edits the `_iterator`
|
|
126
|
-
modules unique to the current document. Template is enabled automatically and
|
|
127
|
-
edits one shared composition for every document of that type. Both tabs offer
|
|
128
|
-
the same configured iterator modules. The Template module picker additionally
|
|
129
|
-
adds a built-in Content item at its root and inside blocks fields; it marks the
|
|
130
|
-
exact position where the current document's Content modules are inserted.
|
|
131
|
-
|
|
132
|
-
Template changes require the content type's `updateAny` permission and are
|
|
133
|
-
saved with optimistic revision checks. Preview includes unsaved changes from
|
|
134
|
-
both tabs.
|
|
135
|
-
|
|
1
|
+
# `@rakun-kit/manager-react` AI usage manual
|
|
2
|
+
|
|
3
|
+
This package is the publishable React manager, its transport clients,
|
|
4
|
+
navigation abstraction, locale runtime, plugin API and CSS. Most Next.js apps
|
|
5
|
+
should use `@rakun-kit/next/manager`; use this package directly for custom
|
|
6
|
+
React hosts.
|
|
7
|
+
|
|
8
|
+
Read the core manual at
|
|
9
|
+
`node_modules/@rakun-kit/core/dist/docs/index.md` before changing manager/server
|
|
10
|
+
contracts.
|
|
11
|
+
|
|
12
|
+
## Install and mount
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
bun add @rakun-kit/manager-react react react-dom
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { ManagerBrowserApp, createHttpManagerClient } from '@rakun-kit/manager-react'
|
|
20
|
+
import '@rakun-kit/manager-react/styles.css'
|
|
21
|
+
|
|
22
|
+
const client = createHttpManagerClient({ baseUrl: '/api/rakun' })
|
|
23
|
+
|
|
24
|
+
export function ManagerPage() {
|
|
25
|
+
return (
|
|
26
|
+
<ManagerBrowserApp client={client} pathname={window.location.pathname} basePath="/backend" />
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Import the stylesheet exactly once. `ManagerBrowserApp` owns browser-history
|
|
32
|
+
navigation. Use `ManagerRuntimeApp` when the host supplies navigation.
|
|
33
|
+
|
|
34
|
+
## Clients and navigation
|
|
35
|
+
|
|
36
|
+
- HTTP: `createHttpManagerClient` from `/client/http`.
|
|
37
|
+
- tRPC: `createTrpcManagerClient` from `/client/trpc`.
|
|
38
|
+
- Custom transport: `createManagerClient` from `/client/request`.
|
|
39
|
+
- Router bridge: `createPathManagerNavigation` from `/state/navigation`.
|
|
40
|
+
|
|
41
|
+
Use operation names and payloads from Rakun contracts; do not recreate endpoint
|
|
42
|
+
types in host code. Keep `baseUrl` (API) distinct from `basePath` (manager UI).
|
|
43
|
+
|
|
44
|
+
## Link field picker
|
|
45
|
+
|
|
46
|
+
The `f.link()` editor always shows Title and Destination inputs. A typed or
|
|
47
|
+
pasted URL is stored as `{ href, title }`. Opening the destination picker
|
|
48
|
+
exposes the home page and configured page-route types with their content type
|
|
49
|
+
icons; selecting an item fills an empty title from its manager label and stores
|
|
50
|
+
`{ routeId, contentTypeId, title }` so core can resolve localized paths.
|
|
51
|
+
Existing string and untitled reference values remain editable and are upgraded
|
|
52
|
+
to the titled shape when changed.
|
|
53
|
+
|
|
54
|
+
Compound arrays such as `f.array(f.link())` render each value as a reorderable
|
|
55
|
+
card in the Info editor. Use the drag handle to change their persisted order;
|
|
56
|
+
current nested input values are preserved while an item is moved.
|
|
57
|
+
|
|
58
|
+
## Media uploads
|
|
59
|
+
|
|
60
|
+
The manager preserves Unicode original file names when uploading media. Names
|
|
61
|
+
with accents, non-Latin scripts, or emoji are encoded into ASCII for the binary
|
|
62
|
+
upload headers and restored by core before metadata is saved.
|
|
63
|
+
|
|
64
|
+
When optimization is enabled, right-click an image and select `Reimport with
|
|
65
|
+
current optimization` to regenerate its optimized source, preview, and
|
|
66
|
+
responsive sizes. The action keeps the existing media ID, title, alt text,
|
|
67
|
+
folder, and content references. You can also multi-select images and use the
|
|
68
|
+
selection toolbar reimport action. Optimization is enabled by default in the
|
|
69
|
+
media library; generated previews are stored as inline `data:image/...`
|
|
70
|
+
strings on the media record for LQIP use.
|
|
71
|
+
|
|
72
|
+
Gallery and file-field image previews use `@rakun-kit/react` `Image` with a
|
|
73
|
+
layout-appropriate `sizes` value so responsive variants are selected when
|
|
74
|
+
available.
|
|
75
|
+
|
|
76
|
+
## Manager plugins
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import {
|
|
80
|
+
defineRakunManagerPlugin,
|
|
81
|
+
ManagerBrowserApp,
|
|
82
|
+
} from '@rakun-kit/manager-react'
|
|
83
|
+
|
|
84
|
+
const plugin = defineRakunManagerPlugin({
|
|
85
|
+
id: '@acme/analytics',
|
|
86
|
+
routes: [{
|
|
87
|
+
id: 'dashboard',
|
|
88
|
+
path: '/analytics',
|
|
89
|
+
component: AnalyticsScreen,
|
|
90
|
+
permissions: ['plugin.analytics.view'],
|
|
91
|
+
}],
|
|
92
|
+
sidebar: [{
|
|
93
|
+
id: 'analytics',
|
|
94
|
+
title: 'analytics.title',
|
|
95
|
+
routeId: 'dashboard',
|
|
96
|
+
position: 'primary',
|
|
97
|
+
group: 'Plugins',
|
|
98
|
+
}],
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
<ManagerBrowserApp plugins={[plugin]} {...props} />
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Plugins can contribute routes, sidebar entries, custom field editors and
|
|
105
|
+
RichText extensions. Use exported plugin types from
|
|
106
|
+
`@rakun-kit/manager-react/plugins`. Plugin ids, route ids, field editor ids,
|
|
107
|
+
Lexical node types and RichText plugin ids must be unique.
|
|
108
|
+
|
|
109
|
+
In Next.js, plugin registration belongs in a `'use client'` wrapper around
|
|
110
|
+
`RakunManagerClientPage`; pass the wrapper to `RakunManagerPage` as
|
|
111
|
+
`managerComponent`.
|
|
112
|
+
|
|
113
|
+
## Languages and copy
|
|
114
|
+
|
|
115
|
+
English is built in. Additional server-provided UI packs come from
|
|
116
|
+
`@rakun-kit/manager-locales` and are configured in core `managerLanguages`.
|
|
117
|
+
Content editing locale and manager UI locale are separate states.
|
|
118
|
+
|
|
119
|
+
Built-in manager copy must use the static manager translation catalog. Project
|
|
120
|
+
labels may use arbitrary project keys. Fields resolve `field.<fieldName>` and
|
|
121
|
+
layout slots resolve `layoutModule.<layoutKey>` with fallbacks.
|
|
122
|
+
|
|
123
|
+
## Content and template tabs
|
|
124
|
+
|
|
125
|
+
For a routeable content type with `iterator`, Content edits the `_iterator`
|
|
126
|
+
modules unique to the current document. Template is enabled automatically and
|
|
127
|
+
edits one shared composition for every document of that type. Both tabs offer
|
|
128
|
+
the same configured iterator modules. The Template module picker additionally
|
|
129
|
+
adds a built-in Content item at its root and inside blocks fields; it marks the
|
|
130
|
+
exact position where the current document's Content modules are inserted.
|
|
131
|
+
|
|
132
|
+
Template changes require the content type's `updateAny` permission and are
|
|
133
|
+
saved with optimistic revision checks. Preview includes unsaved changes from
|
|
134
|
+
both tabs.
|
|
135
|
+
|
|
136
136
|
## Dynamic data mappings
|
|
137
|
-
|
|
138
|
-
List bindings use a type-aware mapping editor. When a mapped target property is
|
|
139
|
-
itself a `blocks` field, choose `Nested list` to open another collection, query,
|
|
140
|
-
filter, and mapping editor for that property. Nested query conditions can select
|
|
141
|
-
`Current item` to read the parent source item at that level, or `Current
|
|
142
|
-
document` to read the root document. This supports mappings such as Category ->
|
|
143
|
-
gallery item -> Project -> image card, and the editor can repeat the flow for
|
|
137
|
+
|
|
138
|
+
List bindings use a type-aware mapping editor. When a mapped target property is
|
|
139
|
+
itself a `blocks` field, choose `Nested list` to open another collection, query,
|
|
140
|
+
filter, and mapping editor for that property. Nested query conditions can select
|
|
141
|
+
`Current item` to read the parent source item at that level, or `Current
|
|
142
|
+
document` to read the root document. This supports mappings such as Category ->
|
|
143
|
+
gallery item -> Project -> image card, and the editor can repeat the flow for
|
|
144
144
|
deeper block structures.
|
|
145
145
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
-
|
|
183
|
-
- `/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
-
|
|
188
|
-
|
|
189
|
-
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
-
|
|
194
|
-
|
|
146
|
+
In the SEO tab, `Current document` exposes compatible fields from the complete
|
|
147
|
+
document (including fields edited in Info), while the reserved `_seo` relation
|
|
148
|
+
itself stays excluded as a source. On create forms, string fields configured in
|
|
149
|
+
core with `.seo('<seoField>')` appear as normal preconfigured dynamic bindings;
|
|
150
|
+
editors can replace or clear them before the first save.
|
|
151
|
+
|
|
152
|
+
## Confirmations
|
|
153
|
+
|
|
154
|
+
Use the shared async confirm API instead of ad-hoc yes/no dialogs:
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { confirm, useConfirm } from '@rakun-kit/manager-react'
|
|
158
|
+
|
|
159
|
+
const result = await confirm({
|
|
160
|
+
title: 'Delete item',
|
|
161
|
+
description: 'This cannot be undone.',
|
|
162
|
+
confirmLabel: 'Delete',
|
|
163
|
+
variant: 'destructive',
|
|
164
|
+
onConfirm: async () => {
|
|
165
|
+
await deleteItem()
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// 'confirmed' | 'cancelled' | 'dismissed'
|
|
170
|
+
if (result !== 'confirmed') return
|
|
171
|
+
|
|
172
|
+
// Or boolean helper:
|
|
173
|
+
if (!(await confirm.yes({ title: 'Continue?' }))) return
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`ConfirmProvider` is mounted by `ManagerRuntimeApp` / `ManagerBrowserApp`.
|
|
177
|
+
`onConfirm` keeps the dialog open with a loading button until the promise
|
|
178
|
+
settles; thrown errors leave it open.
|
|
179
|
+
|
|
180
|
+
## Public entrypoints
|
|
181
|
+
|
|
182
|
+
- `@rakun-kit/manager-react`: manager apps, providers and common helpers.
|
|
183
|
+
- `/client/http`, `/client/request`, `/client/trpc`: manager transports.
|
|
184
|
+
- `/app/runtime-app`: runtime/browser app components.
|
|
185
|
+
- `/state/navigation`, `/state/theme`, `/link`: host integration primitives.
|
|
186
|
+
- `/plugins`, `/rich-text`: plugin and RichText extension contracts.
|
|
187
|
+
- `/i18n`: locale runtime, catalog and locale types.
|
|
188
|
+
- `confirm` / `useConfirm` / `ConfirmProvider`: shared async confirmation dialogs.
|
|
189
|
+
- `/styles.css`: required bundled styles.
|
|
190
|
+
|
|
191
|
+
## Agent constraints
|
|
192
|
+
|
|
193
|
+
- This package can run outside Next.js; do not assume Next navigation or server
|
|
194
|
+
components.
|
|
195
|
+
- Do not import the main React entrypoint into server bootstrap just to obtain
|
|
196
|
+
locale types or data.
|
|
197
|
+
- Permission-gate protected UI, and handle loading, empty and error states.
|
|
198
|
+
- Use existing manager components and operations before adding parallel APIs.
|
|
199
|
+
- Keep manager/core schemas synchronized when an operation changes.
|
|
200
|
+
- Do not hardcode user-facing manager copy.
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
.EditorTheme__code {
|
|
2
|
-
background-color: transparent;
|
|
3
|
-
font-family: Menlo, Consolas, Monaco, monospace;
|
|
4
|
-
display: block;
|
|
5
|
-
padding: 8px 8px 8px 52px;
|
|
6
|
-
line-height: 1.53;
|
|
7
|
-
font-size: 13px;
|
|
8
|
-
margin: 0;
|
|
9
|
-
margin-top: 8px;
|
|
10
|
-
margin-bottom: 8px;
|
|
11
|
-
overflow-x: auto;
|
|
12
|
-
border: 1px solid #ccc;
|
|
13
|
-
position: relative;
|
|
14
|
-
border-radius: 8px;
|
|
15
|
-
tab-size: 2;
|
|
16
|
-
}
|
|
17
|
-
.EditorTheme__code:before {
|
|
18
|
-
content: attr(data-gutter);
|
|
19
|
-
position: absolute;
|
|
20
|
-
background-color: transparent;
|
|
21
|
-
border-right: 1px solid #ccc;
|
|
22
|
-
left: 0;
|
|
23
|
-
top: 0;
|
|
24
|
-
padding: 8px;
|
|
25
|
-
color: #777;
|
|
26
|
-
white-space: pre-wrap;
|
|
27
|
-
text-align: right;
|
|
28
|
-
min-width: 25px;
|
|
29
|
-
}
|
|
30
|
-
.EditorTheme__table {
|
|
31
|
-
border-collapse: collapse;
|
|
32
|
-
border-spacing: 0;
|
|
33
|
-
overflow-y: scroll;
|
|
34
|
-
overflow-x: scroll;
|
|
35
|
-
table-layout: fixed;
|
|
36
|
-
width: fit-content;
|
|
37
|
-
width: 100%;
|
|
38
|
-
margin: 0px 0px 30px 0px;
|
|
39
|
-
}
|
|
40
|
-
.EditorTheme__tokenComment {
|
|
41
|
-
color: slategray;
|
|
42
|
-
}
|
|
43
|
-
.EditorTheme__tokenPunctuation {
|
|
44
|
-
color: #999;
|
|
45
|
-
}
|
|
46
|
-
.EditorTheme__tokenProperty {
|
|
47
|
-
color: #905;
|
|
48
|
-
}
|
|
49
|
-
.EditorTheme__tokenSelector {
|
|
50
|
-
color: #690;
|
|
51
|
-
}
|
|
52
|
-
.EditorTheme__tokenOperator {
|
|
53
|
-
color: #9a6e3a;
|
|
54
|
-
}
|
|
55
|
-
.EditorTheme__tokenAttr {
|
|
56
|
-
color: #07a;
|
|
57
|
-
}
|
|
58
|
-
.EditorTheme__tokenVariable {
|
|
59
|
-
color: #e90;
|
|
60
|
-
}
|
|
61
|
-
.EditorTheme__tokenFunction {
|
|
62
|
-
color: #dd4a68;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.Collapsible__container {
|
|
66
|
-
background-color: var(--background);
|
|
67
|
-
border: 1px solid #ccc;
|
|
68
|
-
border-radius: 0.5rem;
|
|
69
|
-
margin-bottom: 0.5rem;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.Collapsible__title{
|
|
73
|
-
padding: 0.25rem;
|
|
74
|
-
padding-left: 1rem;
|
|
75
|
-
position: relative;
|
|
76
|
-
font-weight: bold;
|
|
77
|
-
outline: none;
|
|
78
|
-
cursor: pointer;
|
|
79
|
-
list-style-type: disclosure-closed;
|
|
80
|
-
list-style-position: inside;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.Collapsible__title p{
|
|
84
|
-
display: inline-flex;
|
|
85
|
-
}
|
|
86
|
-
.Collapsible__title::marker{
|
|
87
|
-
color: lightgray;
|
|
88
|
-
}
|
|
89
|
-
.Collapsible__container[open] >.Collapsible__title {
|
|
90
|
-
list-style-type: disclosure-open;
|
|
91
|
-
}
|
|
1
|
+
.EditorTheme__code {
|
|
2
|
+
background-color: transparent;
|
|
3
|
+
font-family: Menlo, Consolas, Monaco, monospace;
|
|
4
|
+
display: block;
|
|
5
|
+
padding: 8px 8px 8px 52px;
|
|
6
|
+
line-height: 1.53;
|
|
7
|
+
font-size: 13px;
|
|
8
|
+
margin: 0;
|
|
9
|
+
margin-top: 8px;
|
|
10
|
+
margin-bottom: 8px;
|
|
11
|
+
overflow-x: auto;
|
|
12
|
+
border: 1px solid #ccc;
|
|
13
|
+
position: relative;
|
|
14
|
+
border-radius: 8px;
|
|
15
|
+
tab-size: 2;
|
|
16
|
+
}
|
|
17
|
+
.EditorTheme__code:before {
|
|
18
|
+
content: attr(data-gutter);
|
|
19
|
+
position: absolute;
|
|
20
|
+
background-color: transparent;
|
|
21
|
+
border-right: 1px solid #ccc;
|
|
22
|
+
left: 0;
|
|
23
|
+
top: 0;
|
|
24
|
+
padding: 8px;
|
|
25
|
+
color: #777;
|
|
26
|
+
white-space: pre-wrap;
|
|
27
|
+
text-align: right;
|
|
28
|
+
min-width: 25px;
|
|
29
|
+
}
|
|
30
|
+
.EditorTheme__table {
|
|
31
|
+
border-collapse: collapse;
|
|
32
|
+
border-spacing: 0;
|
|
33
|
+
overflow-y: scroll;
|
|
34
|
+
overflow-x: scroll;
|
|
35
|
+
table-layout: fixed;
|
|
36
|
+
width: fit-content;
|
|
37
|
+
width: 100%;
|
|
38
|
+
margin: 0px 0px 30px 0px;
|
|
39
|
+
}
|
|
40
|
+
.EditorTheme__tokenComment {
|
|
41
|
+
color: slategray;
|
|
42
|
+
}
|
|
43
|
+
.EditorTheme__tokenPunctuation {
|
|
44
|
+
color: #999;
|
|
45
|
+
}
|
|
46
|
+
.EditorTheme__tokenProperty {
|
|
47
|
+
color: #905;
|
|
48
|
+
}
|
|
49
|
+
.EditorTheme__tokenSelector {
|
|
50
|
+
color: #690;
|
|
51
|
+
}
|
|
52
|
+
.EditorTheme__tokenOperator {
|
|
53
|
+
color: #9a6e3a;
|
|
54
|
+
}
|
|
55
|
+
.EditorTheme__tokenAttr {
|
|
56
|
+
color: #07a;
|
|
57
|
+
}
|
|
58
|
+
.EditorTheme__tokenVariable {
|
|
59
|
+
color: #e90;
|
|
60
|
+
}
|
|
61
|
+
.EditorTheme__tokenFunction {
|
|
62
|
+
color: #dd4a68;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.Collapsible__container {
|
|
66
|
+
background-color: var(--background);
|
|
67
|
+
border: 1px solid #ccc;
|
|
68
|
+
border-radius: 0.5rem;
|
|
69
|
+
margin-bottom: 0.5rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.Collapsible__title{
|
|
73
|
+
padding: 0.25rem;
|
|
74
|
+
padding-left: 1rem;
|
|
75
|
+
position: relative;
|
|
76
|
+
font-weight: bold;
|
|
77
|
+
outline: none;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
list-style-type: disclosure-closed;
|
|
80
|
+
list-style-position: inside;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.Collapsible__title p{
|
|
84
|
+
display: inline-flex;
|
|
85
|
+
}
|
|
86
|
+
.Collapsible__title::marker{
|
|
87
|
+
color: lightgray;
|
|
88
|
+
}
|
|
89
|
+
.Collapsible__container[open] >.Collapsible__title {
|
|
90
|
+
list-style-type: disclosure-open;
|
|
91
|
+
}
|
|
@@ -127,10 +127,10 @@ function Sortable(props) {
|
|
|
127
127
|
},
|
|
128
128
|
}), [value]);
|
|
129
129
|
const screenReaderInstructions = React.useMemo(() => ({
|
|
130
|
-
draggable: `
|
|
131
|
-
To pick up a sortable item, press space or enter.
|
|
132
|
-
While dragging, use the ${orientation === "vertical" ? "up and down" : orientation === "horizontal" ? "left and right" : "arrow"} keys to move the item.
|
|
133
|
-
Press space or enter again to drop the item in its new position, or press escape to cancel.
|
|
130
|
+
draggable: `
|
|
131
|
+
To pick up a sortable item, press space or enter.
|
|
132
|
+
While dragging, use the ${orientation === "vertical" ? "up and down" : orientation === "horizontal" ? "left and right" : "arrow"} keys to move the item.
|
|
133
|
+
Press space or enter again to drop the item in its new position, or press escape to cancel.
|
|
134
134
|
`,
|
|
135
135
|
}), [orientation]);
|
|
136
136
|
const contextValue = React.useMemo(() => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTranslations.d.ts","sourceRoot":"","sources":["../../../src/i18n/useTranslations.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe;
|
|
1
|
+
{"version":3,"file":"useTranslations.d.ts","sourceRoot":"","sources":["../../../src/i18n/useTranslations.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe;iJAI6gB,CAAC;;CADziB,CAAA"}
|
|
@@ -9,6 +9,7 @@ type FieldComponentProps = EncodedFieldUnknown & {
|
|
|
9
9
|
ref: React.Ref<FieldRef>;
|
|
10
10
|
collapsible?: boolean;
|
|
11
11
|
parentContentType?: EncodedContentType;
|
|
12
|
+
initializeSeoBindings?: boolean;
|
|
12
13
|
};
|
|
13
14
|
type FieldComponent = (config: FieldComponentProps) => React.ReactElement;
|
|
14
15
|
export declare const fieldsMap: { [key in FieldType]: FieldComponent; };
|
|
@@ -24,6 +25,7 @@ declare const ContentTypeEdit: import("react").ForwardRefExoticComponent<{
|
|
|
24
25
|
[key: string]: FieldValue;
|
|
25
26
|
};
|
|
26
27
|
parentContentType?: EncodedContentType;
|
|
28
|
+
initializeSeoBindings?: boolean;
|
|
27
29
|
collapsible?: boolean;
|
|
28
30
|
hideTitle?: boolean;
|
|
29
31
|
} & import("react").RefAttributes<ManagerFieldEditorRef>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContentTypeEdit.d.ts","sourceRoot":"","sources":["../../../../../../src/router/dashboard/[contentType]/[edit]/ContentTypeEdit.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAA2B,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAavE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAsB7C,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"ContentTypeEdit.d.ts","sourceRoot":"","sources":["../../../../../../src/router/dashboard/[contentType]/[edit]/ContentTypeEdit.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAA2B,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAavE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAsB7C,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAA;AAclB,KAAK,mBAAmB,GAAG,mBAAmB,GAAG;IAC/C,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,CAAC,EAAE,UAAU,CAAA;IACxB,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACxB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,iBAAiB,CAAC,EAAE,kBAAkB,CAAA;IACtC,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC,CAAA;AAED,KAAK,cAAc,GAAG,CAAC,MAAM,EAAE,mBAAmB,KAAK,KAAK,CAAC,YAAY,CAAA;AAoEzE,eAAO,MAAM,SAAS,EAYjB,GACF,GAAG,IAAI,SAAS,GAAG,cAAc,GACnC,CAAA;AAED,wBAAgB,YAAY,CAAC,CAAC;;oBAIL,MAAM,UAFM,CAAC,GAAG,IAAI,KAAK,IAAI;EAcrD;AAED,MAAM,MAAM,QAAQ,GAAG,qBAAqB,CAAA;AAwC5C,QAAA,MAAM,eAAe;iBAGJ,kBAAkB;QAC3B,MAAM;kBACI;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE;wBACvB,kBAAkB;4BACd,OAAO;kBACjB,OAAO;gBACT,OAAO;yDA2QrB,CAAA;AAEF,eAAe,eAAe,CAAA"}
|
|
@@ -28,6 +28,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '../../../../components/
|
|
|
28
28
|
import { DynamicDataControl, isDynamicFallbackRequired, isDynamicFieldEnabled, } from './_fields/DynamicDataControl.js';
|
|
29
29
|
import { useManagerPlugins, } from '../../../../plugins.js';
|
|
30
30
|
import MissingUI from './_fields/Missing.js';
|
|
31
|
+
import { getInitialSeoBindings } from './_fields/seoBindings.js';
|
|
31
32
|
const REQUIRED_MARK = '*';
|
|
32
33
|
const defaultDataExtractor = (fieldName, defaultData) => {
|
|
33
34
|
if (!defaultData) {
|
|
@@ -119,7 +120,10 @@ const ContentTypeEdit = forwardRef((props, ref) => {
|
|
|
119
120
|
])), [contentType.fields, props.defaultData]);
|
|
120
121
|
const [formState, setFormState] = useState(formStateInitialValue);
|
|
121
122
|
const conditionFieldState = useMemo(() => mergeConditionFieldState(props.defaultData, formState), [formState, props.defaultData]);
|
|
122
|
-
const [dynamicBindings, setDynamicBindings] = useState(getDefaultBindings(props.defaultData)
|
|
123
|
+
const [dynamicBindings, setDynamicBindings] = useState(() => getDefaultBindings(props.defaultData) ??
|
|
124
|
+
(props.initializeSeoBindings
|
|
125
|
+
? getInitialSeoBindings({ contentType, parentContentType: props.parentContentType })
|
|
126
|
+
: undefined));
|
|
123
127
|
const [dynamicEditorOpen, setDynamicEditorOpen] = useState(null);
|
|
124
128
|
const allItems = useMemo(() => Object.entries(contentType.fields).filter(([_, fieldValue]) => !(fieldValue.visibility === 'api')), [contentType.fields]);
|
|
125
129
|
const visibleItems = useMemo(() => allItems.filter(([, fieldValue]) => evaluateFieldCondition(fieldValue.condition, formState)), [allItems, formState]);
|
|
@@ -187,7 +191,7 @@ const ContentTypeEdit = forwardRef((props, ref) => {
|
|
|
187
191
|
? dynamicBindings?.lists?.[fieldName]
|
|
188
192
|
: dynamicBindings?.fields?.[fieldName];
|
|
189
193
|
const dynamicFallbackPlaceholder = showDynamicData && dynamicBinding ? t('contentEdit.fallbackValue') : undefined;
|
|
190
|
-
const field = FieldComponent ? (_jsx(FieldComponent, { id: id + '.' + fieldName, ref: setRef(i), ...fieldValue, isRequired: isDynamicFallbackRequired(fieldValue, dynamicBinding), defaultData: defaultDataExtractor(fieldName, props.defaultData), dynamicFallbackPlaceholder: dynamicFallbackPlaceholder, parentContentType: dynamicSourceContentType })) : (_jsx(MissingUI, { field: fieldValue.config }));
|
|
194
|
+
const field = FieldComponent ? (_jsx(FieldComponent, { id: id + '.' + fieldName, ref: setRef(i), ...fieldValue, isRequired: isDynamicFallbackRequired(fieldValue, dynamicBinding), defaultData: defaultDataExtractor(fieldName, props.defaultData), dynamicFallbackPlaceholder: dynamicFallbackPlaceholder, parentContentType: dynamicSourceContentType, initializeSeoBindings: props.initializeSeoBindings })) : (_jsx(MissingUI, { field: fieldValue.config }));
|
|
191
195
|
const dynamicOpen = dynamicEditorOpen === fieldName;
|
|
192
196
|
const setDynamicOpen = (open) => setDynamicEditorOpen(open ? fieldName : null);
|
|
193
197
|
const dynamicTrigger = showDynamicData ? (_jsx(DynamicDataControl, { contentType: contentType, documentContentType: dynamicSourceContentType, fieldName: fieldName, field: fieldValue, contentTypes: (contentTypesData ?? []), bindings: dynamicBindings, onChange: (bindings) => {
|