@kyro-cms/admin 0.1.7 → 0.1.8

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.
Files changed (71) hide show
  1. package/package.json +5 -3
  2. package/src/components/Admin.tsx +1 -1
  3. package/src/components/AutoForm.tsx +966 -337
  4. package/src/components/CreateView.tsx +1 -1
  5. package/src/components/DetailView.tsx +1 -1
  6. package/src/components/EnhancedListView.tsx +156 -52
  7. package/src/components/ListView.tsx +1 -1
  8. package/src/components/Modal.tsx +65 -8
  9. package/src/components/Sidebar.astro +2 -2
  10. package/src/components/ThemeProvider.tsx +8 -2
  11. package/src/components/blocks/AccordionBlock.tsx +20 -52
  12. package/src/components/blocks/ArrayBlock.tsx +40 -31
  13. package/src/components/blocks/BlockEditModal.tsx +170 -581
  14. package/src/components/blocks/ButtonBlock.tsx +27 -128
  15. package/src/components/blocks/CodeBlock.tsx +88 -40
  16. package/src/components/blocks/ColumnsBlock.tsx +27 -85
  17. package/src/components/blocks/FileBlock.tsx +38 -39
  18. package/src/components/blocks/HeadingBlock.tsx +9 -31
  19. package/src/components/blocks/HeroBlock.tsx +42 -100
  20. package/src/components/blocks/ImageBlock.tsx +6 -7
  21. package/src/components/blocks/LinkBlock.tsx +27 -33
  22. package/src/components/blocks/ListBlock.tsx +47 -26
  23. package/src/components/blocks/RelationshipBlock.tsx +26 -233
  24. package/src/components/blocks/RichTextBlock.tsx +66 -0
  25. package/src/components/blocks/VStackBlock.tsx +23 -37
  26. package/src/components/blocks/VideoBlock.tsx +52 -32
  27. package/src/components/fields/AccordionField.tsx +213 -0
  28. package/src/components/fields/ArrayField.tsx +241 -0
  29. package/src/components/fields/BlocksField.tsx +5 -5
  30. package/src/components/fields/ButtonField.tsx +53 -0
  31. package/src/components/fields/CheckboxField.tsx +7 -3
  32. package/src/components/fields/ChildrenField.tsx +48 -0
  33. package/src/components/fields/CodeField.tsx +154 -94
  34. package/src/components/fields/ColumnsField.tsx +137 -0
  35. package/src/components/fields/DateField.tsx +9 -24
  36. package/src/components/fields/EditorClient.tsx +426 -160
  37. package/src/components/fields/HeadingField.tsx +31 -0
  38. package/src/components/fields/HeroField.tsx +101 -0
  39. package/src/components/fields/JSONField.tsx +7 -27
  40. package/src/components/fields/LinkField.tsx +81 -0
  41. package/src/components/fields/ListField.tsx +74 -0
  42. package/src/components/fields/MarkdownField.tsx +4 -26
  43. package/src/components/fields/NumberField.tsx +9 -27
  44. package/src/components/fields/PortableTextField.tsx +61 -49
  45. package/src/components/fields/RelationshipBlockField.tsx +233 -0
  46. package/src/components/fields/RelationshipField.tsx +59 -13
  47. package/src/components/fields/SelectField.tsx +6 -4
  48. package/src/components/fields/TextField.tsx +9 -24
  49. package/src/components/fields/UploadField.tsx +613 -0
  50. package/src/components/fields/VideoField.tsx +73 -0
  51. package/src/components/fields/extensions/blockComponents.tsx +11 -1
  52. package/src/components/fields/extensions/blocksStore.ts +1 -1
  53. package/src/components/fields/index.ts +12 -1
  54. package/src/components/layout/Layout.tsx +1 -1
  55. package/src/lib/api.ts +163 -0
  56. package/src/lib/config.ts +1 -1
  57. package/src/lib/dataStore.ts +87 -30
  58. package/src/lib/date-utils.ts +69 -0
  59. package/src/lib/db/version-adapter.ts +248 -0
  60. package/src/lib/i18n.tsx +353 -0
  61. package/src/lib/slugify.ts +15 -0
  62. package/src/lib/validation.ts +250 -0
  63. package/src/pages/api/[collection]/[id]/publish.ts +12 -4
  64. package/src/pages/api/[collection]/[id]/versions.ts +39 -9
  65. package/src/pages/api/[collection]/[id].ts +13 -1
  66. package/src/pages/api/[collection]/index.ts +5 -6
  67. package/src/styles/main.css +12 -2
  68. package/src/components/blocks/BlockEditModal.MARKER +0 -12
  69. package/src/components/fields/FileField.tsx +0 -390
  70. package/src/components/fields/HybridContentField.tsx +0 -109
  71. package/src/components/fields/ImageField.tsx +0 -429
@@ -3,95 +3,63 @@ import {
3
3
  useBlockById,
4
4
  useBlockActions,
5
5
  } from "../fields/extensions/blocksStore";
6
- import { ChevronRight, X, ChevronDown, ChevronUp } from "lucide-react";
6
+ import { ChevronRight, X } from "lucide-react";
7
+ import { AccordionField } from "../fields/AccordionField";
7
8
 
8
- interface AccordionBlockProps {
9
- block: any;
10
- index: number;
11
- }
12
-
13
- export const AccordionBlock: React.FC<AccordionBlockProps> = ({
9
+ export const AccordionBlock: React.FC<{ block: any; index: number }> = ({
14
10
  block,
15
11
  index,
16
12
  }) => {
17
13
  const blockData = useBlockById(block.id);
18
14
  const { updateBlock, removeBlock, moveBlock } = useBlockActions();
15
+
19
16
  const data = blockData?.data ?? block.data ?? {};
20
17
  const items = Array.isArray(data.items) ? data.items : [];
21
- const [openIndex, setOpenIndex] = React.useState<number | null>(0);
18
+
19
+ const handleChange = (items: any[]) => {
20
+ updateBlock(block.id, { data: { ...data, items } });
21
+ };
22
22
 
23
23
  return (
24
- <div className="block-accordion border border-[var(--kyro-border)] rounded-lg p-4 mb-4 relative group bg-[var(--kyro-surface)]">
25
- <div className="flex items-center justify-between mb-4">
24
+ <div className="block-accordion border border-[var(--kyro-border)] rounded-md p-3 mb-2 relative group">
25
+ <div className="flex items-center justify-between mb-2">
26
26
  <div className="flex items-center gap-2">
27
- <span className="text-sm font-medium text-[var(--kyro-text-primary)]">
27
+ <span className="text-xs font-semibold text-[var(--kyro-text-muted)] uppercase">
28
28
  Accordion
29
29
  </span>
30
- <span className="text-xs text-[var(--kyro-text-muted)]">
30
+ <span className="text-[10px] text-[var(--kyro-text-muted)]">
31
31
  ({items.length} items)
32
32
  </span>
33
33
  </div>
34
- <div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
34
+ <div className="flex gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
35
35
  <button
36
36
  type="button"
37
37
  onClick={() => moveBlock(block.id, "up")}
38
- className="p-1.5 hover:bg-[var(--kyro-surface-accent)] rounded"
38
+ className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
39
39
  title="Move up"
40
40
  >
41
- <ChevronRight className="w-3.5 h-3.5 rotate-[-90deg] text-[var(--kyro-text-muted)]" />
41
+ <ChevronRight className="w-3 h-3 rotate-90" />
42
42
  </button>
43
43
  <button
44
44
  type="button"
45
45
  onClick={() => moveBlock(block.id, "down")}
46
- className="p-1.5 hover:bg-[var(--kyro-surface-accent)] rounded"
46
+ className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
47
47
  title="Move down"
48
48
  >
49
- <ChevronRight className="w-3.5 h-3.5 rotate-90 text-[var(--kyro-text-muted)]" />
49
+ <ChevronRight className="w-3 h-3" />
50
50
  </button>
51
51
  <button
52
52
  type="button"
53
53
  onClick={() => removeBlock(block.id)}
54
- className="p-1.5 hover:bg-red-50 rounded"
54
+ className="p-1 hover:bg-red-50 rounded text-red-500"
55
55
  title="Remove"
56
56
  >
57
- <X className="w-3.5 h-3.5 text-red-500" />
57
+ <X className="w-3 h-3" />
58
58
  </button>
59
59
  </div>
60
60
  </div>
61
61
 
62
- <div className="space-y-2">
63
- {items.map((item: any, i: number) => {
64
- const isOpen = openIndex === i;
65
- return (
66
- <div
67
- key={i}
68
- className="border border-[var(--kyro-border)] rounded-lg overflow-hidden"
69
- >
70
- <button
71
- type="button"
72
- onClick={() => setOpenIndex(isOpen ? null : i)}
73
- className="w-full flex items-center justify-between p-3 bg-[var(--kyro-surface-accent)] hover:bg-[var(--kyro-sidebar-active)]/10 transition-colors"
74
- >
75
- <span className="text-sm font-medium text-[var(--kyro-text-primary)]">
76
- {item.title || `Item ${i + 1}`}
77
- </span>
78
- {isOpen ? (
79
- <ChevronUp className="w-4 h-4 text-[var(--kyro-text-muted)]" />
80
- ) : (
81
- <ChevronDown className="w-4 h-4 text-[var(--kyro-text-muted)]" />
82
- )}
83
- </button>
84
- {isOpen && (
85
- <div className="p-3 bg-[var(--kyro-surface)]">
86
- <p className="text-sm text-[var(--kyro-text-primary)] whitespace-pre-wrap">
87
- {item.content || "No content"}
88
- </p>
89
- </div>
90
- )}
91
- </div>
92
- );
93
- })}
94
- </div>
62
+ <AccordionField items={items} onChange={handleChange} compact />
95
63
  </div>
96
64
  );
97
65
  };
@@ -4,71 +4,80 @@ import {
4
4
  useBlockActions,
5
5
  } from "../fields/extensions/blocksStore";
6
6
  import { ChevronRight, X } from "lucide-react";
7
+ import { ArrayField } from "../fields/ArrayField";
7
8
  import { ChildBlocksTree } from "./ChildBlocksTree";
8
9
 
9
- interface ArrayBlockProps {
10
- block: any;
11
- index: number;
12
- }
13
-
14
- export const ArrayBlock: React.FC<ArrayBlockProps> = ({ block, index }) => {
10
+ export const ArrayBlock: React.FC<{ block: any; index: number }> = ({
11
+ block,
12
+ index,
13
+ }) => {
15
14
  const blockData = useBlockById(block.id);
16
15
  const { updateBlock, removeBlock, moveBlock } = useBlockActions();
16
+
17
17
  const data = blockData?.data ?? block.data ?? {};
18
18
  const children = blockData?.children ?? block.children ?? [];
19
-
20
- const handleUpdateChildren = (newChildren: any[]) => {
21
- updateBlock(block.id, { children: newChildren });
22
- };
19
+ const items = Array.isArray(data.items) ? data.items : [];
23
20
 
24
21
  return (
25
- <div className="block-array border border-[var(--kyro-border)] rounded-lg p-4 mb-4 relative group bg-[var(--kyro-surface)]">
26
- <div className="flex items-center justify-between mb-4">
22
+ <div className="block-array border border-[var(--kyro-border)] rounded-md p-3 mb-2 relative group">
23
+ <div className="flex items-center justify-between mb-2">
27
24
  <div className="flex items-center gap-2">
28
- <span className="text-sm font-medium text-[var(--kyro-text-primary)]">
25
+ <span className="text-xs font-semibold text-[var(--kyro-text-muted)] uppercase">
29
26
  Repeater
30
27
  </span>
31
- <span className="text-xs text-[var(--kyro-text-muted)]">
32
- ({children.length} children)
28
+ <span className="text-[10px] text-[var(--kyro-text-muted)]">
29
+ ({items.length + children.length} items)
33
30
  </span>
34
31
  </div>
35
- <div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
32
+ <div className="flex gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
36
33
  <button
37
34
  type="button"
38
35
  onClick={() => moveBlock(block.id, "up")}
39
- className="p-1.5 hover:bg-[var(--kyro-surface-accent)] rounded"
36
+ className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
40
37
  title="Move up"
41
38
  >
42
- <ChevronRight className="w-3.5 h-3.5 rotate-[-90deg] text-[var(--kyro-text-muted)]" />
39
+ <ChevronRight className="w-3 h-3 rotate-90" />
43
40
  </button>
44
41
  <button
45
42
  type="button"
46
43
  onClick={() => moveBlock(block.id, "down")}
47
- className="p-1.5 hover:bg-[var(--kyro-surface-accent)] rounded"
44
+ className="p-1 hover:bg-[var(--kyro-surface-accent)] rounded"
48
45
  title="Move down"
49
46
  >
50
- <ChevronRight className="w-3.5 h-3.5 rotate-90 text-[var(--kyro-text-muted)]" />
47
+ <ChevronRight className="w-3 h-3" />
51
48
  </button>
52
49
  <button
53
50
  type="button"
54
51
  onClick={() => removeBlock(block.id)}
55
- className="p-1.5 hover:bg-red-50 rounded"
52
+ className="p-1 hover:bg-red-50 rounded text-red-500"
56
53
  title="Remove"
57
54
  >
58
- <X className="w-3.5 h-3.5 text-red-500" />
55
+ <X className="w-3 h-3" />
59
56
  </button>
60
57
  </div>
61
58
  </div>
62
59
 
63
- <div className="pt-4 border-t border-[var(--kyro-border)]">
64
- <label className="text-xs font-medium text-[var(--kyro-text-muted)] mb-2 block">
65
- Children
66
- </label>
67
- <ChildBlocksTree
68
- blockId={block.id}
69
- children={children}
70
- onUpdateChildren={handleUpdateChildren}
71
- />
60
+ <div className="space-y-3">
61
+ {items.length > 0 && (
62
+ <ArrayField
63
+ items={items}
64
+ onChange={(newItems) =>
65
+ updateBlock(block.id, { data: { ...data, items: newItems } })
66
+ }
67
+ compact
68
+ />
69
+ )}
70
+
71
+ <div className="pt-2 border-t border-[var(--kyro-border)]">
72
+ <label className="text-[10px] font-medium text-[var(--kyro-text-muted)] mb-1.5 block">
73
+ Children ({children.length})
74
+ </label>
75
+ <ChildBlocksTree
76
+ blockId={block.id}
77
+ children={children}
78
+ onUpdateChildren={(c) => updateBlock(block.id, { children: c })}
79
+ />
80
+ </div>
72
81
  </div>
73
82
  </div>
74
83
  );