@nitrogenbuilder/connector-payload 0.1.19 → 0.1.20

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.
@@ -1,5 +1,4 @@
1
- import React from "react";
2
- export declare const LockedJsonField: React.FC<{
1
+ export declare const LockedJsonField: import("react").ComponentType<{
3
2
  path: string;
4
3
  field: {
5
4
  name: string;
@@ -1,38 +1,6 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState } from "react";
4
- import { useField } from "@payloadcms/ui";
5
- export const LockedJsonField = ({ path, field }) => {
6
- const [unlocked, setUnlocked] = useState(false);
7
- const { value, setValue } = useField({ path });
8
- const displayValue = value ? JSON.stringify(value, null, 2) : "";
9
- return (_jsxs("div", { style: { marginBottom: "1.5rem" }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "0.5rem" }, children: [_jsx("label", { style: { fontWeight: 500 }, children: field.name }), _jsx("button", { type: "button", onClick: () => setUnlocked(!unlocked), style: {
10
- padding: "4px 12px",
11
- fontSize: "12px",
12
- background: unlocked ? "#ef4444" : "#6b7280",
13
- color: "#fff",
14
- border: "none",
15
- borderRadius: "4px",
16
- cursor: "pointer",
17
- }, children: unlocked ? "Lock" : "Unlock Edit" })] }), _jsx("textarea", { value: displayValue, readOnly: !unlocked, onChange: (e) => {
18
- if (!unlocked)
19
- return;
20
- try {
21
- setValue(JSON.parse(e.target.value));
22
- }
23
- catch {
24
- // Allow typing invalid JSON while editing
25
- }
26
- }, style: {
27
- width: "100%",
28
- minHeight: "120px",
29
- fontFamily: "monospace",
30
- fontSize: "12px",
31
- padding: "8px",
32
- background: unlocked ? "#1a1a2e" : "#111",
33
- color: unlocked ? "#e2e8f0" : "#888",
34
- border: `1px solid ${unlocked ? "#ef4444" : "#333"}`,
35
- borderRadius: "4px",
36
- resize: "vertical",
37
- } }), field.admin?.description && (_jsx("p", { style: { fontSize: "12px", color: "#888", marginTop: "4px" }, children: field.admin.description }))] }));
38
- };
1
+ 'use client';
2
+ import dynamic from 'next/dynamic';
3
+ export const LockedJsonField = dynamic(() => import('./LockedJsonFieldRuntime').then((module) => module.LockedJsonFieldRuntime), {
4
+ loading: () => null,
5
+ ssr: false,
6
+ });
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ export declare const LockedJsonFieldRuntime: React.FC<{
3
+ path: string;
4
+ field: {
5
+ name: string;
6
+ admin?: {
7
+ description?: string;
8
+ };
9
+ };
10
+ }>;
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { useField } from '@payloadcms/ui';
5
+ export const LockedJsonFieldRuntime = ({ path, field }) => {
6
+ const [unlocked, setUnlocked] = useState(false);
7
+ const { value, setValue } = useField({ path });
8
+ const displayValue = value ? JSON.stringify(value, null, 2) : '';
9
+ return (_jsxs("div", { style: { marginBottom: '1.5rem' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.5rem' }, children: [_jsx("label", { style: { fontWeight: 500 }, children: field.name }), _jsx("button", { type: "button", onClick: () => setUnlocked(!unlocked), style: {
10
+ padding: '4px 12px',
11
+ fontSize: '12px',
12
+ background: unlocked ? '#ef4444' : '#6b7280',
13
+ color: '#fff',
14
+ border: 'none',
15
+ borderRadius: '4px',
16
+ cursor: 'pointer',
17
+ }, children: unlocked ? 'Lock' : 'Unlock Edit' })] }), _jsx("textarea", { value: displayValue, readOnly: !unlocked, onChange: (e) => {
18
+ if (!unlocked)
19
+ return;
20
+ try {
21
+ setValue(JSON.parse(e.target.value));
22
+ }
23
+ catch {
24
+ // Allow typing invalid JSON while editing
25
+ }
26
+ }, style: {
27
+ width: '100%',
28
+ minHeight: '120px',
29
+ fontFamily: 'monospace',
30
+ fontSize: '12px',
31
+ padding: '8px',
32
+ background: unlocked ? '#1a1a2e' : '#111',
33
+ color: unlocked ? '#e2e8f0' : '#888',
34
+ border: `1px solid ${unlocked ? '#ef4444' : '#333'}`,
35
+ borderRadius: '4px',
36
+ resize: 'vertical',
37
+ } }), field.admin?.description && (_jsx("p", { style: { fontSize: '12px', color: '#888', marginTop: '4px' }, children: field.admin.description }))] }));
38
+ };
@@ -1,361 +1,7 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useCallback } from 'react';
4
- import { Collapsible, useField } from '@payloadcms/ui';
5
- function updateAtPath(root, path, newValue) {
6
- if (path.length === 0)
7
- return newValue;
8
- const [head, ...rest] = path;
9
- if (Array.isArray(root)) {
10
- const copy = [...root];
11
- copy[head] = updateAtPath(copy[head], rest, newValue);
12
- return copy;
13
- }
14
- if (typeof root === 'object' && root !== null) {
15
- const obj = root;
16
- return { ...obj, [head]: updateAtPath(obj[head], rest, newValue) };
17
- }
18
- return root;
19
- }
20
- function deleteAtPath(root, path) {
21
- if (path.length === 0)
22
- return root;
23
- if (path.length === 1) {
24
- const [head] = path;
25
- if (Array.isArray(root))
26
- return root.filter((_, i) => i !== head);
27
- if (typeof root === 'object' && root !== null) {
28
- const { [head]: _removed, ...rest } = root;
29
- return rest;
30
- }
31
- return root;
32
- }
33
- const [head, ...rest] = path;
34
- if (Array.isArray(root)) {
35
- const copy = [...root];
36
- copy[head] = deleteAtPath(copy[head], rest);
37
- return copy;
38
- }
39
- if (typeof root === 'object' && root !== null) {
40
- const obj = root;
41
- return { ...obj, [head]: deleteAtPath(obj[head], rest) };
42
- }
43
- return root;
44
- }
45
- function renameKeyAtPath(root, path, newKey) {
46
- if (path.length === 0)
47
- return root;
48
- const parentPath = path.slice(0, -1);
49
- const oldKey = path[path.length - 1];
50
- function navigate(node, remaining) {
51
- if (remaining.length === 0) {
52
- if (typeof node === 'object' && node !== null && !Array.isArray(node)) {
53
- const obj = node;
54
- const result = {};
55
- for (const [k, v] of Object.entries(obj)) {
56
- result[k === oldKey ? newKey : k] = v;
57
- }
58
- return result;
59
- }
60
- return node;
61
- }
62
- const [head, ...rest] = remaining;
63
- if (Array.isArray(node)) {
64
- const copy = [...node];
65
- copy[head] = navigate(copy[head], rest);
66
- return copy;
67
- }
68
- if (typeof node === 'object' && node !== null) {
69
- const obj = node;
70
- return { ...obj, [head]: navigate(obj[head], rest) };
71
- }
72
- return node;
73
- }
74
- return navigate(root, parentPath);
75
- }
76
- function addChildAtPath(root, path) {
77
- function getAt(node, p) {
78
- if (p.length === 0)
79
- return node;
80
- const [head, ...rest] = p;
81
- if (Array.isArray(node))
82
- return getAt(node[head], rest);
83
- if (typeof node === 'object' && node !== null)
84
- return getAt(node[head], rest);
85
- return undefined;
86
- }
87
- const target = getAt(root, path);
88
- if (Array.isArray(target)) {
89
- return updateAtPath(root, path, [...target, '']);
90
- }
91
- if (typeof target === 'object' && target !== null) {
92
- const obj = target;
93
- let key = 'newKey';
94
- let i = 1;
95
- while (key in obj)
96
- key = `newKey${i++}`;
97
- return updateAtPath(root, path, { ...obj, [key]: '' });
98
- }
99
- return root;
100
- }
101
- // ── Inline text input for editing values and keys ─────────────────────
102
- function InlineEdit({ display, onCommit, colorClass, }) {
103
- const [draft, setDraft] = useState(display);
104
- const commit = () => onCommit(draft);
105
- return (_jsx("input", { autoFocus: true, value: draft, onChange: (e) => setDraft(e.target.value), onBlur: commit, onKeyDown: (e) => {
106
- if (e.key === 'Enter') {
107
- e.preventDefault();
108
- commit();
109
- }
110
- if (e.key === 'Escape') {
111
- onCommit(display);
112
- } // cancel
113
- }, onClick: (e) => e.stopPropagation(), className: colorClass, style: {
114
- background: 'transparent',
115
- border: 'none',
116
- borderBottom: '1px solid currentColor',
117
- color: 'inherit',
118
- font: 'inherit',
119
- padding: '0 2px',
120
- outline: 'none',
121
- minWidth: '4ch',
122
- width: `${Math.max(draft.length + 2, 4)}ch`,
123
- maxWidth: '500px',
124
- } }));
125
- }
126
- // ── Small action button (delete / add) ────────────────────────────────
127
- function ActionBtn({ label, color, onClick, }) {
128
- return (_jsx("button", { type: "button", onClick: onClick, style: {
129
- marginLeft: 6,
130
- padding: '0 5px',
131
- fontSize: '10px',
132
- lineHeight: '16px',
133
- background: color,
134
- color: '#fff',
135
- border: 'none',
136
- borderRadius: '3px',
137
- cursor: 'pointer',
138
- flexShrink: 0,
139
- opacity: 0.85,
140
- }, children: label }));
141
- }
142
- function JsonNode({ k, value, depth, forceOpen, path, unlocked, callbacks, }) {
143
- const defaultOpen = forceOpen !== null ? forceOpen : depth < 2;
144
- const [open, setOpen] = useState(defaultOpen);
145
- const [editingValue, setEditingValue] = useState(false);
146
- const [editingKey, setEditingKey] = useState(false);
147
- const { onUpdate, onDelete, onRenameKey, onAddChild } = callbacks;
148
- const del = unlocked ? (_jsx(ActionBtn, { label: "\u00D7", color: "#ef4444", onClick: (e) => { e.stopPropagation(); onDelete(path); } })) : null;
149
- // Key display — clicking it opens inline edit when unlocked
150
- const keyEl = k === undefined ? null : (_jsxs(_Fragment, { children: [_jsxs("span", { className: "njv-key", children: ["\"", unlocked && editingKey ? (_jsx(InlineEdit, { display: k, colorClass: "njv-key", onCommit: (newKey) => {
151
- setEditingKey(false);
152
- if (newKey !== k)
153
- onRenameKey(path, newKey);
154
- } })) : (_jsx("span", { onClick: unlocked ? (e) => { e.stopPropagation(); setEditingKey(true); } : undefined, style: unlocked ? { cursor: 'text', borderBottom: '1px dotted currentColor' } : undefined, children: k })), "\""] }), _jsx("span", { className: "njv-brace", children: ": " })] }));
155
- // ── null ──
156
- if (value === null) {
157
- return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyEl, _jsx("span", { className: "njv-null", title: unlocked ? 'Click to convert to empty string' : undefined, onClick: unlocked ? (e) => { e.stopPropagation(); onUpdate(path, ''); } : undefined, style: unlocked ? { cursor: 'pointer', borderBottom: '1px dotted currentColor' } : undefined, children: "null" })] }), del] }));
158
- }
159
- // ── string ──
160
- if (typeof value === 'string') {
161
- return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyEl, _jsxs("span", { className: "njv-string", children: ["\"", unlocked && editingValue ? (_jsx(InlineEdit, { display: value, colorClass: "njv-string", onCommit: (v) => { setEditingValue(false); onUpdate(path, v); } })) : (_jsx("span", { onClick: unlocked ? (e) => { e.stopPropagation(); setEditingValue(true); } : undefined, style: unlocked ? { cursor: 'text', borderBottom: '1px dotted currentColor' } : undefined, children: value.length > 120 ? value.slice(0, 120) + '…' : value })), "\""] })] }), del] }));
162
- }
163
- // ── number ──
164
- if (typeof value === 'number') {
165
- return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyEl, _jsx("span", { className: "njv-number", children: unlocked && editingValue ? (_jsx(InlineEdit, { display: String(value), colorClass: "njv-number", onCommit: (v) => {
166
- setEditingValue(false);
167
- const n = Number(v);
168
- onUpdate(path, isNaN(n) ? v : n);
169
- } })) : (_jsx("span", { onClick: unlocked ? (e) => { e.stopPropagation(); setEditingValue(true); } : undefined, style: unlocked ? { cursor: 'text', borderBottom: '1px dotted currentColor' } : undefined, children: value })) })] }), del] }));
170
- }
171
- // ── boolean — click to toggle ──
172
- if (typeof value === 'boolean') {
173
- return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyEl, _jsx("span", { className: "njv-boolean", onClick: unlocked ? (e) => { e.stopPropagation(); onUpdate(path, !value); } : undefined, title: unlocked ? 'Click to toggle' : undefined, style: unlocked ? { cursor: 'pointer', borderBottom: '1px dotted currentColor' } : undefined, children: String(value) })] }), del] }));
174
- }
175
- // ── array ──
176
- if (Array.isArray(value)) {
177
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20, cursor: 'pointer', userSelect: 'none' }, onClick: () => setOpen((o) => !o), children: [_jsx("span", { className: "njv-arrow", children: open ? '▼' : '▶' }), keyEl, _jsx("span", { className: "njv-brace", children: "[" }), !open && (_jsxs(_Fragment, { children: [_jsxs("span", { className: "njv-summary", children: [" ", value.length, " items "] }), _jsx("span", { className: "njv-brace", children: "]" })] }))] }), del] }), open && (_jsxs(_Fragment, { children: [value.map((item, i) => (_jsx(JsonNode, { value: item, depth: depth + 1, forceOpen: forceOpen, path: [...path, i], unlocked: unlocked, callbacks: callbacks }, i))), _jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsx("span", { className: "njv-code", style: { paddingLeft: (depth + 1) * 20 }, children: _jsx("span", { className: "njv-brace", children: "]" }) }), unlocked && (_jsx(ActionBtn, { label: "+ item", color: "#22c55e", onClick: (e) => { e.stopPropagation(); onAddChild(path); } }))] })] }))] }));
178
- }
179
- // ── object ──
180
- if (typeof value === 'object') {
181
- const entries = Object.entries(value);
182
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20, cursor: 'pointer', userSelect: 'none' }, onClick: () => setOpen((o) => !o), children: [_jsx("span", { className: "njv-arrow", children: open ? '▼' : '▶' }), keyEl, _jsx("span", { className: "njv-brace", children: '{' }), !open && (_jsxs(_Fragment, { children: [_jsxs("span", { className: "njv-summary", children: [" ", entries.length, " keys "] }), _jsx("span", { className: "njv-brace", children: '}' })] }))] }), del] }), open && (_jsxs(_Fragment, { children: [entries.map(([ek, ev]) => (_jsx(JsonNode, { k: ek, value: ev, depth: depth + 1, forceOpen: forceOpen, path: [...path, ek], unlocked: unlocked, callbacks: callbacks }, ek))), _jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsx("span", { className: "njv-code", style: { paddingLeft: (depth + 1) * 20 }, children: _jsx("span", { className: "njv-brace", children: '}' }) }), unlocked && (_jsx(ActionBtn, { label: "+ key", color: "#22c55e", onClick: (e) => { e.stopPropagation(); onAddChild(path); } }))] })] }))] }));
183
- }
184
- return null;
185
- }
186
- // ── Injected styles ───────────────────────────────────────────────────
187
- const stylesId = 'nitrogen-json-viewer-styles';
188
- function ensureStyles() {
189
- if (typeof document === 'undefined')
190
- return;
191
- if (document.getElementById(stylesId))
192
- return;
193
- const style = document.createElement('style');
194
- style.id = stylesId;
195
- style.textContent = `
196
- .njv-root {
197
- --njv-bg: var(--theme-elevation-50, #f8f8f8);
198
- --njv-border: var(--theme-elevation-150, #ddd);
199
- --njv-toolbar-bg: var(--theme-elevation-100, #eee);
200
- --njv-gutter-bg: var(--theme-elevation-50, #f0f0f0);
201
- --njv-gutter-color: var(--theme-elevation-400, #999);
202
- --njv-gutter-border: var(--theme-elevation-150, #ddd);
203
- --njv-key: #0451a5;
204
- --njv-string: #a31515;
205
- --njv-number: #098658;
206
- --njv-boolean: #0000ff;
207
- --njv-null: #0000ff;
208
- --njv-brace: var(--theme-elevation-800, #333);
209
- --njv-arrow: var(--theme-elevation-500, #888);
210
- --njv-summary: var(--theme-elevation-400, #888);
211
-
212
- .collapsible__content { padding: 0; }
213
- }
214
-
215
- [data-theme="dark"] .njv-root {
216
- --njv-key: #9cdcfe;
217
- --njv-string: #ce9178;
218
- --njv-number: #b5cea8;
219
- --njv-boolean: #569cd6;
220
- --njv-null: #569cd6;
221
- }
222
-
223
- @media (prefers-color-scheme: dark) {
224
- .njv-root:not([data-theme="light"] .njv-root) {
225
- --njv-key: #9cdcfe;
226
- --njv-string: #ce9178;
227
- --njv-number: #b5cea8;
228
- --njv-boolean: #569cd6;
229
- --njv-null: #569cd6;
230
- }
231
- }
232
-
233
- .njv-key { color: var(--njv-key); }
234
- .njv-string { color: var(--njv-string); word-break: break-all; white-space: normal; }
235
- .njv-number { color: var(--njv-number); }
236
- .njv-boolean { color: var(--njv-boolean); }
237
- .njv-null { color: var(--njv-null); font-style: italic; }
238
- .njv-brace { color: var(--njv-brace); }
239
- .njv-arrow {
240
- color: var(--njv-arrow);
241
- display: inline-block;
242
- width: 14px;
243
- font-size: 9px;
244
- margin-right: 4px;
245
- text-align: center;
246
- }
247
- .njv-summary { color: var(--njv-summary); font-style: italic; }
248
-
249
- .njv-body { counter-reset: njv-line; }
250
-
251
- .njv-line {
252
- display: flex;
253
- align-items: baseline;
254
- min-height: 24px;
255
- line-height: 24px;
256
- counter-increment: njv-line;
257
- }
258
-
259
- .njv-line::before {
260
- content: counter(njv-line);
261
- flex-shrink: 0;
262
- width: 44px;
263
- text-align: right;
264
- padding-right: 12px;
265
- color: var(--njv-gutter-color);
266
- background: var(--njv-gutter-bg);
267
- border-right: 1px solid var(--njv-gutter-border);
268
- user-select: none;
269
- position: sticky;
270
- left: 0;
271
- z-index: 1;
272
- }
273
-
274
- .njv-code {
275
- padding-left: 12px;
276
- white-space: nowrap;
277
- flex: 1;
278
- }
279
-
280
- .njv-toolbar {
281
- display: flex;
282
- gap: 8px 12px;
283
- padding: 0 12px;
284
- border-bottom: 1px solid var(--njv-border);
285
- background: var(--njv-toolbar-bg);
286
- button { margin-block: 12px; }
287
- }
288
- `;
289
- document.head.appendChild(style);
290
- }
291
- // ── Main component ────────────────────────────────────────────────────
292
- const NitrogenDataViewer = ({ path, field }) => {
293
- const { value, setValue } = useField({ path });
294
- const [copied, setCopied] = useState(false);
295
- const [pasted, setPasted] = useState(false);
296
- const [forceOpen, setForceOpen] = useState(null);
297
- const [treeKey, setTreeKey] = useState(0);
298
- const [unlocked, setUnlocked] = useState(false);
299
- ensureStyles();
300
- let parsed = [];
301
- try {
302
- parsed = typeof value === 'string' ? JSON.parse(value) : value;
303
- }
304
- catch {
305
- parsed = [];
306
- }
307
- const jsonString = value != null ? JSON.stringify(value, null, 2) : '[]';
308
- const moduleCount = Array.isArray(parsed) ? parsed.length : null;
309
- const handleCopy = useCallback(() => {
310
- navigator.clipboard.writeText(jsonString).then(() => {
311
- setCopied(true);
312
- setTimeout(() => setCopied(false), 2000);
313
- });
314
- }, [jsonString]);
315
- const handlePaste = useCallback(() => {
316
- navigator.clipboard.readText().then((text) => {
317
- try {
318
- const parsed = JSON.parse(text);
319
- setValue(parsed);
320
- setPasted(true);
321
- setTimeout(() => setPasted(false), 2000);
322
- }
323
- catch {
324
- // ignore invalid JSON
325
- }
326
- });
327
- }, [setValue]);
328
- const expandAll = useCallback(() => {
329
- setForceOpen(true);
330
- setTreeKey((k) => k + 1);
331
- }, []);
332
- const collapseAll = useCallback(() => {
333
- setForceOpen(false);
334
- setTreeKey((k) => k + 1);
335
- }, []);
336
- const callbacks = {
337
- onUpdate: (p, v) => setValue(updateAtPath(parsed, p, v)),
338
- onDelete: (p) => setValue(deleteAtPath(parsed, p)),
339
- onRenameKey: (p, newKey) => setValue(renameKeyAtPath(parsed, p, newKey)),
340
- onAddChild: (p) => setValue(addChildAtPath(parsed, p)),
341
- };
342
- const header = (_jsxs("span", { children: [typeof field.label === 'string' ? field.label : field.name, moduleCount !== null && (_jsxs("span", { style: { opacity: 0.5, fontWeight: 400, marginLeft: 8 }, children: ["(", moduleCount, " item", moduleCount !== 1 ? 's' : '', ")"] }))] }));
343
- return (_jsxs("div", { className: "njv-root field-type", style: { marginBottom: '1.5rem' }, children: [_jsx(Collapsible, { header: header, initCollapsed: true, children: _jsxs("div", { style: { border: '1px solid var(--njv-border)', borderRadius: '0 0 4px 4px', overflow: 'hidden' }, children: [_jsxs("div", { className: "njv-toolbar", children: [_jsx("button", { type: "button", className: "btn btn--size-small btn--style-secondary", onClick: handleCopy, children: copied ? '✓ Copied' : 'Copy' }), unlocked && (_jsx("button", { type: "button", className: "btn btn--size-small btn--style-secondary", onClick: handlePaste, children: pasted ? '✓ Pasted' : 'Paste' })), _jsx("button", { type: "button", className: "btn btn--size-small btn--style-secondary", onClick: expandAll, children: "Expand All" }), _jsx("button", { type: "button", className: "btn btn--size-small btn--style-secondary", onClick: collapseAll, children: "Collapse All" }), _jsx("button", { type: "button", onClick: () => setUnlocked((u) => !u), style: {
344
- marginBlock: '12px',
345
- marginLeft: 'auto',
346
- padding: '4px 12px',
347
- fontSize: '12px',
348
- background: unlocked ? '#ef4444' : '#6b7280',
349
- color: '#fff',
350
- border: 'none',
351
- borderRadius: '4px',
352
- cursor: 'pointer',
353
- }, children: unlocked ? 'Lock' : 'Unlock Edit' })] }), _jsx("div", { className: "njv-body", style: {
354
- maxHeight: '50vh',
355
- overflow: 'auto',
356
- fontFamily: 'var(--font-mono, "SF Mono", Menlo, Consolas, monospace)',
357
- fontSize: 'var(--font-body-size, 13px)',
358
- backgroundColor: 'var(--njv-bg)',
359
- }, children: _jsx(JsonNode, { value: parsed ?? [], depth: 0, forceOpen: forceOpen, path: [], unlocked: unlocked, callbacks: callbacks }, treeKey) })] }) }), typeof field.admin?.description === 'string' && (_jsx("p", { style: { fontSize: '12px', color: '#888', marginTop: '4px' }, children: field.admin.description }))] }));
360
- };
2
+ import dynamic from 'next/dynamic';
3
+ const NitrogenDataViewer = dynamic(() => import('./NitrogenDataViewerRuntime'), {
4
+ loading: () => null,
5
+ ssr: false,
6
+ });
361
7
  export default NitrogenDataViewer;
@@ -0,0 +1,3 @@
1
+ import type { JSONFieldClientComponent } from 'payload';
2
+ declare const NitrogenDataViewerRuntime: JSONFieldClientComponent;
3
+ export default NitrogenDataViewerRuntime;
@@ -0,0 +1,353 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState, useCallback } from 'react';
4
+ import { Collapsible, useField } from '@payloadcms/ui';
5
+ function updateAtPath(root, path, newValue) {
6
+ if (path.length === 0)
7
+ return newValue;
8
+ const [head, ...rest] = path;
9
+ if (Array.isArray(root)) {
10
+ const copy = [...root];
11
+ copy[head] = updateAtPath(copy[head], rest, newValue);
12
+ return copy;
13
+ }
14
+ if (typeof root === 'object' && root !== null) {
15
+ const obj = root;
16
+ return { ...obj, [head]: updateAtPath(obj[head], rest, newValue) };
17
+ }
18
+ return root;
19
+ }
20
+ function deleteAtPath(root, path) {
21
+ if (path.length === 0)
22
+ return root;
23
+ if (path.length === 1) {
24
+ const [head] = path;
25
+ if (Array.isArray(root))
26
+ return root.filter((_, i) => i !== head);
27
+ if (typeof root === 'object' && root !== null) {
28
+ const { [head]: _removed, ...rest } = root;
29
+ return rest;
30
+ }
31
+ return root;
32
+ }
33
+ const [head, ...rest] = path;
34
+ if (Array.isArray(root)) {
35
+ const copy = [...root];
36
+ copy[head] = deleteAtPath(copy[head], rest);
37
+ return copy;
38
+ }
39
+ if (typeof root === 'object' && root !== null) {
40
+ const obj = root;
41
+ return { ...obj, [head]: deleteAtPath(obj[head], rest) };
42
+ }
43
+ return root;
44
+ }
45
+ function renameKeyAtPath(root, path, newKey) {
46
+ if (path.length === 0)
47
+ return root;
48
+ const parentPath = path.slice(0, -1);
49
+ const oldKey = path[path.length - 1];
50
+ function navigate(node, remaining) {
51
+ if (remaining.length === 0) {
52
+ if (typeof node === 'object' && node !== null && !Array.isArray(node)) {
53
+ const obj = node;
54
+ const result = {};
55
+ for (const [k, v] of Object.entries(obj)) {
56
+ result[k === oldKey ? newKey : k] = v;
57
+ }
58
+ return result;
59
+ }
60
+ return node;
61
+ }
62
+ const [head, ...rest] = remaining;
63
+ if (Array.isArray(node)) {
64
+ const copy = [...node];
65
+ copy[head] = navigate(copy[head], rest);
66
+ return copy;
67
+ }
68
+ if (typeof node === 'object' && node !== null) {
69
+ const obj = node;
70
+ return { ...obj, [head]: navigate(obj[head], rest) };
71
+ }
72
+ return node;
73
+ }
74
+ return navigate(root, parentPath);
75
+ }
76
+ function addChildAtPath(root, path) {
77
+ function getAt(node, p) {
78
+ if (p.length === 0)
79
+ return node;
80
+ const [head, ...rest] = p;
81
+ if (Array.isArray(node))
82
+ return getAt(node[head], rest);
83
+ if (typeof node === 'object' && node !== null) {
84
+ return getAt(node[head], rest);
85
+ }
86
+ return undefined;
87
+ }
88
+ const target = getAt(root, path);
89
+ if (Array.isArray(target)) {
90
+ return updateAtPath(root, path, [...target, '']);
91
+ }
92
+ if (typeof target === 'object' && target !== null) {
93
+ const obj = target;
94
+ let key = 'newKey';
95
+ let i = 1;
96
+ while (key in obj)
97
+ key = `newKey${i++}`;
98
+ return updateAtPath(root, path, { ...obj, [key]: '' });
99
+ }
100
+ return root;
101
+ }
102
+ function InlineEdit({ display, onCommit, colorClass, }) {
103
+ const [draft, setDraft] = useState(display);
104
+ const commit = () => onCommit(draft);
105
+ return (_jsx("input", { autoFocus: true, value: draft, onChange: (e) => setDraft(e.target.value), onBlur: commit, onKeyDown: (e) => {
106
+ if (e.key === 'Enter') {
107
+ e.preventDefault();
108
+ commit();
109
+ }
110
+ if (e.key === 'Escape')
111
+ onCommit(display);
112
+ }, onClick: (e) => e.stopPropagation(), className: colorClass, style: {
113
+ background: 'transparent',
114
+ border: 'none',
115
+ borderBottom: '1px solid currentColor',
116
+ color: 'inherit',
117
+ font: 'inherit',
118
+ padding: '0 2px',
119
+ outline: 'none',
120
+ minWidth: '4ch',
121
+ width: `${Math.max(draft.length + 2, 4)}ch`,
122
+ maxWidth: '500px',
123
+ } }));
124
+ }
125
+ function ActionBtn({ label, color, onClick, }) {
126
+ return (_jsx("button", { type: "button", onClick: onClick, style: {
127
+ marginLeft: 6,
128
+ padding: '0 5px',
129
+ fontSize: '10px',
130
+ lineHeight: '16px',
131
+ background: color,
132
+ color: '#fff',
133
+ border: 'none',
134
+ borderRadius: '3px',
135
+ cursor: 'pointer',
136
+ flexShrink: 0,
137
+ opacity: 0.85,
138
+ }, children: label }));
139
+ }
140
+ function JsonNode({ k, value, depth, forceOpen, path, unlocked, callbacks, }) {
141
+ const defaultOpen = forceOpen !== null ? forceOpen : depth < 2;
142
+ const [open, setOpen] = useState(defaultOpen);
143
+ const [editingValue, setEditingValue] = useState(false);
144
+ const [editingKey, setEditingKey] = useState(false);
145
+ const { onUpdate, onDelete, onRenameKey, onAddChild } = callbacks;
146
+ const del = unlocked ? (_jsx(ActionBtn, { label: "\u00D7", color: "#ef4444", onClick: (e) => { e.stopPropagation(); onDelete(path); } })) : null;
147
+ const keyEl = k === undefined ? null : (_jsxs(_Fragment, { children: [_jsxs("span", { className: "njv-key", children: ["\"", unlocked && editingKey ? (_jsx(InlineEdit, { display: k, colorClass: "njv-key", onCommit: (newKey) => {
148
+ setEditingKey(false);
149
+ if (newKey !== k)
150
+ onRenameKey(path, newKey);
151
+ } })) : (_jsx("span", { onClick: unlocked ? (e) => { e.stopPropagation(); setEditingKey(true); } : undefined, style: unlocked ? { cursor: 'text', borderBottom: '1px dotted currentColor' } : undefined, children: k })), "\""] }), _jsx("span", { className: "njv-brace", children: ": " })] }));
152
+ if (value === null) {
153
+ return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyEl, _jsx("span", { className: "njv-null", title: unlocked ? 'Click to convert to empty string' : undefined, onClick: unlocked ? (e) => { e.stopPropagation(); onUpdate(path, ''); } : undefined, style: unlocked ? { cursor: 'pointer', borderBottom: '1px dotted currentColor' } : undefined, children: "null" })] }), del] }));
154
+ }
155
+ if (typeof value === 'string') {
156
+ return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyEl, _jsxs("span", { className: "njv-string", children: ["\"", unlocked && editingValue ? (_jsx(InlineEdit, { display: value, colorClass: "njv-string", onCommit: (v) => {
157
+ setEditingValue(false);
158
+ onUpdate(path, v);
159
+ } })) : (_jsx("span", { onClick: unlocked ? (e) => { e.stopPropagation(); setEditingValue(true); } : undefined, style: unlocked ? { cursor: 'text', borderBottom: '1px dotted currentColor' } : undefined, children: value.length > 120 ? value.slice(0, 120) + '…' : value })), "\""] })] }), del] }));
160
+ }
161
+ if (typeof value === 'number') {
162
+ return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyEl, _jsx("span", { className: "njv-number", children: unlocked && editingValue ? (_jsx(InlineEdit, { display: String(value), colorClass: "njv-number", onCommit: (v) => {
163
+ setEditingValue(false);
164
+ const n = Number(v);
165
+ onUpdate(path, Number.isNaN(n) ? v : n);
166
+ } })) : (_jsx("span", { onClick: unlocked ? (e) => { e.stopPropagation(); setEditingValue(true); } : undefined, style: unlocked ? { cursor: 'text', borderBottom: '1px dotted currentColor' } : undefined, children: value })) })] }), del] }));
167
+ }
168
+ if (typeof value === 'boolean') {
169
+ return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyEl, _jsx("span", { className: "njv-boolean", onClick: unlocked ? (e) => { e.stopPropagation(); onUpdate(path, !value); } : undefined, title: unlocked ? 'Click to toggle' : undefined, style: unlocked ? { cursor: 'pointer', borderBottom: '1px dotted currentColor' } : undefined, children: String(value) })] }), del] }));
170
+ }
171
+ if (Array.isArray(value)) {
172
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20, cursor: 'pointer', userSelect: 'none' }, onClick: () => setOpen((o) => !o), children: [_jsx("span", { className: "njv-arrow", children: open ? '▼' : '▶' }), keyEl, _jsx("span", { className: "njv-brace", children: "[" }), !open && (_jsxs(_Fragment, { children: [_jsxs("span", { className: "njv-summary", children: [" ", value.length, " items "] }), _jsx("span", { className: "njv-brace", children: "]" })] }))] }), del] }), open && (_jsxs(_Fragment, { children: [value.map((item, i) => (_jsx(JsonNode, { value: item, depth: depth + 1, forceOpen: forceOpen, path: [...path, i], unlocked: unlocked, callbacks: callbacks }, i))), _jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsx("span", { className: "njv-code", style: { paddingLeft: (depth + 1) * 20 }, children: _jsx("span", { className: "njv-brace", children: "]" }) }), unlocked && (_jsx(ActionBtn, { label: "+ item", color: "#22c55e", onClick: (e) => { e.stopPropagation(); onAddChild(path); } }))] })] }))] }));
173
+ }
174
+ if (typeof value === 'object') {
175
+ const entries = Object.entries(value);
176
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20, cursor: 'pointer', userSelect: 'none' }, onClick: () => setOpen((o) => !o), children: [_jsx("span", { className: "njv-arrow", children: open ? '▼' : '▶' }), keyEl, _jsx("span", { className: "njv-brace", children: '{' }), !open && (_jsxs(_Fragment, { children: [_jsxs("span", { className: "njv-summary", children: [" ", entries.length, " keys "] }), _jsx("span", { className: "njv-brace", children: '}' })] }))] }), del] }), open && (_jsxs(_Fragment, { children: [entries.map(([ek, ev]) => (_jsx(JsonNode, { k: ek, value: ev, depth: depth + 1, forceOpen: forceOpen, path: [...path, ek], unlocked: unlocked, callbacks: callbacks }, ek))), _jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsx("span", { className: "njv-code", style: { paddingLeft: (depth + 1) * 20 }, children: _jsx("span", { className: "njv-brace", children: '}' }) }), unlocked && (_jsx(ActionBtn, { label: "+ key", color: "#22c55e", onClick: (e) => { e.stopPropagation(); onAddChild(path); } }))] })] }))] }));
177
+ }
178
+ return null;
179
+ }
180
+ const stylesId = 'nitrogen-json-viewer-styles';
181
+ function ensureStyles() {
182
+ if (typeof document === 'undefined')
183
+ return;
184
+ if (document.getElementById(stylesId))
185
+ return;
186
+ const style = document.createElement('style');
187
+ style.id = stylesId;
188
+ style.textContent = `
189
+ .njv-root {
190
+ --njv-bg: var(--theme-elevation-50, #f8f8f8);
191
+ --njv-border: var(--theme-elevation-150, #ddd);
192
+ --njv-toolbar-bg: var(--theme-elevation-100, #eee);
193
+ --njv-gutter-bg: var(--theme-elevation-50, #f0f0f0);
194
+ --njv-gutter-color: var(--theme-elevation-400, #999);
195
+ --njv-gutter-border: var(--theme-elevation-150, #ddd);
196
+ --njv-key: #0451a5;
197
+ --njv-string: #a31515;
198
+ --njv-number: #098658;
199
+ --njv-boolean: #0000ff;
200
+ --njv-null: #0000ff;
201
+ --njv-brace: var(--theme-elevation-800, #333);
202
+ --njv-arrow: var(--theme-elevation-500, #888);
203
+ --njv-summary: var(--theme-elevation-400, #888);
204
+
205
+ .collapsible__content { padding: 0; }
206
+ }
207
+
208
+ [data-theme="dark"] .njv-root {
209
+ --njv-key: #9cdcfe;
210
+ --njv-string: #ce9178;
211
+ --njv-number: #b5cea8;
212
+ --njv-boolean: #569cd6;
213
+ --njv-null: #569cd6;
214
+ }
215
+
216
+ @media (prefers-color-scheme: dark) {
217
+ .njv-root:not([data-theme="light"] .njv-root) {
218
+ --njv-key: #9cdcfe;
219
+ --njv-string: #ce9178;
220
+ --njv-number: #b5cea8;
221
+ --njv-boolean: #569cd6;
222
+ --njv-null: #569cd6;
223
+ }
224
+ }
225
+
226
+ .njv-key { color: var(--njv-key); }
227
+ .njv-string { color: var(--njv-string); word-break: break-all; white-space: normal; }
228
+ .njv-number { color: var(--njv-number); }
229
+ .njv-boolean { color: var(--njv-boolean); }
230
+ .njv-null { color: var(--njv-null); font-style: italic; }
231
+ .njv-brace { color: var(--njv-brace); }
232
+ .njv-arrow {
233
+ color: var(--njv-arrow);
234
+ display: inline-block;
235
+ width: 14px;
236
+ font-size: 9px;
237
+ margin-right: 4px;
238
+ text-align: center;
239
+ }
240
+ .njv-summary { color: var(--njv-summary); font-style: italic; }
241
+
242
+ .njv-body { counter-reset: njv-line; }
243
+
244
+ .njv-line {
245
+ display: flex;
246
+ align-items: baseline;
247
+ min-height: 24px;
248
+ line-height: 24px;
249
+ counter-increment: njv-line;
250
+ }
251
+
252
+ .njv-line::before {
253
+ content: counter(njv-line);
254
+ flex-shrink: 0;
255
+ width: 44px;
256
+ text-align: right;
257
+ padding-right: 12px;
258
+ color: var(--njv-gutter-color);
259
+ background: var(--njv-gutter-bg);
260
+ border-right: 1px solid var(--njv-gutter-border);
261
+ user-select: none;
262
+ position: sticky;
263
+ left: 0;
264
+ z-index: 1;
265
+ }
266
+
267
+ .njv-code {
268
+ padding-left: 12px;
269
+ white-space: nowrap;
270
+ flex: 1;
271
+ }
272
+
273
+ .njv-toolbar {
274
+ display: flex;
275
+ gap: 8px 12px;
276
+ padding: 0 12px;
277
+ border-bottom: 1px solid var(--njv-border);
278
+ background: var(--njv-toolbar-bg);
279
+ button { margin-block: 12px; }
280
+ }
281
+ `;
282
+ document.head.appendChild(style);
283
+ }
284
+ const NitrogenDataViewerRuntime = ({ path, field }) => {
285
+ const { value, setValue } = useField({ path });
286
+ const [copied, setCopied] = useState(false);
287
+ const [pasted, setPasted] = useState(false);
288
+ const [forceOpen, setForceOpen] = useState(null);
289
+ const [treeKey, setTreeKey] = useState(0);
290
+ const [unlocked, setUnlocked] = useState(false);
291
+ ensureStyles();
292
+ let parsed = [];
293
+ try {
294
+ parsed = typeof value === 'string' ? JSON.parse(value) : value;
295
+ }
296
+ catch {
297
+ parsed = [];
298
+ }
299
+ const jsonString = value != null ? JSON.stringify(value, null, 2) : '[]';
300
+ const moduleCount = Array.isArray(parsed) ? parsed.length : null;
301
+ const handleCopy = useCallback(() => {
302
+ navigator.clipboard.writeText(jsonString).then(() => {
303
+ setCopied(true);
304
+ setTimeout(() => setCopied(false), 2000);
305
+ });
306
+ }, [jsonString]);
307
+ const handlePaste = useCallback(() => {
308
+ navigator.clipboard.readText().then((text) => {
309
+ try {
310
+ const parsedValue = JSON.parse(text);
311
+ setValue(parsedValue);
312
+ setPasted(true);
313
+ setTimeout(() => setPasted(false), 2000);
314
+ }
315
+ catch {
316
+ // ignore invalid JSON
317
+ }
318
+ });
319
+ }, [setValue]);
320
+ const expandAll = useCallback(() => {
321
+ setForceOpen(true);
322
+ setTreeKey((k) => k + 1);
323
+ }, []);
324
+ const collapseAll = useCallback(() => {
325
+ setForceOpen(false);
326
+ setTreeKey((k) => k + 1);
327
+ }, []);
328
+ const callbacks = {
329
+ onUpdate: (p, v) => setValue(updateAtPath(parsed, p, v)),
330
+ onDelete: (p) => setValue(deleteAtPath(parsed, p)),
331
+ onRenameKey: (p, newKey) => setValue(renameKeyAtPath(parsed, p, newKey)),
332
+ onAddChild: (p) => setValue(addChildAtPath(parsed, p)),
333
+ };
334
+ const header = (_jsxs("span", { children: [typeof field.label === 'string' ? field.label : field.name, moduleCount !== null && (_jsxs("span", { style: { opacity: 0.5, fontWeight: 400, marginLeft: 8 }, children: ["(", moduleCount, " item", moduleCount !== 1 ? 's' : '', ")"] }))] }));
335
+ return (_jsxs("div", { className: "njv-root field-type", style: { marginBottom: '1.5rem' }, children: [_jsx(Collapsible, { header: header, initCollapsed: true, children: _jsxs("div", { style: { border: '1px solid var(--njv-border)', borderRadius: '0 0 4px 4px', overflow: 'hidden' }, children: [_jsxs("div", { className: "njv-toolbar", children: [_jsx("button", { type: "button", className: "btn btn--size-small btn--style-secondary", onClick: handleCopy, children: copied ? '✓ Copied' : 'Copy' }), unlocked && (_jsx("button", { type: "button", className: "btn btn--size-small btn--style-secondary", onClick: handlePaste, children: pasted ? '✓ Pasted' : 'Paste' })), _jsx("button", { type: "button", className: "btn btn--size-small btn--style-secondary", onClick: expandAll, children: "Expand All" }), _jsx("button", { type: "button", className: "btn btn--size-small btn--style-secondary", onClick: collapseAll, children: "Collapse All" }), _jsx("button", { type: "button", onClick: () => setUnlocked((u) => !u), style: {
336
+ marginBlock: '12px',
337
+ marginLeft: 'auto',
338
+ padding: '4px 12px',
339
+ fontSize: '12px',
340
+ background: unlocked ? '#ef4444' : '#6b7280',
341
+ color: '#fff',
342
+ border: 'none',
343
+ borderRadius: '4px',
344
+ cursor: 'pointer',
345
+ }, children: unlocked ? 'Lock' : 'Unlock Edit' })] }), _jsx("div", { className: "njv-body", style: {
346
+ maxHeight: '50vh',
347
+ overflow: 'auto',
348
+ fontFamily: 'var(--font-mono, "SF Mono", Menlo, Consolas, monospace)',
349
+ fontSize: 'var(--font-body-size, 13px)',
350
+ backgroundColor: 'var(--njv-bg)',
351
+ }, children: _jsx(JsonNode, { value: parsed ?? [], depth: 0, forceOpen: forceOpen, path: [], unlocked: unlocked, callbacks: callbacks }, treeKey) })] }) }), typeof field.admin?.description === 'string' && (_jsx("p", { style: { fontSize: '12px', color: '#888', marginTop: '4px' }, children: field.admin.description }))] }));
352
+ };
353
+ export default NitrogenDataViewerRuntime;
@@ -1,4 +1,3 @@
1
- import React from "react";
2
- export declare const NitrogenEditButton: React.FC<{
1
+ export declare const NitrogenEditButton: import("react").ComponentType<{
3
2
  collection: string;
4
3
  }>;
@@ -1,78 +1,6 @@
1
- "use client";
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Button } from "@payloadcms/ui";
4
- import { useState, useEffect } from "react";
5
- import { nitrogenButtonGroupStyle, nitrogenEditDevButtonVars, nitrogenEditLiveButtonVars, nitrogenSecondaryButtonVars, nitrogenViewButtonVars, } from "./nitrogenAdminButtonStyles";
6
- export const NitrogenEditButton = ({ collection }) => {
7
- const [id, setId] = useState(undefined);
8
- const [token, setToken] = useState(undefined);
9
- const [hasDevelopmentUrl, setHasDevelopmentUrl] = useState(false);
10
- const [slug, setSlug] = useState(undefined);
11
- const [frontendUrl, setFrontendUrl] = useState(undefined);
12
- const [instanceUrl, setInstanceUrl] = useState(undefined);
13
- const [authorId, setAuthorId] = useState(undefined);
14
- useEffect(() => {
15
- const segments = window.location.pathname.split("/").filter(Boolean);
16
- const docId = segments.length >= 4 ? segments[3] : undefined;
17
- if (docId) {
18
- setId(docId);
19
- // Fetch the document to get its slug (for View button)
20
- fetch(`/api/${collection}/${docId}`)
21
- .then((res) => res.json())
22
- .then((data) => {
23
- if (data.slug) {
24
- setSlug(data.slug);
25
- }
26
- })
27
- .catch((err) => {
28
- console.error("Failed to fetch document:", err);
29
- });
30
- }
31
- // Fetch the current user's ID for the authorId param
32
- fetch("/api/users/me")
33
- .then((res) => res.json())
34
- .then((data) => {
35
- if (data.user?.id) {
36
- setAuthorId(String(data.user.id));
37
- }
38
- })
39
- .catch((err) => {
40
- console.error("Failed to fetch current user:", err);
41
- });
42
- // Fetch the connector token and URLs from NitrogenSettings global
43
- fetch("/api/globals/nitrogen-settings")
44
- .then((res) => res.json())
45
- .then((data) => {
46
- if (data.connectorToken) {
47
- setToken(data.connectorToken);
48
- }
49
- if (data.developmentUrl) {
50
- setHasDevelopmentUrl(true);
51
- }
52
- if (data.instanceUrl) {
53
- setInstanceUrl(data.instanceUrl.replace(/\/$/, ""));
54
- }
55
- const url = data.frontendUrl;
56
- if (url) {
57
- setFrontendUrl(url.replace(/\/$/, ""));
58
- }
59
- })
60
- .catch((err) => {
61
- console.error("Failed to fetch nitrogen settings:", err);
62
- });
63
- }, [collection]);
64
- if (!id || !token || !authorId)
65
- return null;
66
- const param = collection === "nitrogen-templates" ? "templateId" : "pageId";
67
- const editorBase = instanceUrl ?? "";
68
- const baseHref = `${editorBase}/nitrogen-editor?token=${encodeURIComponent(token)}&collection=${encodeURIComponent(collection)}&${param}=${id}&authorId=${encodeURIComponent(authorId)}`;
69
- return (_jsxs("div", { style: nitrogenButtonGroupStyle, children: [slug && frontendUrl && (_jsx(Button, { buttonStyle: "primary", el: "anchor", margin: false, newTab: true, url: `${frontendUrl}/${slug}`, extraButtonProps: {
70
- style: nitrogenViewButtonVars,
71
- }, children: "View Page" })), hasDevelopmentUrl ? (_jsxs(_Fragment, { children: [_jsx(Button, { buttonStyle: "primary", el: "anchor", margin: false, newTab: true, url: baseHref, extraButtonProps: {
72
- style: nitrogenEditLiveButtonVars,
73
- }, children: "Edit (Live)" }), _jsx(Button, { buttonStyle: "primary", el: "anchor", margin: false, newTab: true, url: `${baseHref}&development=true`, extraButtonProps: {
74
- style: nitrogenEditDevButtonVars,
75
- }, children: "Edit (Dev)" })] })) : (_jsx(Button, { buttonStyle: "secondary", el: "anchor", margin: false, newTab: true, url: baseHref, extraButtonProps: {
76
- style: nitrogenSecondaryButtonVars,
77
- }, children: "Edit with Nitrogen" }))] }));
78
- };
1
+ 'use client';
2
+ import dynamic from 'next/dynamic';
3
+ export const NitrogenEditButton = dynamic(() => import('./NitrogenEditButtonRuntime').then((module) => module.NitrogenEditButtonRuntime), {
4
+ loading: () => null,
5
+ ssr: false,
6
+ });
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare const NitrogenEditButtonRuntime: React.FC<{
3
+ collection: string;
4
+ }>;
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ import { Button } from '@payloadcms/ui';
5
+ import { nitrogenButtonGroupStyle, nitrogenEditDevButtonVars, nitrogenEditLiveButtonVars, nitrogenSecondaryButtonVars, nitrogenViewButtonVars, } from './nitrogenAdminButtonStyles';
6
+ export const NitrogenEditButtonRuntime = ({ collection }) => {
7
+ const [id, setId] = useState(undefined);
8
+ const [token, setToken] = useState(undefined);
9
+ const [hasDevelopmentUrl, setHasDevelopmentUrl] = useState(false);
10
+ const [slug, setSlug] = useState(undefined);
11
+ const [frontendUrl, setFrontendUrl] = useState(undefined);
12
+ const [instanceUrl, setInstanceUrl] = useState(undefined);
13
+ const [authorId, setAuthorId] = useState(undefined);
14
+ useEffect(() => {
15
+ const segments = window.location.pathname.split('/').filter(Boolean);
16
+ const docId = segments.length >= 4 ? segments[3] : undefined;
17
+ if (docId) {
18
+ setId(docId);
19
+ fetch(`/api/${collection}/${docId}`)
20
+ .then((res) => res.json())
21
+ .then((data) => {
22
+ if (data.slug) {
23
+ setSlug(data.slug);
24
+ }
25
+ })
26
+ .catch((err) => {
27
+ console.error('Failed to fetch document:', err);
28
+ });
29
+ }
30
+ fetch('/api/users/me')
31
+ .then((res) => res.json())
32
+ .then((data) => {
33
+ if (data.user?.id) {
34
+ setAuthorId(String(data.user.id));
35
+ }
36
+ })
37
+ .catch((err) => {
38
+ console.error('Failed to fetch current user:', err);
39
+ });
40
+ fetch('/api/globals/nitrogen-settings')
41
+ .then((res) => res.json())
42
+ .then((data) => {
43
+ if (data.connectorToken) {
44
+ setToken(data.connectorToken);
45
+ }
46
+ if (data.developmentUrl) {
47
+ setHasDevelopmentUrl(true);
48
+ }
49
+ if (data.instanceUrl) {
50
+ setInstanceUrl(data.instanceUrl.replace(/\/$/, ''));
51
+ }
52
+ const url = data.frontendUrl;
53
+ if (url) {
54
+ setFrontendUrl(url.replace(/\/$/, ''));
55
+ }
56
+ })
57
+ .catch((err) => {
58
+ console.error('Failed to fetch nitrogen settings:', err);
59
+ });
60
+ }, [collection]);
61
+ if (!id || !token || !authorId)
62
+ return null;
63
+ const param = collection === 'nitrogen-templates' ? 'templateId' : 'pageId';
64
+ const editorBase = instanceUrl ?? '';
65
+ const baseHref = `${editorBase}/nitrogen-editor?token=${encodeURIComponent(token)}&collection=${encodeURIComponent(collection)}&${param}=${id}&authorId=${encodeURIComponent(authorId)}`;
66
+ return (_jsxs("div", { style: nitrogenButtonGroupStyle, children: [slug && frontendUrl && (_jsx(Button, { buttonStyle: "primary", el: "anchor", margin: false, newTab: true, url: `${frontendUrl}/${slug}`, extraButtonProps: {
67
+ style: nitrogenViewButtonVars,
68
+ }, children: "View Page" })), hasDevelopmentUrl ? (_jsxs(_Fragment, { children: [_jsx(Button, { buttonStyle: "primary", el: "anchor", margin: false, newTab: true, url: baseHref, extraButtonProps: {
69
+ style: nitrogenEditLiveButtonVars,
70
+ }, children: "Edit (Live)" }), _jsx(Button, { buttonStyle: "primary", el: "anchor", margin: false, newTab: true, url: `${baseHref}&development=true`, extraButtonProps: {
71
+ style: nitrogenEditDevButtonVars,
72
+ }, children: "Edit (Dev)" })] })) : (_jsx(Button, { buttonStyle: "secondary", el: "anchor", margin: false, newTab: true, url: baseHref, extraButtonProps: {
73
+ style: nitrogenSecondaryButtonVars,
74
+ }, children: "Edit with Nitrogen" }))] }));
75
+ };
@@ -1,4 +1,3 @@
1
- import React from "react";
2
- export declare const NitrogenViewButton: React.FC<{
1
+ export declare const NitrogenViewButton: import("react").ComponentType<{
3
2
  collection: string;
4
3
  }>;
@@ -1,46 +1,6 @@
1
- "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { Button } from "@payloadcms/ui";
4
- import { useState, useEffect } from "react";
5
- import { nitrogenViewButtonVars, } from "./nitrogenAdminButtonStyles";
6
- export const NitrogenViewButton = ({ collection }) => {
7
- const [slug, setSlug] = useState(undefined);
8
- const [frontendUrl, setFrontendUrl] = useState(undefined);
9
- useEffect(() => {
10
- // Get the document ID from the URL
11
- const segments = window.location.pathname.split("/").filter(Boolean);
12
- const docId = segments.length >= 4 ? segments[3] : undefined;
13
- if (!docId)
14
- return;
15
- // Fetch the document to get its slug
16
- fetch(`/api/${collection}/${docId}`)
17
- .then((res) => res.json())
18
- .then((data) => {
19
- if (data.slug) {
20
- setSlug(data.slug);
21
- }
22
- })
23
- .catch((err) => {
24
- console.error("Failed to fetch document:", err);
25
- });
26
- // Fetch nitrogen settings to get the frontend URL
27
- fetch("/api/globals/nitrogen-settings")
28
- .then((res) => res.json())
29
- .then((data) => {
30
- const url = data.developmentUrl || data.frontendUrl;
31
- if (url) {
32
- setFrontendUrl(url.replace(/\/$/, ""));
33
- }
34
- })
35
- .catch((err) => {
36
- console.error("Failed to fetch nitrogen settings:", err);
37
- });
38
- }, [collection]);
39
- if (!slug || !frontendUrl) {
40
- return null;
41
- }
42
- const href = `${frontendUrl}/${slug}`;
43
- return (_jsx(Button, { buttonStyle: "primary", el: "anchor", margin: false, newTab: true, url: href, extraButtonProps: {
44
- style: nitrogenViewButtonVars,
45
- }, children: "View Page" }));
46
- };
1
+ 'use client';
2
+ import dynamic from 'next/dynamic';
3
+ export const NitrogenViewButton = dynamic(() => import('./NitrogenViewButtonRuntime').then((module) => module.NitrogenViewButtonRuntime), {
4
+ loading: () => null,
5
+ ssr: false,
6
+ });
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare const NitrogenViewButtonRuntime: React.FC<{
3
+ collection: string;
4
+ }>;
@@ -0,0 +1,43 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ import { Button } from '@payloadcms/ui';
5
+ import { nitrogenViewButtonVars } from './nitrogenAdminButtonStyles';
6
+ export const NitrogenViewButtonRuntime = ({ collection }) => {
7
+ const [slug, setSlug] = useState(undefined);
8
+ const [frontendUrl, setFrontendUrl] = useState(undefined);
9
+ useEffect(() => {
10
+ const segments = window.location.pathname.split('/').filter(Boolean);
11
+ const docId = segments.length >= 4 ? segments[3] : undefined;
12
+ if (!docId)
13
+ return;
14
+ fetch(`/api/${collection}/${docId}`)
15
+ .then((res) => res.json())
16
+ .then((data) => {
17
+ if (data.slug) {
18
+ setSlug(data.slug);
19
+ }
20
+ })
21
+ .catch((err) => {
22
+ console.error('Failed to fetch document:', err);
23
+ });
24
+ fetch('/api/globals/nitrogen-settings')
25
+ .then((res) => res.json())
26
+ .then((data) => {
27
+ const url = data.developmentUrl || data.frontendUrl;
28
+ if (url) {
29
+ setFrontendUrl(url.replace(/\/$/, ''));
30
+ }
31
+ })
32
+ .catch((err) => {
33
+ console.error('Failed to fetch nitrogen settings:', err);
34
+ });
35
+ }, [collection]);
36
+ if (!slug || !frontendUrl) {
37
+ return null;
38
+ }
39
+ const href = `${frontendUrl}/${slug}`;
40
+ return (_jsx(Button, { buttonStyle: "primary", el: "anchor", margin: false, newTab: true, url: href, extraButtonProps: {
41
+ style: nitrogenViewButtonVars,
42
+ }, children: "View Page" }));
43
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitrogenbuilder/connector-payload",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "description": "Nitrogen page builder connector plugin for Payload CMS 3.x",
5
5
  "author": "Leonardo Dentzien <leo@torchmedia.ca>",
6
6
  "type": "module",