@kyro-cms/admin 0.10.12 → 0.10.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyro-cms/admin",
3
- "version": "0.10.12",
3
+ "version": "0.10.14",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },
@@ -540,6 +540,8 @@ export function AutoForm({
540
540
  setLocalSaveStatus("saving");
541
541
 
542
542
  try {
543
+ let dataToPublish = { ...formData };
544
+
543
545
  if (isNewDoc && !globalSlug) {
544
546
  // Create then immediately publish
545
547
  const data = normalizeUploadFields({ ...formData }) as Record<string, unknown>;
@@ -560,10 +562,11 @@ export function AutoForm({
560
562
  const savedData = result.data || data;
561
563
  setFormData({ ...formData, ...savedData });
562
564
  setLastSavedData({ ...formData, ...savedData });
565
+ dataToPublish = { ...formData, ...savedData };
563
566
  }
564
567
 
565
568
  // Save and publish (X-Draft: false writes to main doc + versions table)
566
- const data = normalizeUploadFields({ ...formData }) as Record<string, unknown>;
569
+ const data = normalizeUploadFields(dataToPublish) as Record<string, unknown>;
567
570
  const response = await saveDocument(data, false);
568
571
 
569
572
  if (response?.ok) {
@@ -107,17 +107,6 @@ export const BlockEditModal: React.FC<BlockEditModalProps> = ({
107
107
  accentClass={theme.border}
108
108
  >
109
109
  <div className="space-y-4">
110
- <div>
111
- <label className="text-[10px] font-medium text-[var(--kyro-text-muted)] mb-1 block">
112
- Block Name
113
- </label>
114
- <input
115
- value={(blockData?.name as string) || ""}
116
- onChange={(e) => updateBlock(block.id, { name: e.target.value })}
117
- placeholder={blockSchema?.label || (block.type as string)}
118
- className="w-full bg-[var(--kyro-bg-primary)] border border-[var(--kyro-border)] rounded-lg px-3 py-2 text-sm text-[var(--kyro-text-primary)] outline-none focus:border-[var(--kyro-primary)] transition-colors"
119
- />
120
- </div>
121
110
 
122
111
  {renderFields()}
123
112
 
@@ -257,12 +257,18 @@ const SortableBlockComponent = ({
257
257
  onKeyDown={(e) => { if (e.key === "Enter") commitName(); if (e.key === "Escape") { setNameDraft((block.name as string) || ""); setEditingName(false); } }}
258
258
  onClick={(e) => e.stopPropagation()}
259
259
  placeholder={getBlockLabel(block.type as string)}
260
- className="w-full bg-[var(--kyro-surface-accent)] border border-[var(--kyro-primary)] rounded px-2 py-0.5 text-xs font-semibold text-[var(--kyro-text-primary)] outline-none"
260
+ className="w-full max-w-[150px] sm:max-w-[250px] md:max-w-[400px] bg-[var(--kyro-surface-accent)] border border-[var(--kyro-primary)] rounded px-2 py-0.5 text-xs font-semibold text-[var(--kyro-text-primary)] outline-none"
261
261
  />
262
262
  ) : (
263
263
  <div
264
- onClick={(e) => { e.stopPropagation(); setNameDraft((block.name as string) || ""); setEditingName(true); }}
265
- className="text-xs font-semibold text-[var(--kyro-text-secondary)] truncate cursor-text hover:text-[var(--kyro-text-primary)] transition-colors"
264
+ onClick={(e) => {
265
+ if (window.innerWidth >= 768) {
266
+ e.stopPropagation();
267
+ setNameDraft((block.name as string) || "");
268
+ setEditingName(true);
269
+ }
270
+ }}
271
+ className="text-xs font-semibold text-[var(--kyro-text-secondary)] truncate md:cursor-text hover:text-[var(--kyro-text-primary)] transition-colors"
266
272
  title="Click to rename"
267
273
  >
268
274
  {itemLabel}
@@ -310,7 +316,19 @@ const SortableBlockComponent = ({
310
316
  </button>
311
317
  </div>
312
318
  ) : (
313
- <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
319
+ <div className="flex items-center gap-1 opacity-100 md:opacity-0 md:group-hover:opacity-100 transition-opacity">
320
+ <button
321
+ type="button"
322
+ onClick={(e) => {
323
+ e.stopPropagation();
324
+ setNameDraft((block.name as string) || "");
325
+ setEditingName(true);
326
+ }}
327
+ className="md:hidden p-1 hover:bg-[var(--kyro-surface-accent)] rounded text-[var(--kyro-text-secondary)] transition-colors"
328
+ title="Rename Block"
329
+ >
330
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
331
+ </button>
314
332
  <button
315
333
  type="button"
316
334
  onClick={(e) => {
@@ -65,7 +65,7 @@ export function RelationshipField({
65
65
  ? field.relationTo
66
66
  : [field.relationTo];
67
67
  const isPolymorphic = relationTo.length > 1;
68
- const [activeRelation, setActiveRelation] = useState(relationTo[0]);
68
+ const [activeRelation, setActiveRelation] = useState(relationTo[0] || "");
69
69
 
70
70
  const extractIds = useCallback((): string[] => {
71
71
  if (!value) return [];
package/src/lib/config.ts CHANGED
@@ -101,6 +101,33 @@ function updateFieldByPath(
101
101
  if (field.fields && Array.isArray(field.fields)) {
102
102
  return updateFieldByPath(field.fields, remainingPath, updates);
103
103
  }
104
+
105
+ // For tabs fields, traverse into all tabs
106
+ if (field.type === "tabs" && field.tabs && Array.isArray(field.tabs)) {
107
+ for (const tab of field.tabs) {
108
+ if (tab.fields && Array.isArray(tab.fields)) {
109
+ if (updateFieldByPath(tab.fields, remainingPath, updates)) {
110
+ return true;
111
+ }
112
+ }
113
+ }
114
+ return false;
115
+ }
116
+
117
+ // For blocks fields, the next path segment is the block slug
118
+ if (field.type === "blocks" && field.blocks && Array.isArray(field.blocks)) {
119
+ const blockSlug = remainingPath.split(".")[0];
120
+ const restOfPath = remainingPath.split(".").slice(1).join(".");
121
+ if (!restOfPath) return false;
122
+
123
+ for (const block of field.blocks) {
124
+ if (block.slug === blockSlug && block.fields && Array.isArray(block.fields)) {
125
+ return updateFieldByPath(block.fields, restOfPath, updates);
126
+ }
127
+ }
128
+ return false;
129
+ }
130
+
104
131
  // For array fields, look in the nested fields
105
132
  if (field.type === "array" && field.fields && Array.isArray(field.fields)) {
106
133
  return updateFieldByPath(field.fields, remainingPath, updates);
@@ -112,6 +139,27 @@ function updateFieldByPath(
112
139
  return true;
113
140
  }
114
141
  }
142
+
143
+ // If it's a structural field (like tabs, row, or collapsible) that we should pass through implicitly,
144
+ // we must traverse it even if it has a name, because these fields do not create nested data objects.
145
+ const isFlatStructuralField =
146
+ !field.name ||
147
+ field.type === "tabs" ||
148
+ field.type === "row" ||
149
+ field.type === "collapsible";
150
+
151
+ if (isFlatStructuralField) {
152
+ if (field.fields && Array.isArray(field.fields)) {
153
+ if (updateFieldByPath(field.fields, path, updates)) return true;
154
+ }
155
+ if (field.type === "tabs" && field.tabs && Array.isArray(field.tabs)) {
156
+ for (const tab of field.tabs) {
157
+ if (tab.fields && Array.isArray(tab.fields)) {
158
+ if (updateFieldByPath(tab.fields, path, updates)) return true;
159
+ }
160
+ }
161
+ }
162
+ }
115
163
  }
116
164
  return false;
117
165
  }