@nitrogenbuilder/connector-payload 0.1.35 → 0.1.37
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/dist/components/NitrogenDataViewer.js +2 -2
- package/dist/components/NitrogenDataViewerRuntime.d.ts +2 -1
- package/dist/components/NitrogenDataViewerRuntime.js +163 -69
- package/dist/endpoints/helpers.d.ts +19 -0
- package/dist/endpoints/helpers.js +39 -0
- package/dist/endpoints/media.js +70 -8
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import NitrogenDataViewerRuntime from './NitrogenDataViewerRuntime';
|
|
3
|
-
const NitrogenDataViewer = ({ field, value }) => {
|
|
3
|
+
const NitrogenDataViewer = ({ field, path, value }) => {
|
|
4
4
|
const label = typeof field.label === 'string' ? field.label : field.name;
|
|
5
5
|
const description = typeof field.admin?.description === 'string' ? field.admin.description : undefined;
|
|
6
|
-
return (_jsx(NitrogenDataViewerRuntime, { description: description, label: label,
|
|
6
|
+
return (_jsx(NitrogenDataViewerRuntime, { description: description, initialValue: value, label: label, path: path }));
|
|
7
7
|
};
|
|
8
8
|
export default NitrogenDataViewer;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { useField } from '@payloadcms/ui';
|
|
4
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
4
5
|
function updateAtPath(root, path, newValue) {
|
|
5
6
|
if (path.length === 0)
|
|
6
7
|
return newValue;
|
|
@@ -73,10 +74,10 @@ function renameKeyAtPath(root, path, newKey) {
|
|
|
73
74
|
return navigate(root, parentPath);
|
|
74
75
|
}
|
|
75
76
|
function addChildAtPath(root, path) {
|
|
76
|
-
function getAt(node,
|
|
77
|
-
if (
|
|
77
|
+
function getAt(node, currentPath) {
|
|
78
|
+
if (currentPath.length === 0)
|
|
78
79
|
return node;
|
|
79
|
-
const [head, ...rest] =
|
|
80
|
+
const [head, ...rest] = currentPath;
|
|
80
81
|
if (Array.isArray(node))
|
|
81
82
|
return getAt(node[head], rest);
|
|
82
83
|
if (typeof node === 'object' && node !== null) {
|
|
@@ -91,88 +92,117 @@ function addChildAtPath(root, path) {
|
|
|
91
92
|
if (typeof target === 'object' && target !== null) {
|
|
92
93
|
const obj = target;
|
|
93
94
|
let key = 'newKey';
|
|
94
|
-
let
|
|
95
|
-
while (key in obj)
|
|
96
|
-
key = `newKey${
|
|
95
|
+
let index = 1;
|
|
96
|
+
while (key in obj) {
|
|
97
|
+
key = `newKey${index++}`;
|
|
98
|
+
}
|
|
97
99
|
return updateAtPath(root, path, { ...obj, [key]: '' });
|
|
98
100
|
}
|
|
99
101
|
return root;
|
|
100
102
|
}
|
|
101
|
-
function
|
|
103
|
+
function coerceDisplayValue(value) {
|
|
104
|
+
if (typeof value === 'string') {
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
return value != null ? JSON.stringify(value, null, 2) : '[]';
|
|
108
|
+
}
|
|
109
|
+
function parseJsonValue(value) {
|
|
110
|
+
if (typeof value === 'string') {
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(value);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return value ?? [];
|
|
119
|
+
}
|
|
120
|
+
function InlineEdit({ colorClass, display, onCommit, }) {
|
|
102
121
|
const [draft, setDraft] = useState(display);
|
|
103
122
|
const commit = () => onCommit(draft);
|
|
104
|
-
return (_jsx("input", { autoFocus: true,
|
|
105
|
-
if (
|
|
106
|
-
|
|
123
|
+
return (_jsx("input", { autoFocus: true, className: colorClass, onBlur: commit, onChange: (event) => setDraft(event.target.value), onClick: (event) => event.stopPropagation(), onKeyDown: (event) => {
|
|
124
|
+
if (event.key === 'Enter') {
|
|
125
|
+
event.preventDefault();
|
|
107
126
|
commit();
|
|
108
127
|
}
|
|
109
|
-
if (
|
|
128
|
+
if (event.key === 'Escape') {
|
|
110
129
|
onCommit(display);
|
|
111
|
-
|
|
130
|
+
}
|
|
131
|
+
}, style: {
|
|
112
132
|
background: 'transparent',
|
|
113
133
|
border: 'none',
|
|
114
134
|
borderBottom: '1px solid currentColor',
|
|
115
135
|
color: 'inherit',
|
|
116
136
|
font: 'inherit',
|
|
117
|
-
|
|
118
|
-
outline: 'none',
|
|
137
|
+
maxWidth: '500px',
|
|
119
138
|
minWidth: '4ch',
|
|
139
|
+
outline: 'none',
|
|
140
|
+
padding: '0 2px',
|
|
120
141
|
width: `${Math.max(draft.length + 2, 4)}ch`,
|
|
121
|
-
|
|
122
|
-
} }));
|
|
142
|
+
}, value: draft }));
|
|
123
143
|
}
|
|
124
|
-
function
|
|
125
|
-
return (_jsx("button", {
|
|
126
|
-
marginLeft: 6,
|
|
127
|
-
padding: '0 5px',
|
|
128
|
-
fontSize: '10px',
|
|
129
|
-
lineHeight: '16px',
|
|
144
|
+
function ActionButton({ color, label, onClick, }) {
|
|
145
|
+
return (_jsx("button", { onClick: onClick, style: {
|
|
130
146
|
background: color,
|
|
131
|
-
color: '#fff',
|
|
132
147
|
border: 'none',
|
|
133
|
-
borderRadius:
|
|
148
|
+
borderRadius: 3,
|
|
149
|
+
color: '#fff',
|
|
134
150
|
cursor: 'pointer',
|
|
135
151
|
flexShrink: 0,
|
|
152
|
+
fontSize: 10,
|
|
153
|
+
lineHeight: '16px',
|
|
154
|
+
marginLeft: 6,
|
|
136
155
|
opacity: 0.85,
|
|
137
|
-
|
|
156
|
+
padding: '0 5px',
|
|
157
|
+
}, type: "button", children: label }));
|
|
138
158
|
}
|
|
139
|
-
function JsonNode({
|
|
159
|
+
function JsonNode({ callbacks, depth, forceOpen, k, path, unlocked, value, }) {
|
|
140
160
|
const defaultOpen = forceOpen !== null ? forceOpen : depth < 2;
|
|
141
161
|
const [open, setOpen] = useState(defaultOpen);
|
|
142
|
-
const [editingValue, setEditingValue] = useState(false);
|
|
143
162
|
const [editingKey, setEditingKey] = useState(false);
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
const
|
|
163
|
+
const [editingValue, setEditingValue] = useState(false);
|
|
164
|
+
const { onAddChild, onDelete, onRenameKey, onUpdate } = callbacks;
|
|
165
|
+
const deleteButton = unlocked ? (_jsx(ActionButton, { color: "#ef4444", label: "\u00D7", onClick: (event) => {
|
|
166
|
+
event.stopPropagation();
|
|
167
|
+
onDelete(path);
|
|
168
|
+
} })) : null;
|
|
169
|
+
const keyElement = k === undefined ? null : (_jsxs(_Fragment, { children: [_jsxs("span", { className: "njv-key", children: ["\"", unlocked && editingKey ? (_jsx(InlineEdit, { colorClass: "njv-key", display: k, onCommit: (newKey) => {
|
|
147
170
|
setEditingKey(false);
|
|
148
|
-
if (newKey !== k)
|
|
171
|
+
if (newKey !== k) {
|
|
149
172
|
onRenameKey(path, newKey);
|
|
150
|
-
|
|
173
|
+
}
|
|
174
|
+
} })) : (_jsx("span", { onClick: unlocked ? (event) => { event.stopPropagation(); setEditingKey(true); } : undefined, style: unlocked ? { borderBottom: '1px dotted currentColor', cursor: 'text' } : undefined, children: k })), "\""] }), _jsx("span", { className: "njv-brace", children: ": " })] }));
|
|
151
175
|
if (value === null) {
|
|
152
|
-
return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [
|
|
176
|
+
return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyElement, _jsx("span", { className: "njv-null", onClick: unlocked ? (event) => { event.stopPropagation(); onUpdate(path, ''); } : undefined, style: unlocked ? { borderBottom: '1px dotted currentColor', cursor: 'pointer' } : undefined, title: unlocked ? 'Click to convert to empty string' : undefined, children: "null" })] }), deleteButton] }));
|
|
153
177
|
}
|
|
154
178
|
if (typeof value === 'string') {
|
|
155
|
-
return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [
|
|
179
|
+
return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyElement, _jsxs("span", { className: "njv-string", children: ["\"", unlocked && editingValue ? (_jsx(InlineEdit, { colorClass: "njv-string", display: value, onCommit: (nextValue) => {
|
|
156
180
|
setEditingValue(false);
|
|
157
|
-
onUpdate(path,
|
|
158
|
-
} })) : (_jsx("span", { onClick: unlocked ? (
|
|
181
|
+
onUpdate(path, nextValue);
|
|
182
|
+
} })) : (_jsx("span", { onClick: unlocked ? (event) => { event.stopPropagation(); setEditingValue(true); } : undefined, style: unlocked ? { borderBottom: '1px dotted currentColor', cursor: 'text' } : undefined, children: value.length > 120 ? `${value.slice(0, 120)}…` : value })), "\""] })] }), deleteButton] }));
|
|
159
183
|
}
|
|
160
184
|
if (typeof value === 'number') {
|
|
161
|
-
return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [
|
|
185
|
+
return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyElement, _jsx("span", { className: "njv-number", children: unlocked && editingValue ? (_jsx(InlineEdit, { colorClass: "njv-number", display: String(value), onCommit: (nextValue) => {
|
|
162
186
|
setEditingValue(false);
|
|
163
|
-
const
|
|
164
|
-
onUpdate(path, Number.isNaN(
|
|
165
|
-
} })) : (_jsx("span", { onClick: unlocked ? (
|
|
187
|
+
const numericValue = Number(nextValue);
|
|
188
|
+
onUpdate(path, Number.isNaN(numericValue) ? nextValue : numericValue);
|
|
189
|
+
} })) : (_jsx("span", { onClick: unlocked ? (event) => { event.stopPropagation(); setEditingValue(true); } : undefined, style: unlocked ? { borderBottom: '1px dotted currentColor', cursor: 'text' } : undefined, children: value })) })] }), deleteButton] }));
|
|
166
190
|
}
|
|
167
191
|
if (typeof value === 'boolean') {
|
|
168
|
-
return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [
|
|
192
|
+
return (_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", style: { paddingLeft: depth * 20 }, children: [keyElement, _jsx("span", { className: "njv-boolean", onClick: unlocked ? (event) => { event.stopPropagation(); onUpdate(path, !value); } : undefined, style: unlocked ? { borderBottom: '1px dotted currentColor', cursor: 'pointer' } : undefined, title: unlocked ? 'Click to toggle' : undefined, children: String(value) })] }), deleteButton] }));
|
|
169
193
|
}
|
|
170
194
|
if (Array.isArray(value)) {
|
|
171
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code",
|
|
195
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", onClick: () => setOpen((current) => !current), style: { cursor: 'pointer', paddingLeft: depth * 20, userSelect: 'none' }, children: [_jsx("span", { className: "njv-arrow", children: open ? '▼' : '▶' }), keyElement, _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: "]" })] })) : null] }), deleteButton] }), open ? (_jsxs(_Fragment, { children: [value.map((item, index) => (_jsx(JsonNode, { callbacks: callbacks, depth: depth + 1, forceOpen: forceOpen, path: [...path, index], unlocked: unlocked, value: item }, index))), _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(ActionButton, { color: "#22c55e", label: "+ item", onClick: (event) => {
|
|
196
|
+
event.stopPropagation();
|
|
197
|
+
onAddChild(path);
|
|
198
|
+
} })) : null] })] })) : null] }));
|
|
172
199
|
}
|
|
173
200
|
if (typeof value === 'object') {
|
|
174
201
|
const entries = Object.entries(value);
|
|
175
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code",
|
|
202
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "njv-line", style: { alignItems: 'center' }, children: [_jsxs("span", { className: "njv-code", onClick: () => setOpen((current) => !current), style: { cursor: 'pointer', paddingLeft: depth * 20, userSelect: 'none' }, children: [_jsx("span", { className: "njv-arrow", children: open ? '▼' : '▶' }), keyElement, _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: '}' })] })) : null] }), deleteButton] }), open ? (_jsxs(_Fragment, { children: [entries.map(([entryKey, entryValue]) => (_jsx(JsonNode, { callbacks: callbacks, depth: depth + 1, forceOpen: forceOpen, k: entryKey, path: [...path, entryKey], unlocked: unlocked, value: entryValue }, entryKey))), _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(ActionButton, { color: "#22c55e", label: "+ key", onClick: (event) => {
|
|
203
|
+
event.stopPropagation();
|
|
204
|
+
onAddChild(path);
|
|
205
|
+
} })) : null] })] })) : null] }));
|
|
176
206
|
}
|
|
177
207
|
return null;
|
|
178
208
|
}
|
|
@@ -280,43 +310,86 @@ function ensureStyles() {
|
|
|
280
310
|
`;
|
|
281
311
|
document.head.appendChild(style);
|
|
282
312
|
}
|
|
283
|
-
const NitrogenDataViewerRuntime = ({ description, label,
|
|
284
|
-
const
|
|
313
|
+
const NitrogenDataViewerRuntime = ({ description, initialValue, label, path }) => {
|
|
314
|
+
const field = path ? useField({ path }) : null;
|
|
315
|
+
const value = field?.value ?? initialValue;
|
|
316
|
+
const setValue = field?.setValue;
|
|
285
317
|
const [collapsed, setCollapsed] = useState(true);
|
|
318
|
+
const [copied, setCopied] = useState(false);
|
|
286
319
|
const [forceOpen, setForceOpen] = useState(null);
|
|
320
|
+
const [manualPasteValue, setManualPasteValue] = useState('');
|
|
321
|
+
const [pasteError, setPasteError] = useState('');
|
|
322
|
+
const [pasted, setPasted] = useState(false);
|
|
323
|
+
const [showPastePanel, setShowPastePanel] = useState(false);
|
|
287
324
|
const [treeKey, setTreeKey] = useState(0);
|
|
325
|
+
const [unlocked, setUnlocked] = useState(false);
|
|
288
326
|
ensureStyles();
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
parsed = typeof value === 'string' ? JSON.parse(value) : value;
|
|
292
|
-
}
|
|
293
|
-
catch {
|
|
294
|
-
parsed = [];
|
|
295
|
-
}
|
|
296
|
-
const jsonString = value != null ? JSON.stringify(value, null, 2) : '[]';
|
|
327
|
+
const parsed = useMemo(() => parseJsonValue(value), [value]);
|
|
328
|
+
const jsonString = useMemo(() => coerceDisplayValue(value), [value]);
|
|
297
329
|
const moduleCount = Array.isArray(parsed) ? parsed.length : null;
|
|
330
|
+
const applyValue = useCallback((nextValue) => {
|
|
331
|
+
if (!setValue)
|
|
332
|
+
return;
|
|
333
|
+
setValue(nextValue);
|
|
334
|
+
}, [setValue]);
|
|
335
|
+
const callbacks = useMemo(() => ({
|
|
336
|
+
onAddChild: (targetPath) => {
|
|
337
|
+
applyValue(addChildAtPath(parsed, targetPath));
|
|
338
|
+
},
|
|
339
|
+
onDelete: (targetPath) => {
|
|
340
|
+
applyValue(deleteAtPath(parsed, targetPath));
|
|
341
|
+
},
|
|
342
|
+
onRenameKey: (targetPath, newKey) => {
|
|
343
|
+
applyValue(renameKeyAtPath(parsed, targetPath, newKey));
|
|
344
|
+
},
|
|
345
|
+
onUpdate: (targetPath, nextValue) => {
|
|
346
|
+
applyValue(updateAtPath(parsed, targetPath, nextValue));
|
|
347
|
+
},
|
|
348
|
+
}), [applyValue, parsed]);
|
|
298
349
|
const handleCopy = useCallback(() => {
|
|
299
350
|
navigator.clipboard.writeText(jsonString).then(() => {
|
|
300
351
|
setCopied(true);
|
|
301
352
|
setTimeout(() => setCopied(false), 2000);
|
|
302
353
|
});
|
|
303
354
|
}, [jsonString]);
|
|
355
|
+
const applyPastedJson = useCallback((rawValue) => {
|
|
356
|
+
const trimmed = rawValue.trim();
|
|
357
|
+
if (!trimmed) {
|
|
358
|
+
setPasteError('Paste some JSON first.');
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const parsedValue = JSON.parse(trimmed);
|
|
363
|
+
applyValue(parsedValue);
|
|
364
|
+
setManualPasteValue('');
|
|
365
|
+
setPasteError('');
|
|
366
|
+
setPasted(true);
|
|
367
|
+
setShowPastePanel(false);
|
|
368
|
+
setTreeKey((current) => current + 1);
|
|
369
|
+
setTimeout(() => setPasted(false), 2000);
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
setPasteError('That is not valid JSON.');
|
|
373
|
+
}
|
|
374
|
+
}, [applyValue]);
|
|
375
|
+
const handlePasteFromClipboard = useCallback(() => {
|
|
376
|
+
navigator.clipboard.readText().then((text) => {
|
|
377
|
+
applyPastedJson(text);
|
|
378
|
+
}, () => {
|
|
379
|
+
setPasteError('Clipboard access was blocked. Paste manually below.');
|
|
380
|
+
setShowPastePanel(true);
|
|
381
|
+
});
|
|
382
|
+
}, [applyPastedJson]);
|
|
304
383
|
const expandAll = useCallback(() => {
|
|
305
384
|
setForceOpen(true);
|
|
306
|
-
setTreeKey((
|
|
385
|
+
setTreeKey((current) => current + 1);
|
|
307
386
|
}, []);
|
|
308
387
|
const collapseAll = useCallback(() => {
|
|
309
388
|
setForceOpen(false);
|
|
310
|
-
setTreeKey((
|
|
389
|
+
setTreeKey((current) => current + 1);
|
|
311
390
|
}, []);
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
onDelete: () => undefined,
|
|
315
|
-
onRenameKey: () => undefined,
|
|
316
|
-
onAddChild: () => undefined,
|
|
317
|
-
};
|
|
318
|
-
const header = (_jsxs("span", { children: [label, moduleCount !== null && (_jsxs("span", { style: { opacity: 0.5, fontWeight: 400, marginLeft: 8 }, children: ["(", moduleCount, " item", moduleCount !== 1 ? 's' : '', ")"] }))] }));
|
|
319
|
-
return (_jsxs("div", { className: "njv-root field-type", style: { marginBottom: '1.5rem' }, children: [_jsxs("div", { style: { border: '1px solid var(--njv-border)', borderRadius: '4px', overflow: 'hidden' }, children: [_jsxs("button", { type: "button", onClick: () => setCollapsed((current) => !current), style: {
|
|
391
|
+
const header = (_jsxs("span", { children: [label, moduleCount !== null ? (_jsxs("span", { style: { fontWeight: 400, marginLeft: 8, opacity: 0.5 }, children: ["(", moduleCount, " item", moduleCount !== 1 ? 's' : '', ")"] })) : null] }));
|
|
392
|
+
return (_jsxs("div", { className: "njv-root field-type", style: { marginBottom: '1.5rem' }, children: [_jsxs("div", { style: { border: '1px solid var(--njv-border)', borderRadius: 4, overflow: 'hidden' }, children: [_jsxs("button", { onClick: () => setCollapsed((current) => !current), style: {
|
|
320
393
|
alignItems: 'center',
|
|
321
394
|
background: 'var(--theme-elevation-50, #f8f8f8)',
|
|
322
395
|
border: 'none',
|
|
@@ -326,12 +399,33 @@ const NitrogenDataViewerRuntime = ({ description, label, value }) => {
|
|
|
326
399
|
justifyContent: 'space-between',
|
|
327
400
|
padding: '12px 16px',
|
|
328
401
|
width: '100%',
|
|
329
|
-
}, children: [header, _jsx("span", { style: { fontSize: 12, opacity: 0.65 }, children: collapsed ? 'Show' : 'Hide' })] }), !collapsed
|
|
330
|
-
|
|
331
|
-
|
|
402
|
+
}, type: "button", children: [header, _jsx("span", { style: { fontSize: 12, opacity: 0.65 }, children: collapsed ? 'Show' : 'Hide' })] }), !collapsed ? (_jsxs("div", { children: [_jsxs("div", { className: "njv-toolbar", children: [_jsx("button", { className: "btn btn--size-small btn--style-secondary", onClick: handleCopy, type: "button", children: copied ? '✓ Copied' : 'Copy' }), _jsx("button", { className: "btn btn--size-small btn--style-secondary", onClick: () => setUnlocked((current) => !current), type: "button", children: unlocked ? 'Lock' : 'Unlock' }), unlocked ? (_jsxs(_Fragment, { children: [_jsx("button", { className: "btn btn--size-small btn--style-secondary", onClick: handlePasteFromClipboard, type: "button", children: pasted ? '✓ Pasted' : 'Paste' }), _jsx("button", { className: "btn btn--size-small btn--style-secondary", onClick: () => {
|
|
403
|
+
setShowPastePanel((current) => !current);
|
|
404
|
+
setPasteError('');
|
|
405
|
+
}, type: "button", children: showPastePanel ? 'Hide Paste Box' : 'Paste Manually' })] })) : null, _jsx("button", { className: "btn btn--size-small btn--style-secondary", onClick: expandAll, type: "button", children: "Expand All" }), _jsx("button", { className: "btn btn--size-small btn--style-secondary", onClick: collapseAll, type: "button", children: "Collapse All" })] }), unlocked && showPastePanel ? (_jsxs("div", { style: {
|
|
406
|
+
background: 'var(--theme-elevation-50, #f8f8f8)',
|
|
407
|
+
borderBottom: '1px solid var(--njv-border)',
|
|
408
|
+
display: 'grid',
|
|
409
|
+
gap: 8,
|
|
410
|
+
padding: 12,
|
|
411
|
+
}, children: [_jsx("textarea", { onChange: (event) => setManualPasteValue(event.target.value), placeholder: "Paste Nitrogen JSON here...", style: {
|
|
412
|
+
border: '1px solid var(--theme-elevation-150, #ddd)',
|
|
413
|
+
borderRadius: 4,
|
|
414
|
+
fontFamily: 'var(--font-mono, \"SF Mono\", Menlo, Consolas, monospace)',
|
|
415
|
+
fontSize: 12,
|
|
416
|
+
minHeight: 160,
|
|
417
|
+
padding: 12,
|
|
418
|
+
resize: 'vertical',
|
|
419
|
+
width: '100%',
|
|
420
|
+
}, value: manualPasteValue }), _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8 }, children: [_jsx("button", { className: "btn btn--size-small btn--style-primary", onClick: () => applyPastedJson(manualPasteValue), type: "button", children: "Apply Pasted JSON" }), _jsx("button", { className: "btn btn--size-small btn--style-secondary", onClick: () => {
|
|
421
|
+
setManualPasteValue('');
|
|
422
|
+
setPasteError('');
|
|
423
|
+
}, type: "button", children: "Clear" })] }), pasteError ? (_jsx("p", { style: { color: 'var(--theme-error-500, #dc2626)', fontSize: 12, margin: 0 }, children: pasteError })) : null] })) : null, _jsx("div", { className: "njv-body", style: {
|
|
424
|
+
backgroundColor: 'var(--njv-bg)',
|
|
332
425
|
fontFamily: 'var(--font-mono, "SF Mono", Menlo, Consolas, monospace)',
|
|
333
426
|
fontSize: 'var(--font-body-size, 13px)',
|
|
334
|
-
|
|
335
|
-
|
|
427
|
+
maxHeight: '50vh',
|
|
428
|
+
overflow: 'auto',
|
|
429
|
+
}, children: _jsx(JsonNode, { callbacks: callbacks, depth: 0, forceOpen: forceOpen, path: [], unlocked: unlocked, value: parsed }, treeKey) })] })) : null] }), typeof description === 'string' ? (_jsx("p", { style: { color: '#888', fontSize: 12, marginTop: 4 }, children: description })) : null] }));
|
|
336
430
|
};
|
|
337
431
|
export default NitrogenDataViewerRuntime;
|
|
@@ -65,10 +65,29 @@ export declare function buildMediaItemResponse(doc: MediaDoc): {
|
|
|
65
65
|
medium?: import("../types").MediaSize;
|
|
66
66
|
large?: import("../types").MediaSize;
|
|
67
67
|
};
|
|
68
|
+
thumbnailURL: string;
|
|
68
69
|
name: string;
|
|
69
70
|
mime: string;
|
|
70
71
|
ext: string;
|
|
71
72
|
size: number;
|
|
73
|
+
post_date: string;
|
|
74
|
+
formats: {
|
|
75
|
+
thumbnail: {
|
|
76
|
+
url: string;
|
|
77
|
+
width: number;
|
|
78
|
+
height: number;
|
|
79
|
+
} | null;
|
|
80
|
+
medium: {
|
|
81
|
+
url: string;
|
|
82
|
+
width: number;
|
|
83
|
+
height: number;
|
|
84
|
+
} | null;
|
|
85
|
+
large: {
|
|
86
|
+
url: string;
|
|
87
|
+
width: number;
|
|
88
|
+
height: number;
|
|
89
|
+
} | null;
|
|
90
|
+
};
|
|
72
91
|
};
|
|
73
92
|
export declare function getNitrogenSettings(payload: Payload): Promise<NitrogenSettingsGlobal>;
|
|
74
93
|
/**
|
|
@@ -96,6 +96,29 @@ export function buildListItemResponse(doc, settings, collectionSlug = 'pages') {
|
|
|
96
96
|
export function buildMediaItemResponse(doc) {
|
|
97
97
|
const filename = doc.filename || '';
|
|
98
98
|
const ext = filename.includes('.') ? `.${filename.split('.').pop()}` : '';
|
|
99
|
+
const formats = {
|
|
100
|
+
thumbnail: doc.sizes?.thumbnail
|
|
101
|
+
? {
|
|
102
|
+
url: doc.sizes.thumbnail.url || '',
|
|
103
|
+
width: doc.sizes.thumbnail.width || 0,
|
|
104
|
+
height: doc.sizes.thumbnail.height || 0,
|
|
105
|
+
}
|
|
106
|
+
: null,
|
|
107
|
+
medium: doc.sizes?.medium
|
|
108
|
+
? {
|
|
109
|
+
url: doc.sizes.medium.url || '',
|
|
110
|
+
width: doc.sizes.medium.width || 0,
|
|
111
|
+
height: doc.sizes.medium.height || 0,
|
|
112
|
+
}
|
|
113
|
+
: null,
|
|
114
|
+
large: doc.sizes?.large
|
|
115
|
+
? {
|
|
116
|
+
url: doc.sizes.large.url || '',
|
|
117
|
+
width: doc.sizes.large.width || 0,
|
|
118
|
+
height: doc.sizes.large.height || 0,
|
|
119
|
+
}
|
|
120
|
+
: null,
|
|
121
|
+
};
|
|
99
122
|
return {
|
|
100
123
|
// Payload-native fields (used by provider renderFiles/renderSelectedFile)
|
|
101
124
|
id: doc.id,
|
|
@@ -108,13 +131,29 @@ export function buildMediaItemResponse(doc) {
|
|
|
108
131
|
height: doc.height || 0,
|
|
109
132
|
createdAt: doc.createdAt,
|
|
110
133
|
sizes: doc.sizes || {},
|
|
134
|
+
thumbnailURL: formats.thumbnail?.url || doc.url || '',
|
|
111
135
|
// CtrlFile-compatible aliases
|
|
112
136
|
name: filename,
|
|
113
137
|
mime: doc.mimeType || '',
|
|
114
138
|
ext,
|
|
115
139
|
size: doc.filesize ? Math.round((doc.filesize / 1024) * 10) / 10 : 0,
|
|
140
|
+
post_date: formatDateForWordPress(doc.createdAt),
|
|
141
|
+
formats,
|
|
116
142
|
};
|
|
117
143
|
}
|
|
144
|
+
function formatDateForWordPress(value) {
|
|
145
|
+
if (!value)
|
|
146
|
+
return '';
|
|
147
|
+
const date = new Date(value);
|
|
148
|
+
if (Number.isNaN(date.getTime()))
|
|
149
|
+
return value;
|
|
150
|
+
const pad = (part) => String(part).padStart(2, '0');
|
|
151
|
+
return [
|
|
152
|
+
date.getFullYear(),
|
|
153
|
+
pad(date.getMonth() + 1),
|
|
154
|
+
pad(date.getDate()),
|
|
155
|
+
].join('-') + ` ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
156
|
+
}
|
|
118
157
|
export async function getNitrogenSettings(payload) {
|
|
119
158
|
return payload.findGlobal({ slug: 'nitrogen-settings' });
|
|
120
159
|
}
|
package/dist/endpoints/media.js
CHANGED
|
@@ -1,4 +1,38 @@
|
|
|
1
1
|
import { buildMediaItemResponse, requireAuth } from './helpers';
|
|
2
|
+
const MIME_TYPES_BY_EXTENSION = {
|
|
3
|
+
'.jpg': ['image/jpeg'],
|
|
4
|
+
'.jpeg': ['image/jpeg'],
|
|
5
|
+
'.png': ['image/png'],
|
|
6
|
+
'.gif': ['image/gif'],
|
|
7
|
+
'.svg': ['image/svg+xml'],
|
|
8
|
+
'.apng': ['image/apng'],
|
|
9
|
+
'.avif': ['image/avif'],
|
|
10
|
+
'.webp': ['image/webp'],
|
|
11
|
+
'.ico': ['image/x-icon', 'image/vnd.microsoft.icon'],
|
|
12
|
+
'.bmp': ['image/bmp'],
|
|
13
|
+
'.tiff': ['image/tiff'],
|
|
14
|
+
'.tif': ['image/tiff'],
|
|
15
|
+
'.mp4': ['video/mp4', 'audio/mp4'],
|
|
16
|
+
'.webm': ['video/webm', 'audio/webm'],
|
|
17
|
+
'.ogg': ['video/ogg', 'audio/ogg'],
|
|
18
|
+
'.ogv': ['video/ogg'],
|
|
19
|
+
'.mp3': ['audio/mpeg'],
|
|
20
|
+
'.wav': ['audio/wav', 'audio/x-wav'],
|
|
21
|
+
'.m4a': ['audio/mp4', 'audio/x-m4a'],
|
|
22
|
+
'.aac': ['audio/aac'],
|
|
23
|
+
'.oga': ['audio/ogg'],
|
|
24
|
+
'.flac': ['audio/flac'],
|
|
25
|
+
};
|
|
26
|
+
function normalizeExtension(value) {
|
|
27
|
+
const trimmed = value.trim().toLowerCase();
|
|
28
|
+
if (!trimmed)
|
|
29
|
+
return '';
|
|
30
|
+
return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
|
|
31
|
+
}
|
|
32
|
+
function parsePositiveInteger(value, fallback) {
|
|
33
|
+
const parsed = Number.parseInt(value || '', 10);
|
|
34
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
35
|
+
}
|
|
2
36
|
export const mediaEndpoints = [
|
|
3
37
|
// GET /api/nitrogen/v1/media — List media items
|
|
4
38
|
{
|
|
@@ -9,33 +43,61 @@ export const mediaEndpoints = [
|
|
|
9
43
|
const url = new URL(req.url || '', 'http://localhost');
|
|
10
44
|
// Parse Payload-style query params: where[mimeType][in][0]=image/jpg
|
|
11
45
|
const mimeTypes = [];
|
|
46
|
+
const extensions = [];
|
|
12
47
|
for (const [key, value] of url.searchParams.entries()) {
|
|
13
48
|
if (/^where\[mimeType\]\[in\]\[\d+\]$/.test(key)) {
|
|
14
49
|
mimeTypes.push(value);
|
|
15
50
|
}
|
|
51
|
+
if (key === 'filters[ext]' || /^filters\[ext\](\[(\d*)\])?$/.test(key)) {
|
|
52
|
+
const extension = normalizeExtension(value);
|
|
53
|
+
if (extension) {
|
|
54
|
+
extensions.push(extension);
|
|
55
|
+
mimeTypes.push(...(MIME_TYPES_BY_EXTENSION[extension] || []));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
16
58
|
}
|
|
17
|
-
// Parse search
|
|
18
|
-
const search = url.searchParams.get('where[filename][contains]') ||
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
59
|
+
// Parse search from either Payload-style or WordPress-compatible params.
|
|
60
|
+
const search = url.searchParams.get('where[filename][contains]') ||
|
|
61
|
+
url.searchParams.get('filters[search]') ||
|
|
62
|
+
'';
|
|
63
|
+
const page = parsePositiveInteger(url.searchParams.get('page') || url.searchParams.get('paged'), 1);
|
|
64
|
+
const limit = Math.min(parsePositiveInteger(url.searchParams.get('limit') || url.searchParams.get('per_page'), 100), 100);
|
|
65
|
+
const and = [];
|
|
22
66
|
if (mimeTypes.length > 0) {
|
|
23
|
-
|
|
67
|
+
and.push({ mimeType: { in: Array.from(new Set(mimeTypes)) } });
|
|
68
|
+
}
|
|
69
|
+
else if (extensions.length > 0) {
|
|
70
|
+
and.push({
|
|
71
|
+
or: Array.from(new Set(extensions)).map((extension) => ({
|
|
72
|
+
filename: { contains: extension },
|
|
73
|
+
})),
|
|
74
|
+
});
|
|
24
75
|
}
|
|
25
76
|
if (search) {
|
|
26
|
-
|
|
77
|
+
and.push({
|
|
78
|
+
or: [
|
|
79
|
+
{ filename: { contains: search } },
|
|
80
|
+
{ alt: { contains: search } },
|
|
81
|
+
],
|
|
82
|
+
});
|
|
27
83
|
}
|
|
84
|
+
const where = and.length > 1 ? { and } : and[0] || {};
|
|
28
85
|
const result = await payload.find({
|
|
29
86
|
collection: 'media',
|
|
30
87
|
where,
|
|
31
88
|
page,
|
|
32
|
-
limit
|
|
89
|
+
limit,
|
|
33
90
|
sort: '-createdAt',
|
|
34
91
|
});
|
|
35
92
|
const docs = result.docs.map((doc) => buildMediaItemResponse(doc));
|
|
36
93
|
return Response.json({
|
|
37
94
|
docs,
|
|
95
|
+
images: docs,
|
|
96
|
+
pages: result.totalPages,
|
|
38
97
|
totalPages: result.totalPages,
|
|
98
|
+
totalDocs: result.totalDocs,
|
|
99
|
+
page: result.page,
|
|
100
|
+
limit: result.limit,
|
|
39
101
|
});
|
|
40
102
|
},
|
|
41
103
|
},
|
package/package.json
CHANGED