@aphexcms/cms-core 0.1.3 → 0.1.4

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 (280) hide show
  1. package/dist/api/assets.d.ts +48 -0
  2. package/dist/api/assets.d.ts.map +1 -0
  3. package/dist/api/assets.js +52 -0
  4. package/dist/api/client.d.ts +37 -0
  5. package/dist/api/client.d.ts.map +1 -0
  6. package/dist/api/client.js +125 -0
  7. package/dist/api/documents.d.ts +56 -0
  8. package/dist/api/documents.d.ts.map +1 -0
  9. package/dist/api/documents.js +77 -0
  10. package/dist/api/index.d.ts +7 -0
  11. package/dist/api/index.d.ts.map +1 -0
  12. package/dist/api/index.js +5 -0
  13. package/dist/api/organizations.d.ts +101 -0
  14. package/dist/api/organizations.d.ts.map +1 -0
  15. package/dist/api/organizations.js +92 -0
  16. package/dist/api/types.d.ts +23 -0
  17. package/dist/api/types.d.ts.map +1 -0
  18. package/dist/api/types.js +1 -0
  19. package/dist/app.d.ts +19 -0
  20. package/dist/auth/MULTI_TENANCY_PLAN.md +1183 -0
  21. package/dist/auth/auth-errors.d.ts +7 -0
  22. package/dist/auth/auth-errors.d.ts.map +1 -0
  23. package/dist/auth/auth-errors.js +13 -0
  24. package/dist/auth/auth-hooks.d.ts +6 -0
  25. package/dist/auth/auth-hooks.d.ts.map +1 -0
  26. package/dist/auth/auth-hooks.js +108 -0
  27. package/dist/auth/provider.d.ts +17 -0
  28. package/dist/auth/provider.d.ts.map +1 -0
  29. package/dist/auth/provider.js +1 -0
  30. package/dist/client/index.d.ts +24 -0
  31. package/dist/client/index.d.ts.map +1 -0
  32. package/dist/client/index.js +31 -0
  33. package/dist/components/AdminApp.svelte +1077 -0
  34. package/dist/components/AdminApp.svelte.d.ts +24 -0
  35. package/dist/components/AdminApp.svelte.d.ts.map +1 -0
  36. package/dist/components/admin/AdminLayout.svelte +115 -0
  37. package/dist/components/admin/AdminLayout.svelte.d.ts +15 -0
  38. package/dist/components/admin/AdminLayout.svelte.d.ts.map +1 -0
  39. package/dist/components/admin/DocumentEditor.svelte +795 -0
  40. package/dist/components/admin/DocumentEditor.svelte.d.ts +18 -0
  41. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -0
  42. package/dist/components/admin/DocumentTypesList.svelte +97 -0
  43. package/dist/components/admin/DocumentTypesList.svelte.d.ts +14 -0
  44. package/dist/components/admin/DocumentTypesList.svelte.d.ts.map +1 -0
  45. package/dist/components/admin/ObjectModal.svelte +135 -0
  46. package/dist/components/admin/ObjectModal.svelte.d.ts +15 -0
  47. package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -0
  48. package/dist/components/admin/SchemaField.svelte +171 -0
  49. package/dist/components/admin/SchemaField.svelte.d.ts +19 -0
  50. package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -0
  51. package/dist/components/admin/fields/ArrayField.svelte +266 -0
  52. package/dist/components/admin/fields/ArrayField.svelte.d.ts +12 -0
  53. package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -0
  54. package/dist/components/admin/fields/BooleanField.svelte +35 -0
  55. package/dist/components/admin/fields/BooleanField.svelte.d.ts +13 -0
  56. package/dist/components/admin/fields/BooleanField.svelte.d.ts.map +1 -0
  57. package/dist/components/admin/fields/ImageField.svelte +284 -0
  58. package/dist/components/admin/fields/ImageField.svelte.d.ts +15 -0
  59. package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -0
  60. package/dist/components/admin/fields/NumberField.svelte +82 -0
  61. package/dist/components/admin/fields/NumberField.svelte.d.ts +14 -0
  62. package/dist/components/admin/fields/NumberField.svelte.d.ts.map +1 -0
  63. package/dist/components/admin/fields/ReferenceField.svelte +260 -0
  64. package/dist/components/admin/fields/ReferenceField.svelte.d.ts +12 -0
  65. package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -0
  66. package/dist/components/admin/fields/SlugField.svelte +74 -0
  67. package/dist/components/admin/fields/SlugField.svelte.d.ts +15 -0
  68. package/dist/components/admin/fields/SlugField.svelte.d.ts.map +1 -0
  69. package/dist/components/admin/fields/StringField.svelte +40 -0
  70. package/dist/components/admin/fields/StringField.svelte.d.ts +14 -0
  71. package/dist/components/admin/fields/StringField.svelte.d.ts.map +1 -0
  72. package/dist/components/admin/fields/TextareaField.svelte +40 -0
  73. package/dist/components/admin/fields/TextareaField.svelte.d.ts +14 -0
  74. package/dist/components/admin/fields/TextareaField.svelte.d.ts.map +1 -0
  75. package/dist/components/fields/index.d.ts +9 -0
  76. package/dist/components/fields/index.d.ts.map +1 -0
  77. package/dist/components/fields/index.js +9 -0
  78. package/dist/components/index.d.ts +7 -0
  79. package/dist/components/index.d.ts.map +1 -0
  80. package/dist/components/index.js +12 -0
  81. package/dist/components/layout/OrganizationSwitcher.svelte +218 -0
  82. package/dist/components/layout/OrganizationSwitcher.svelte.d.ts +11 -0
  83. package/dist/components/layout/OrganizationSwitcher.svelte.d.ts.map +1 -0
  84. package/dist/components/layout/Sidebar.svelte +88 -0
  85. package/dist/components/layout/Sidebar.svelte.d.ts +14 -0
  86. package/dist/components/layout/Sidebar.svelte.d.ts.map +1 -0
  87. package/dist/components/layout/sidebar/AppSidebar.svelte +63 -0
  88. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts +11 -0
  89. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts.map +1 -0
  90. package/dist/components/layout/sidebar/NavMain.svelte +95 -0
  91. package/dist/components/layout/sidebar/NavMain.svelte.d.ts +19 -0
  92. package/dist/components/layout/sidebar/NavMain.svelte.d.ts.map +1 -0
  93. package/dist/components/layout/sidebar/NavSecondary.svelte +69 -0
  94. package/dist/components/layout/sidebar/NavSecondary.svelte.d.ts +9 -0
  95. package/dist/components/layout/sidebar/NavSecondary.svelte.d.ts.map +1 -0
  96. package/dist/components/layout/sidebar/NavUser.svelte +85 -0
  97. package/dist/components/layout/sidebar/NavUser.svelte.d.ts +9 -0
  98. package/dist/components/layout/sidebar/NavUser.svelte.d.ts.map +1 -0
  99. package/dist/config.d.ts +3 -0
  100. package/dist/config.d.ts.map +1 -0
  101. package/dist/config.js +15 -0
  102. package/dist/db/adapters/index.d.ts +1 -0
  103. package/dist/db/adapters/index.d.ts.map +1 -0
  104. package/dist/db/adapters/index.js +4 -0
  105. package/dist/db/index.d.ts +2 -0
  106. package/dist/db/index.d.ts.map +1 -0
  107. package/dist/db/index.js +4 -0
  108. package/dist/db/interfaces/asset.d.ts +51 -0
  109. package/dist/db/interfaces/asset.d.ts.map +1 -0
  110. package/dist/db/interfaces/asset.js +1 -0
  111. package/dist/db/interfaces/document.d.ts +36 -0
  112. package/dist/db/interfaces/document.d.ts.map +1 -0
  113. package/dist/db/interfaces/document.js +1 -0
  114. package/dist/db/interfaces/index.d.ts +73 -0
  115. package/dist/db/interfaces/index.d.ts.map +1 -0
  116. package/dist/db/interfaces/index.js +1 -0
  117. package/dist/db/interfaces/organization.d.ts +27 -0
  118. package/dist/db/interfaces/organization.d.ts.map +1 -0
  119. package/dist/db/interfaces/organization.js +1 -0
  120. package/dist/db/interfaces/schema.d.ts +21 -0
  121. package/dist/db/interfaces/schema.d.ts.map +1 -0
  122. package/dist/db/interfaces/schema.js +1 -0
  123. package/dist/db/interfaces/user.d.ts +15 -0
  124. package/dist/db/interfaces/user.d.ts.map +1 -0
  125. package/dist/db/interfaces/user.js +1 -0
  126. package/dist/db/utils/reference-resolver.d.ts +18 -0
  127. package/dist/db/utils/reference-resolver.d.ts.map +1 -0
  128. package/dist/db/utils/reference-resolver.js +80 -0
  129. package/dist/define.d.ts +3 -0
  130. package/dist/define.d.ts.map +1 -0
  131. package/dist/define.js +4 -0
  132. package/dist/email/index.d.ts +2 -0
  133. package/dist/email/index.d.ts.map +1 -0
  134. package/dist/email/index.js +4 -0
  135. package/dist/email/interfaces/email.d.ts +42 -0
  136. package/dist/email/interfaces/email.d.ts.map +1 -0
  137. package/dist/email/interfaces/email.js +1 -0
  138. package/dist/engine.d.ts +26 -0
  139. package/dist/engine.d.ts.map +1 -0
  140. package/dist/engine.js +66 -0
  141. package/dist/field-validation/rule.d.ts +51 -0
  142. package/dist/field-validation/rule.d.ts.map +1 -0
  143. package/dist/field-validation/rule.js +221 -0
  144. package/dist/field-validation/utils.d.ts +21 -0
  145. package/dist/field-validation/utils.d.ts.map +1 -0
  146. package/dist/field-validation/utils.js +66 -0
  147. package/dist/hooks.d.ts +23 -0
  148. package/dist/hooks.d.ts.map +1 -0
  149. package/dist/hooks.js +96 -0
  150. package/dist/index.d.ts +2 -0
  151. package/dist/index.d.ts.map +1 -0
  152. package/dist/index.js +4 -0
  153. package/dist/plugins/README.md +154 -0
  154. package/dist/routes/assets-by-id.d.ts +5 -0
  155. package/dist/routes/assets-by-id.d.ts.map +1 -0
  156. package/dist/routes/assets-by-id.js +138 -0
  157. package/dist/routes/assets-cdn.d.ts +3 -0
  158. package/dist/routes/assets-cdn.d.ts.map +1 -0
  159. package/dist/routes/assets-cdn.js +155 -0
  160. package/dist/routes/assets.d.ts +4 -0
  161. package/dist/routes/assets.d.ts.map +1 -0
  162. package/dist/routes/assets.js +94 -0
  163. package/dist/routes/documents-by-id.d.ts +5 -0
  164. package/dist/routes/documents-by-id.d.ts.map +1 -0
  165. package/dist/routes/documents-by-id.js +142 -0
  166. package/dist/routes/documents-publish.d.ts +4 -0
  167. package/dist/routes/documents-publish.d.ts.map +1 -0
  168. package/dist/routes/documents-publish.js +151 -0
  169. package/dist/routes/documents.d.ts +4 -0
  170. package/dist/routes/documents.d.ts.map +1 -0
  171. package/dist/routes/documents.js +131 -0
  172. package/dist/routes/index.d.ts +6 -0
  173. package/dist/routes/index.d.ts.map +1 -0
  174. package/dist/routes/index.js +10 -0
  175. package/dist/routes/organizations-by-id.d.ts +5 -0
  176. package/dist/routes/organizations-by-id.d.ts.map +1 -0
  177. package/dist/routes/organizations-by-id.js +187 -0
  178. package/dist/routes/organizations-invitations.d.ts +4 -0
  179. package/dist/routes/organizations-invitations.d.ts.map +1 -0
  180. package/dist/routes/organizations-invitations.js +125 -0
  181. package/dist/routes/organizations-members.d.ts +5 -0
  182. package/dist/routes/organizations-members.d.ts.map +1 -0
  183. package/dist/routes/organizations-members.js +206 -0
  184. package/dist/routes/organizations-switch.d.ts +3 -0
  185. package/dist/routes/organizations-switch.d.ts.map +1 -0
  186. package/dist/routes/organizations-switch.js +53 -0
  187. package/dist/routes/organizations.d.ts +4 -0
  188. package/dist/routes/organizations.d.ts.map +1 -0
  189. package/dist/routes/organizations.js +108 -0
  190. package/dist/routes/schemas-by-type.d.ts +3 -0
  191. package/dist/routes/schemas-by-type.d.ts.map +1 -0
  192. package/dist/routes/schemas-by-type.js +25 -0
  193. package/dist/routes/schemas.d.ts +3 -0
  194. package/dist/routes/schemas.d.ts.map +1 -0
  195. package/dist/routes/schemas.js +11 -0
  196. package/dist/routes-exports.d.ts +14 -0
  197. package/dist/routes-exports.d.ts.map +1 -0
  198. package/dist/routes-exports.js +19 -0
  199. package/dist/schema-context.svelte.d.ts +10 -0
  200. package/dist/schema-context.svelte.d.ts.map +1 -0
  201. package/dist/schema-context.svelte.js +18 -0
  202. package/dist/schema-utils/cleanup.d.ts +21 -0
  203. package/dist/schema-utils/cleanup.d.ts.map +1 -0
  204. package/dist/schema-utils/cleanup.js +80 -0
  205. package/dist/schema-utils/index.d.ts +4 -0
  206. package/dist/schema-utils/index.d.ts.map +1 -0
  207. package/dist/schema-utils/index.js +4 -0
  208. package/dist/schema-utils/utils.d.ts +30 -0
  209. package/dist/schema-utils/utils.d.ts.map +1 -0
  210. package/dist/schema-utils/utils.js +37 -0
  211. package/dist/schema-utils/validator.d.ts +6 -0
  212. package/dist/schema-utils/validator.d.ts.map +1 -0
  213. package/dist/schema-utils/validator.js +45 -0
  214. package/dist/server/index.d.ts +16 -0
  215. package/dist/server/index.d.ts.map +1 -0
  216. package/dist/server/index.js +28 -0
  217. package/dist/services/asset-service.d.ts +86 -0
  218. package/dist/services/asset-service.d.ts.map +1 -0
  219. package/dist/services/asset-service.js +187 -0
  220. package/dist/services/index.d.ts +3 -0
  221. package/dist/services/index.d.ts.map +1 -0
  222. package/dist/services/index.js +4 -0
  223. package/dist/storage/adapters/index.d.ts +2 -0
  224. package/dist/storage/adapters/index.d.ts.map +1 -0
  225. package/dist/storage/adapters/index.js +2 -0
  226. package/dist/storage/adapters/local-storage-adapter.d.ts +54 -0
  227. package/dist/storage/adapters/local-storage-adapter.d.ts.map +1 -0
  228. package/dist/storage/adapters/local-storage-adapter.js +187 -0
  229. package/dist/storage/index.d.ts +3 -0
  230. package/dist/storage/index.d.ts.map +1 -0
  231. package/dist/storage/index.js +6 -0
  232. package/dist/storage/interfaces/index.d.ts +2 -0
  233. package/dist/storage/interfaces/index.d.ts.map +1 -0
  234. package/dist/storage/interfaces/index.js +2 -0
  235. package/dist/storage/interfaces/storage.d.ts +91 -0
  236. package/dist/storage/interfaces/storage.d.ts.map +1 -0
  237. package/dist/storage/interfaces/storage.js +1 -0
  238. package/dist/storage/providers/storage.d.ts +43 -0
  239. package/dist/storage/providers/storage.d.ts.map +1 -0
  240. package/dist/storage/providers/storage.js +64 -0
  241. package/dist/types/asset.d.ts +73 -0
  242. package/dist/types/asset.d.ts.map +1 -0
  243. package/dist/types/asset.js +2 -0
  244. package/dist/types/auth.d.ts +50 -0
  245. package/dist/types/auth.d.ts.map +1 -0
  246. package/dist/types/auth.js +41 -0
  247. package/dist/types/config.d.ts +47 -0
  248. package/dist/types/config.d.ts.map +1 -0
  249. package/dist/types/config.js +1 -0
  250. package/dist/types/document.d.ts +34 -0
  251. package/dist/types/document.d.ts.map +1 -0
  252. package/dist/types/document.js +1 -0
  253. package/dist/types/index.d.ts +9 -0
  254. package/dist/types/index.d.ts.map +1 -0
  255. package/dist/types/index.js +8 -0
  256. package/dist/types/organization.d.ts +105 -0
  257. package/dist/types/organization.d.ts.map +1 -0
  258. package/dist/types/organization.js +3 -0
  259. package/dist/types/schemas.d.ts +114 -0
  260. package/dist/types/schemas.d.ts.map +1 -0
  261. package/dist/types/schemas.js +1 -0
  262. package/dist/types/sidebar.d.ts +33 -0
  263. package/dist/types/sidebar.d.ts.map +1 -0
  264. package/dist/types/sidebar.js +1 -0
  265. package/dist/types/user.d.ts +14 -0
  266. package/dist/types/user.d.ts.map +1 -0
  267. package/dist/types/user.js +1 -0
  268. package/dist/utils/content-hash.d.ts +22 -0
  269. package/dist/utils/content-hash.d.ts.map +1 -0
  270. package/dist/utils/content-hash.js +67 -0
  271. package/dist/utils/image-url.d.ts +88 -0
  272. package/dist/utils/image-url.d.ts.map +1 -0
  273. package/dist/utils/image-url.js +165 -0
  274. package/dist/utils/index.d.ts +6 -0
  275. package/dist/utils/index.d.ts.map +1 -0
  276. package/dist/utils/index.js +9 -0
  277. package/dist/utils/slug.d.ts +13 -0
  278. package/dist/utils/slug.d.ts.map +1 -0
  279. package/dist/utils/slug.js +30 -0
  280. package/package.json +11 -41
@@ -0,0 +1,266 @@
1
+ <script lang="ts">
2
+ import { Button } from '@aphexcms/ui/shadcn/button';
3
+ import * as DropdownMenu from '@aphexcms/ui/shadcn/dropdown-menu';
4
+ import type { ArrayField as ArrayFieldType, SchemaType } from '../../../types/schemas.js';
5
+ import { getArrayTypes, getSchemaByName } from '../../../schema-utils/utils.js';
6
+ import { getSchemaContext } from '../../../schema-context.svelte.js';
7
+ import ObjectModal from '../ObjectModal.svelte';
8
+
9
+ interface Props {
10
+ field: ArrayFieldType;
11
+ value: any;
12
+ onUpdate: (value: any) => void;
13
+ onOpenReference?: (documentId: string, documentType: string) => void;
14
+ readonly?: boolean;
15
+ }
16
+
17
+ let { field, value, onUpdate, onOpenReference, readonly = false }: Props = $props();
18
+
19
+ // Get schemas from context
20
+ const schemas = getSchemaContext();
21
+
22
+ // Get available types for this array field
23
+ const availableTypes = $derived(getArrayTypes(schemas, field));
24
+
25
+ // Modal state
26
+ let modalOpen = $state(false);
27
+ let editingIndex = $state<number | null>(null);
28
+ let editingType = $state<string | null>(null);
29
+ let editingSchema = $state<SchemaType | null>(null);
30
+ let editingValue = $state<Record<string, any>>({});
31
+
32
+ // Ensure value is always an array
33
+ const arrayValue = $derived(Array.isArray(value) ? value : []);
34
+
35
+ function handleTypeSelected(selectedType: string) {
36
+ if (readonly || !selectedType) return;
37
+
38
+ // Get the schema for the selected type
39
+ const schema = getSchemaByName(schemas, selectedType);
40
+ if (!schema) return;
41
+
42
+ // Initialize empty object with default values
43
+ const newItem: Record<string, any> = { _type: selectedType };
44
+
45
+ if (schema.fields) {
46
+ schema.fields.forEach((field) => {
47
+ if (field.type === 'boolean' && 'initialValue' in field) {
48
+ newItem[field.name] = field.initialValue;
49
+ } else {
50
+ newItem[field.name] = '';
51
+ }
52
+ });
53
+ }
54
+
55
+ // Set modal state directly
56
+ editingIndex = arrayValue.length; // New item index
57
+ editingType = selectedType;
58
+ editingSchema = schema;
59
+ editingValue = newItem;
60
+ modalOpen = true;
61
+ }
62
+
63
+ function handleEditItem(index: number) {
64
+ const item = arrayValue[index];
65
+ if (!item._type) return;
66
+
67
+ const schema = getSchemaByName(schemas, item._type);
68
+ if (!schema) return;
69
+
70
+ editingIndex = index;
71
+ editingType = item._type;
72
+ editingSchema = schema;
73
+ editingValue = item;
74
+ modalOpen = true;
75
+ console.log('MODAL IS OPEN: ', modalOpen);
76
+ }
77
+
78
+ function handleRemoveItem(index: number) {
79
+ if (readonly) return;
80
+ const newArray = arrayValue.filter((_, i) => i !== index);
81
+ onUpdate(newArray);
82
+ }
83
+
84
+ function handleModalSave(editedData: Record<string, any>) {
85
+ if (editingIndex === null || !editingType) return;
86
+
87
+ // Add the type information to the data
88
+ const itemData = { ...editedData, _type: editingType };
89
+
90
+ const newArray = [...arrayValue];
91
+
92
+ if (editingIndex >= newArray.length) {
93
+ // Adding new item
94
+ newArray.push(itemData);
95
+ } else {
96
+ // Editing existing item
97
+ newArray[editingIndex] = itemData;
98
+ }
99
+
100
+ onUpdate(newArray);
101
+
102
+ // Reset modal state
103
+ modalOpen = false;
104
+ editingIndex = null;
105
+ editingType = null;
106
+ editingSchema = null;
107
+ editingValue = {};
108
+ }
109
+
110
+ function handleModalClose() {
111
+ modalOpen = false;
112
+ editingIndex = null;
113
+ editingType = null;
114
+ editingSchema = null;
115
+ editingValue = {};
116
+ }
117
+
118
+ // Get the title to display for an item
119
+ function getItemTitle(item: any): string {
120
+ if (!item._type) return 'Unknown Item';
121
+
122
+ const schema = getSchemaByName(schemas, item._type);
123
+ if (!schema) return item._type;
124
+
125
+ // Try to find a meaningful field to use as title
126
+ const titleField = item.title || item.heading || item.name || item.label;
127
+ if (titleField && typeof titleField === 'string' && titleField.trim()) {
128
+ return titleField;
129
+ }
130
+
131
+ return schema.title || item._type;
132
+ }
133
+ </script>
134
+
135
+ <div class="border-border space-y-4 rounded-md border p-4">
136
+ <h4 class="text-sm font-medium">{field.title}</h4>
137
+
138
+ <!-- Array items -->
139
+ {#if arrayValue.length > 0}
140
+ <div class="space-y-2">
141
+ {#each arrayValue as item, index (index)}
142
+ <div class="border-border/50 space-y-2 rounded border p-3">
143
+ <div class="flex items-center justify-between">
144
+ <div class="flex items-center gap-2">
145
+ <span class="text-muted-foreground text-xs">#{index + 1}</span>
146
+ <h5 class="text-sm font-medium">{getItemTitle(item)}</h5>
147
+ {#if item._type}
148
+ <span class="bg-muted rounded px-2 py-1 text-xs">{item._type}</span>
149
+ {/if}
150
+ </div>
151
+ <div class="flex items-center gap-2">
152
+ <Button
153
+ variant="ghost"
154
+ size="sm"
155
+ onclick={() => {
156
+ handleEditItem(index);
157
+ }}
158
+ class="h-8 w-8 p-0"
159
+ title={readonly ? 'View item' : 'Edit item'}
160
+ >
161
+ {#if readonly}
162
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
163
+ <path
164
+ stroke-linecap="round"
165
+ stroke-linejoin="round"
166
+ stroke-width="2"
167
+ d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
168
+ />
169
+ <path
170
+ stroke-linecap="round"
171
+ stroke-linejoin="round"
172
+ stroke-width="2"
173
+ d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
174
+ />
175
+ </svg>
176
+ {:else}
177
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
178
+ <path
179
+ stroke-linecap="round"
180
+ stroke-linejoin="round"
181
+ stroke-width="2"
182
+ d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
183
+ />
184
+ </svg>
185
+ {/if}
186
+ </Button>
187
+
188
+ {#if !readonly}
189
+ <Button
190
+ variant="ghost"
191
+ size="sm"
192
+ onclick={() => handleRemoveItem(index)}
193
+ class="text-destructive hover:text-destructive h-8 w-8 p-0"
194
+ title="Remove item"
195
+ >
196
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
197
+ <path
198
+ stroke-linecap="round"
199
+ stroke-linejoin="round"
200
+ stroke-width="2"
201
+ d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
202
+ />
203
+ </svg>
204
+ </Button>
205
+ {/if}
206
+ </div>
207
+ </div>
208
+
209
+ <!-- Show a preview of the item content -->
210
+ <div class="text-muted-foreground pl-6 text-xs">
211
+ {#if item.title || item.heading}
212
+ {item.title || item.heading}
213
+ {:else if item.description}
214
+ {item.description.substring(0, 100)}{item.description.length > 100 ? '...' : ''}
215
+ {:else}
216
+ {readonly ? 'Click view to see details' : 'Click edit to configure this item'}
217
+ {/if}
218
+ </div>
219
+ </div>
220
+ {/each}
221
+ </div>
222
+ {/if}
223
+
224
+ <!-- Add Item section (hidden for read-only) -->
225
+ {#if !readonly}
226
+ <div class="border-border border-t pt-2">
227
+ <DropdownMenu.Root>
228
+ <DropdownMenu.Trigger>
229
+ {#snippet child({ props })}
230
+ <Button {...props} variant="outline" class="w-full cursor-pointer">
231
+ <svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
232
+ <path
233
+ stroke-linecap="round"
234
+ stroke-linejoin="round"
235
+ stroke-width="2"
236
+ d="M12 4v16m8-8H4"
237
+ />
238
+ </svg>
239
+ Add Item
240
+ </Button>
241
+ {/snippet}
242
+ </DropdownMenu.Trigger>
243
+ <DropdownMenu.Content class="w-56">
244
+ {#each availableTypes as type, index (index)}
245
+ <DropdownMenu.Item onclick={() => handleTypeSelected(type.name)}>
246
+ {type.title}
247
+ </DropdownMenu.Item>
248
+ {/each}
249
+ </DropdownMenu.Content>
250
+ </DropdownMenu.Root>
251
+ </div>
252
+ {/if}
253
+ </div>
254
+
255
+ <!-- Object editing modal -->
256
+ {#if editingSchema}
257
+ <ObjectModal
258
+ open={modalOpen}
259
+ schema={editingSchema}
260
+ value={editingValue}
261
+ onClose={handleModalClose}
262
+ onSave={handleModalSave}
263
+ {onOpenReference}
264
+ {readonly}
265
+ />
266
+ {/if}
@@ -0,0 +1,12 @@
1
+ import type { ArrayField as ArrayFieldType } from '../../../types/schemas.js';
2
+ interface Props {
3
+ field: ArrayFieldType;
4
+ value: any;
5
+ onUpdate: (value: any) => void;
6
+ onOpenReference?: (documentId: string, documentType: string) => void;
7
+ readonly?: boolean;
8
+ }
9
+ declare const ArrayField: import("svelte").Component<Props, {}, "">;
10
+ type ArrayField = ReturnType<typeof ArrayField>;
11
+ export default ArrayField;
12
+ //# sourceMappingURL=ArrayField.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,2BAA2B,CAAC;AAMzF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AA0NF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import { Label } from '@aphexcms/ui/shadcn/label';
3
+ import type { Field } from '../../../types/schemas.js';
4
+
5
+ interface Props {
6
+ field: Field;
7
+ value: any;
8
+ onUpdate: (value: any) => void;
9
+ validationClasses?: string;
10
+ onBlur?: (event: any) => void;
11
+ readonly?: boolean;
12
+ }
13
+
14
+ let { field, value, onUpdate, validationClasses, onBlur, readonly = false }: Props = $props();
15
+
16
+ function handleBooleanChange(event: Event) {
17
+ const target = event.target as HTMLInputElement;
18
+ onUpdate(target.checked);
19
+ }
20
+ </script>
21
+
22
+ <div class="flex items-center space-x-2">
23
+ <input
24
+ id={field.name}
25
+ type="checkbox"
26
+ checked={value || false}
27
+ onchange={handleBooleanChange}
28
+ onblur={onBlur}
29
+ class="border-input h-4 w-4 rounded border {validationClasses}"
30
+ disabled={readonly}
31
+ />
32
+ <Label for={field.name} class="text-sm font-normal">
33
+ {field.title}
34
+ </Label>
35
+ </div>
@@ -0,0 +1,13 @@
1
+ import type { Field } from '../../../types/schemas.js';
2
+ interface Props {
3
+ field: Field;
4
+ value: any;
5
+ onUpdate: (value: any) => void;
6
+ validationClasses?: string;
7
+ onBlur?: (event: any) => void;
8
+ readonly?: boolean;
9
+ }
10
+ declare const BooleanField: import("svelte").Component<Props, {}, "">;
11
+ type BooleanField = ReturnType<typeof BooleanField>;
12
+ export default BooleanField;
13
+ //# sourceMappingURL=BooleanField.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BooleanField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/BooleanField.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAGtD,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAuBF,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,284 @@
1
+ <script lang="ts">
2
+ import { Button } from '@aphexcms/ui/shadcn/button';
3
+ import { Trash2, Upload, Image as ImageIcon, FileImage } from 'lucide-svelte';
4
+ import type { ImageValue } from '../../../types/asset.js';
5
+ import type { ImageField as ImageFieldType } from '../../../types/schemas.js';
6
+ import { assets } from '../../../api/assets';
7
+
8
+ interface Props {
9
+ field: ImageFieldType;
10
+ value: ImageValue | null;
11
+ validationClasses?: string;
12
+ onUpdate: (value: ImageValue | null) => void;
13
+ schemaType?: string;
14
+ fieldPath?: string;
15
+ readonly?: boolean;
16
+ }
17
+
18
+ let {
19
+ field,
20
+ value,
21
+ onUpdate,
22
+ validationClasses,
23
+ schemaType,
24
+ fieldPath,
25
+ readonly = false
26
+ }: Props = $props();
27
+
28
+ // Component state
29
+ let isDragging = $state(false);
30
+ let isUploading = $state(false);
31
+ let uploadError = $state<string | null>(null);
32
+ let fileInputRef: HTMLInputElement;
33
+
34
+ // Upload file to server
35
+ async function uploadFile(file: File): Promise<ImageValue | null> {
36
+ isUploading = true;
37
+ uploadError = null;
38
+
39
+ try {
40
+ const formData = new FormData();
41
+ formData.append('file', file);
42
+
43
+ // Add field metadata for privacy checking
44
+ if (schemaType) formData.append('schemaType', schemaType);
45
+ if (fieldPath) formData.append('fieldPath', fieldPath);
46
+
47
+ const result = await assets.upload(formData);
48
+
49
+ if (!result.success) {
50
+ throw new Error(result.error || 'Upload failed');
51
+ }
52
+
53
+ // Extract asset from response
54
+ const asset = result.data;
55
+
56
+ // Return Sanity-style image value
57
+ return {
58
+ _type: 'image',
59
+ asset: {
60
+ _type: 'reference',
61
+ _ref: asset!.id
62
+ }
63
+ };
64
+ } catch (error) {
65
+ uploadError = error instanceof Error ? error.message : 'Upload failed';
66
+ return null;
67
+ } finally {
68
+ isUploading = false;
69
+ }
70
+ }
71
+
72
+ // Handle file selection
73
+ async function handleFileSelect(files: FileList | null) {
74
+ if (readonly || !files || files.length === 0) return;
75
+
76
+ const file = files[0];
77
+
78
+ const imageValue = await uploadFile(file);
79
+ if (imageValue) {
80
+ onUpdate(imageValue);
81
+ }
82
+ }
83
+
84
+ // Drag and drop handlers
85
+ function handleDragOver(event: DragEvent) {
86
+ if (readonly) return;
87
+ event.preventDefault();
88
+ isDragging = true;
89
+ }
90
+
91
+ function handleDragLeave(event: DragEvent) {
92
+ if (readonly) return;
93
+ event.preventDefault();
94
+ isDragging = false;
95
+ }
96
+
97
+ function handleDrop(event: DragEvent) {
98
+ if (readonly) return;
99
+ event.preventDefault();
100
+ isDragging = false;
101
+ handleFileSelect(event.dataTransfer?.files || null);
102
+ }
103
+
104
+ // File input handlers
105
+ function handleFileInputChange(event: Event) {
106
+ if (readonly) return;
107
+ const target = event.target as HTMLInputElement;
108
+ handleFileSelect(target.files);
109
+ }
110
+
111
+ function openFileDialog() {
112
+ if (readonly) return;
113
+ fileInputRef?.click();
114
+ }
115
+
116
+ // Remove image
117
+ function removeImage() {
118
+ if (readonly) return;
119
+ onUpdate(null);
120
+ uploadError = null;
121
+ }
122
+
123
+ // Asset data state
124
+ let assetData = $state<any>(null);
125
+ let loadingAsset = $state(false);
126
+
127
+ // Fetch asset details when asset reference changes
128
+ $effect(() => {
129
+ async function loadAsset() {
130
+ if (value?.asset?._ref) {
131
+ loadingAsset = true;
132
+ try {
133
+ const result = await assets.getById(value.asset._ref);
134
+ if (result.success) {
135
+ assetData = result.data;
136
+ } else {
137
+ console.error('Failed to fetch asset details');
138
+ assetData = null;
139
+ }
140
+ } catch (error) {
141
+ console.error('Error fetching asset:', error);
142
+ assetData = null;
143
+ } finally {
144
+ loadingAsset = false;
145
+ }
146
+ } else {
147
+ assetData = null;
148
+ }
149
+ }
150
+ loadAsset();
151
+ });
152
+
153
+ // Get asset URL for preview
154
+ const previewUrl = $derived(assetData?.url || null);
155
+ </script>
156
+
157
+ <!-- Hidden file input -->
158
+ <input
159
+ bind:this={fileInputRef}
160
+ type="file"
161
+ accept={field.accept || 'image/*'}
162
+ style="display: none"
163
+ onchange={handleFileInputChange}
164
+ />
165
+
166
+ {#if value && value.asset}
167
+ <!-- Image preview with controls -->
168
+ <div class="border-border overflow-hidden rounded-md border {validationClasses}">
169
+ <div class="group relative">
170
+ <!-- Image preview (Sanity-style aspect ratio ~2.75:1) -->
171
+ <div class="bg-muted flex items-center justify-center" style="aspect-ratio: 2.75 / 1;">
172
+ {#if loadingAsset}
173
+ <div class="text-muted-foreground flex flex-col items-center gap-2">
174
+ <div class="border-primary h-8 w-8 animate-spin rounded-full border-b-2"></div>
175
+ <span class="text-sm">Loading image...</span>
176
+ </div>
177
+ {:else if previewUrl}
178
+ <img
179
+ src={previewUrl}
180
+ alt={assetData?.alt || 'Uploaded image'}
181
+ class="h-full w-full object-contain"
182
+ loading="lazy"
183
+ />
184
+ {:else}
185
+ <div class="text-muted-foreground flex flex-col items-center gap-2">
186
+ <ImageIcon size={32} />
187
+ <span class="text-sm">Image: {value.asset._ref}</span>
188
+ <span class="text-xs">Failed to load preview</span>
189
+ </div>
190
+ {/if}
191
+ </div>
192
+
193
+ <!-- Overlay controls (hidden for read-only) -->
194
+ {#if !readonly}
195
+ <div
196
+ class="absolute inset-0 flex items-center justify-center gap-2 bg-black/50 opacity-0 transition-opacity group-hover:opacity-100"
197
+ >
198
+ <Button variant="secondary" size="sm" onclick={openFileDialog} disabled={isUploading}>
199
+ <Upload size={16} class="mr-1" />
200
+ Replace
201
+ </Button>
202
+ <Button variant="destructive" size="sm" onclick={removeImage} disabled={isUploading}>
203
+ <Trash2 size={16} class="mr-1" />
204
+ Remove
205
+ </Button>
206
+ </div>
207
+ {/if}
208
+ </div>
209
+
210
+ <!-- Additional image controls/metadata could go here -->
211
+ {#if field.fields}
212
+ <div class="border-border space-y-2 border-t p-3">
213
+ <!-- Custom fields like caption, alt text, etc. would be rendered here -->
214
+ <p class="text-muted-foreground text-xs">Custom fields coming soon...</p>
215
+ </div>
216
+ {/if}
217
+ </div>
218
+ {:else}
219
+ <!-- Sanity-style upload bar -->
220
+ <div class="border-border overflow-hidden rounded-md border {validationClasses}">
221
+ <div class="flex items-center">
222
+ <!-- Drag and drop area (left side) -->
223
+ <div
224
+ class="flex-1 px-4 py-3 transition-colors {readonly
225
+ ? ''
226
+ : isDragging
227
+ ? 'bg-primary/5'
228
+ : 'hover:bg-muted/50'}"
229
+ ondragover={readonly ? undefined : handleDragOver}
230
+ ondragleave={readonly ? undefined : handleDragLeave}
231
+ ondrop={readonly ? undefined : handleDrop}
232
+ tabindex={readonly ? -1 : 0}
233
+ role={readonly ? undefined : 'button'}
234
+ >
235
+ {#if isUploading}
236
+ <div class="flex items-center gap-3">
237
+ <div class="border-primary h-5 w-5 animate-spin rounded-full border-b-2"></div>
238
+ <span class="text-muted-foreground text-sm">Uploading...</span>
239
+ </div>
240
+ {:else}
241
+ <div class="flex items-center gap-3">
242
+ <FileImage size={20} class="text-muted-foreground" />
243
+ <span class="text-muted-foreground text-sm">
244
+ {readonly ? 'No image' : isDragging ? 'Drop image here' : 'Drag or paste image here'}
245
+ </span>
246
+ </div>
247
+ {/if}
248
+ </div>
249
+
250
+ <!-- Buttons (right side) -->
251
+ <div class="border-border bg-muted/20 flex items-center gap-2 border-l px-3 py-2">
252
+ <Button
253
+ variant="outline"
254
+ size="sm"
255
+ onclick={openFileDialog}
256
+ disabled={isUploading || readonly}
257
+ type="button"
258
+ >
259
+ <Upload size={16} class="mr-1" />
260
+ Upload
261
+ </Button>
262
+
263
+ <Button
264
+ variant="outline"
265
+ size="sm"
266
+ disabled={isUploading || readonly}
267
+ type="button"
268
+ onclick={() => {
269
+ // TODO: Open asset browser/selector
270
+ console.log('Open asset selector');
271
+ }}
272
+ >
273
+ <ImageIcon size={16} class="mr-1" />
274
+ Select
275
+ </Button>
276
+ </div>
277
+ </div>
278
+ </div>
279
+ {/if}
280
+
281
+ <!-- Error display -->
282
+ {#if uploadError}
283
+ <p class="text-destructive mt-2 text-sm">{uploadError}</p>
284
+ {/if}
@@ -0,0 +1,15 @@
1
+ import type { ImageValue } from '../../../types/asset.js';
2
+ import type { ImageField as ImageFieldType } from '../../../types/schemas.js';
3
+ interface Props {
4
+ field: ImageFieldType;
5
+ value: ImageValue | null;
6
+ validationClasses?: string;
7
+ onUpdate: (value: ImageValue | null) => void;
8
+ schemaType?: string;
9
+ fieldPath?: string;
10
+ readonly?: boolean;
11
+ }
12
+ declare const ImageField: import("svelte").Component<Props, {}, "">;
13
+ type ImageField = ReturnType<typeof ImageField>;
14
+ export default ImageField;
15
+ //# sourceMappingURL=ImageField.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ImageField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAI7E,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAuPF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}