@fnd-platform/cms 1.0.0-alpha.1

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 (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/lib/cms-project.d.ts +127 -0
  4. package/lib/cms-project.d.ts.map +1 -0
  5. package/lib/cms-project.js +343 -0
  6. package/lib/cms-project.js.map +1 -0
  7. package/lib/index.d.ts +11 -0
  8. package/lib/index.d.ts.map +1 -0
  9. package/lib/index.js +20 -0
  10. package/lib/index.js.map +1 -0
  11. package/lib/options.d.ts +59 -0
  12. package/lib/options.d.ts.map +1 -0
  13. package/lib/options.js +3 -0
  14. package/lib/options.js.map +1 -0
  15. package/lib/templates/admin-breadcrumbs.d.ts +13 -0
  16. package/lib/templates/admin-breadcrumbs.d.ts.map +1 -0
  17. package/lib/templates/admin-breadcrumbs.js +80 -0
  18. package/lib/templates/admin-breadcrumbs.js.map +1 -0
  19. package/lib/templates/admin-content-route.d.ts +18 -0
  20. package/lib/templates/admin-content-route.d.ts.map +1 -0
  21. package/lib/templates/admin-content-route.js +100 -0
  22. package/lib/templates/admin-content-route.js.map +1 -0
  23. package/lib/templates/admin-content-type-route.d.ts +9 -0
  24. package/lib/templates/admin-content-type-route.d.ts.map +1 -0
  25. package/lib/templates/admin-content-type-route.js +96 -0
  26. package/lib/templates/admin-content-type-route.js.map +1 -0
  27. package/lib/templates/admin-header.d.ts +13 -0
  28. package/lib/templates/admin-header.d.ts.map +1 -0
  29. package/lib/templates/admin-header.js +123 -0
  30. package/lib/templates/admin-header.js.map +1 -0
  31. package/lib/templates/admin-index.d.ts +9 -0
  32. package/lib/templates/admin-index.d.ts.map +1 -0
  33. package/lib/templates/admin-index.js +60 -0
  34. package/lib/templates/admin-index.js.map +1 -0
  35. package/lib/templates/admin-layout.d.ts +10 -0
  36. package/lib/templates/admin-layout.d.ts.map +1 -0
  37. package/lib/templates/admin-layout.js +46 -0
  38. package/lib/templates/admin-layout.js.map +1 -0
  39. package/lib/templates/admin-sidebar.d.ts +13 -0
  40. package/lib/templates/admin-sidebar.d.ts.map +1 -0
  41. package/lib/templates/admin-sidebar.js +149 -0
  42. package/lib/templates/admin-sidebar.js.map +1 -0
  43. package/lib/templates/content-editor.d.ts +10 -0
  44. package/lib/templates/content-editor.d.ts.map +1 -0
  45. package/lib/templates/content-editor.js +354 -0
  46. package/lib/templates/content-editor.js.map +1 -0
  47. package/lib/templates/content-schema.d.ts +10 -0
  48. package/lib/templates/content-schema.d.ts.map +1 -0
  49. package/lib/templates/content-schema.js +274 -0
  50. package/lib/templates/content-schema.js.map +1 -0
  51. package/lib/templates/content-table.d.ts +13 -0
  52. package/lib/templates/content-table.d.ts.map +1 -0
  53. package/lib/templates/content-table.js +177 -0
  54. package/lib/templates/content-table.js.map +1 -0
  55. package/lib/templates/content-types-examples.d.ts +19 -0
  56. package/lib/templates/content-types-examples.d.ts.map +1 -0
  57. package/lib/templates/content-types-examples.js +275 -0
  58. package/lib/templates/content-types-examples.js.map +1 -0
  59. package/lib/templates/content-types-registry.d.ts +10 -0
  60. package/lib/templates/content-types-registry.d.ts.map +1 -0
  61. package/lib/templates/content-types-registry.js +87 -0
  62. package/lib/templates/content-types-registry.js.map +1 -0
  63. package/lib/templates/content-types.d.ts +10 -0
  64. package/lib/templates/content-types.d.ts.map +1 -0
  65. package/lib/templates/content-types.js +384 -0
  66. package/lib/templates/content-types.js.map +1 -0
  67. package/lib/templates/dashboard-stats.d.ts +13 -0
  68. package/lib/templates/dashboard-stats.d.ts.map +1 -0
  69. package/lib/templates/dashboard-stats.js +117 -0
  70. package/lib/templates/dashboard-stats.js.map +1 -0
  71. package/lib/templates/editor/index.d.ts +6 -0
  72. package/lib/templates/editor/index.d.ts.map +1 -0
  73. package/lib/templates/editor/index.js +21 -0
  74. package/lib/templates/editor/index.js.map +1 -0
  75. package/lib/templates/editor/rich-text-editor.d.ts +7 -0
  76. package/lib/templates/editor/rich-text-editor.d.ts.map +1 -0
  77. package/lib/templates/editor/rich-text-editor.js +115 -0
  78. package/lib/templates/editor/rich-text-editor.js.map +1 -0
  79. package/lib/templates/editor/toolbar.d.ts +7 -0
  80. package/lib/templates/editor/toolbar.d.ts.map +1 -0
  81. package/lib/templates/editor/toolbar.js +272 -0
  82. package/lib/templates/editor/toolbar.js.map +1 -0
  83. package/lib/templates/form-fields/boolean-field.d.ts +7 -0
  84. package/lib/templates/form-fields/boolean-field.d.ts.map +1 -0
  85. package/lib/templates/form-fields/boolean-field.js +76 -0
  86. package/lib/templates/form-fields/boolean-field.js.map +1 -0
  87. package/lib/templates/form-fields/date-field.d.ts +7 -0
  88. package/lib/templates/form-fields/date-field.d.ts.map +1 -0
  89. package/lib/templates/form-fields/date-field.js +61 -0
  90. package/lib/templates/form-fields/date-field.js.map +1 -0
  91. package/lib/templates/form-fields/datetime-field.d.ts +7 -0
  92. package/lib/templates/form-fields/datetime-field.d.ts.map +1 -0
  93. package/lib/templates/form-fields/datetime-field.js +87 -0
  94. package/lib/templates/form-fields/datetime-field.js.map +1 -0
  95. package/lib/templates/form-fields/index.d.ts +23 -0
  96. package/lib/templates/form-fields/index.d.ts.map +1 -0
  97. package/lib/templates/form-fields/index.js +275 -0
  98. package/lib/templates/form-fields/index.js.map +1 -0
  99. package/lib/templates/form-fields/media-field.d.ts +10 -0
  100. package/lib/templates/form-fields/media-field.d.ts.map +1 -0
  101. package/lib/templates/form-fields/media-field.js +225 -0
  102. package/lib/templates/form-fields/media-field.js.map +1 -0
  103. package/lib/templates/form-fields/multiselect-field.d.ts +7 -0
  104. package/lib/templates/form-fields/multiselect-field.d.ts.map +1 -0
  105. package/lib/templates/form-fields/multiselect-field.js +121 -0
  106. package/lib/templates/form-fields/multiselect-field.js.map +1 -0
  107. package/lib/templates/form-fields/number-field.d.ts +7 -0
  108. package/lib/templates/form-fields/number-field.d.ts.map +1 -0
  109. package/lib/templates/form-fields/number-field.js +87 -0
  110. package/lib/templates/form-fields/number-field.js.map +1 -0
  111. package/lib/templates/form-fields/reference-field.d.ts +9 -0
  112. package/lib/templates/form-fields/reference-field.d.ts.map +1 -0
  113. package/lib/templates/form-fields/reference-field.js +145 -0
  114. package/lib/templates/form-fields/reference-field.js.map +1 -0
  115. package/lib/templates/form-fields/richtext-field.d.ts +9 -0
  116. package/lib/templates/form-fields/richtext-field.d.ts.map +1 -0
  117. package/lib/templates/form-fields/richtext-field.js +60 -0
  118. package/lib/templates/form-fields/richtext-field.js.map +1 -0
  119. package/lib/templates/form-fields/select-field.d.ts +7 -0
  120. package/lib/templates/form-fields/select-field.d.ts.map +1 -0
  121. package/lib/templates/form-fields/select-field.js +70 -0
  122. package/lib/templates/form-fields/select-field.js.map +1 -0
  123. package/lib/templates/form-fields/slug-field.d.ts +7 -0
  124. package/lib/templates/form-fields/slug-field.d.ts.map +1 -0
  125. package/lib/templates/form-fields/slug-field.js +143 -0
  126. package/lib/templates/form-fields/slug-field.js.map +1 -0
  127. package/lib/templates/form-fields/tags-field.d.ts +7 -0
  128. package/lib/templates/form-fields/tags-field.d.ts.map +1 -0
  129. package/lib/templates/form-fields/tags-field.js +172 -0
  130. package/lib/templates/form-fields/tags-field.js.map +1 -0
  131. package/lib/templates/form-fields/text-field.d.ts +7 -0
  132. package/lib/templates/form-fields/text-field.d.ts.map +1 -0
  133. package/lib/templates/form-fields/text-field.js +63 -0
  134. package/lib/templates/form-fields/text-field.js.map +1 -0
  135. package/lib/templates/form-fields/textarea-field.d.ts +7 -0
  136. package/lib/templates/form-fields/textarea-field.d.ts.map +1 -0
  137. package/lib/templates/form-fields/textarea-field.js +64 -0
  138. package/lib/templates/form-fields/textarea-field.js.map +1 -0
  139. package/lib/templates/index.d.ts +34 -0
  140. package/lib/templates/index.d.ts.map +1 -0
  141. package/lib/templates/index.js +92 -0
  142. package/lib/templates/index.js.map +1 -0
  143. package/lib/templates/media/index.d.ts +12 -0
  144. package/lib/templates/media/index.d.ts.map +1 -0
  145. package/lib/templates/media/index.js +50 -0
  146. package/lib/templates/media/index.js.map +1 -0
  147. package/lib/templates/media/media-api.d.ts +13 -0
  148. package/lib/templates/media/media-api.d.ts.map +1 -0
  149. package/lib/templates/media/media-api.js +274 -0
  150. package/lib/templates/media/media-api.js.map +1 -0
  151. package/lib/templates/media/media-grid.d.ts +14 -0
  152. package/lib/templates/media/media-grid.d.ts.map +1 -0
  153. package/lib/templates/media/media-grid.js +314 -0
  154. package/lib/templates/media/media-grid.js.map +1 -0
  155. package/lib/templates/media/media-library-route.d.ts +13 -0
  156. package/lib/templates/media/media-library-route.d.ts.map +1 -0
  157. package/lib/templates/media/media-library-route.js +105 -0
  158. package/lib/templates/media/media-library-route.js.map +1 -0
  159. package/lib/templates/media/media-picker.d.ts +13 -0
  160. package/lib/templates/media/media-picker.d.ts.map +1 -0
  161. package/lib/templates/media/media-picker.js +152 -0
  162. package/lib/templates/media/media-picker.js.map +1 -0
  163. package/lib/templates/media/media-uploader.d.ts +14 -0
  164. package/lib/templates/media/media-uploader.d.ts.map +1 -0
  165. package/lib/templates/media/media-uploader.js +318 -0
  166. package/lib/templates/media/media-uploader.js.map +1 -0
  167. package/lib/templates/recent-content.d.ts +13 -0
  168. package/lib/templates/recent-content.d.ts.map +1 -0
  169. package/lib/templates/recent-content.js +138 -0
  170. package/lib/templates/recent-content.js.map +1 -0
  171. package/lib/templates/slug-utils.d.ts +10 -0
  172. package/lib/templates/slug-utils.d.ts.map +1 -0
  173. package/lib/templates/slug-utils.js +194 -0
  174. package/lib/templates/slug-utils.js.map +1 -0
  175. package/lib/templates/ui-avatar.d.ts +8 -0
  176. package/lib/templates/ui-avatar.d.ts.map +1 -0
  177. package/lib/templates/ui-avatar.js +60 -0
  178. package/lib/templates/ui-avatar.js.map +1 -0
  179. package/lib/templates/ui-badge.d.ts +8 -0
  180. package/lib/templates/ui-badge.d.ts.map +1 -0
  181. package/lib/templates/ui-badge.js +52 -0
  182. package/lib/templates/ui-badge.js.map +1 -0
  183. package/lib/templates/ui-dialog.d.ts +10 -0
  184. package/lib/templates/ui-dialog.d.ts.map +1 -0
  185. package/lib/templates/ui-dialog.js +134 -0
  186. package/lib/templates/ui-dialog.js.map +1 -0
  187. package/lib/templates/ui-dropdown-menu.d.ts +8 -0
  188. package/lib/templates/ui-dropdown-menu.d.ts.map +1 -0
  189. package/lib/templates/ui-dropdown-menu.js +210 -0
  190. package/lib/templates/ui-dropdown-menu.js.map +1 -0
  191. package/lib/templates/ui-popover.d.ts +8 -0
  192. package/lib/templates/ui-popover.d.ts.map +1 -0
  193. package/lib/templates/ui-popover.js +43 -0
  194. package/lib/templates/ui-popover.js.map +1 -0
  195. package/lib/templates/ui-progress.d.ts +10 -0
  196. package/lib/templates/ui-progress.d.ts.map +1 -0
  197. package/lib/templates/ui-progress.js +40 -0
  198. package/lib/templates/ui-progress.js.map +1 -0
  199. package/lib/templates/ui-table.d.ts +8 -0
  200. package/lib/templates/ui-table.d.ts.map +1 -0
  201. package/lib/templates/ui-table.js +129 -0
  202. package/lib/templates/ui-table.js.map +1 -0
  203. package/lib/templates/ui-tabs.d.ts +10 -0
  204. package/lib/templates/ui-tabs.d.ts.map +1 -0
  205. package/lib/templates/ui-tabs.js +67 -0
  206. package/lib/templates/ui-tabs.js.map +1 -0
  207. package/package.json +52 -0
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, '__esModule', { value: true });
3
+ exports.getNumberFieldTemplate = getNumberFieldTemplate;
4
+ /**
5
+ * Generates the number field component template.
6
+ *
7
+ * @returns Template string for app/components/form-fields/number-field.tsx
8
+ */
9
+ function getNumberFieldTemplate() {
10
+ return `import type { NumberFieldDefinition } from '~/lib/content-types';
11
+ import { Input } from '~/components/ui/input';
12
+ import { Label } from '~/components/ui/label';
13
+ import { cn } from '~/lib/utils';
14
+ import type { FieldProps } from './index';
15
+
16
+ /**
17
+ * Numeric input field.
18
+ */
19
+ export function NumberField({
20
+ field,
21
+ value,
22
+ onChange,
23
+ error,
24
+ disabled,
25
+ }: FieldProps<NumberFieldDefinition>) {
26
+ const id = \`field-\${field.name}\`;
27
+
28
+ const handleChange = (inputValue: string) => {
29
+ if (inputValue === '') {
30
+ onChange(undefined);
31
+ return;
32
+ }
33
+
34
+ const numValue = field.decimal
35
+ ? parseFloat(inputValue)
36
+ : parseInt(inputValue, 10);
37
+
38
+ if (!isNaN(numValue)) {
39
+ onChange(numValue);
40
+ }
41
+ };
42
+
43
+ return (
44
+ <div className="space-y-2">
45
+ <Label htmlFor={id} className={cn(field.required && "after:content-['*'] after:ml-0.5 after:text-destructive")}>
46
+ {field.label}
47
+ </Label>
48
+ <Input
49
+ id={id}
50
+ name={field.name}
51
+ type="number"
52
+ value={value !== undefined && value !== null ? String(value) : ''}
53
+ onChange={(e) => handleChange(e.target.value)}
54
+ placeholder={field.placeholder}
55
+ disabled={disabled}
56
+ min={field.min}
57
+ max={field.max}
58
+ step={field.step ?? (field.decimal ? 'any' : 1)}
59
+ className={cn(error && 'border-destructive')}
60
+ aria-invalid={!!error}
61
+ aria-describedby={error ? \`\${id}-error\` : field.description ? \`\${id}-description\` : undefined}
62
+ />
63
+ {field.description && !error && (
64
+ <p id={\`\${id}-description\`} className="text-sm text-muted-foreground">
65
+ {field.description}
66
+ </p>
67
+ )}
68
+ {(field.min !== undefined || field.max !== undefined) && !error && !field.description && (
69
+ <p className="text-sm text-muted-foreground">
70
+ {field.min !== undefined && field.max !== undefined
71
+ ? \`Value must be between \${field.min} and \${field.max}\`
72
+ : field.min !== undefined
73
+ ? \`Minimum value: \${field.min}\`
74
+ : \`Maximum value: \${field.max}\`}
75
+ </p>
76
+ )}
77
+ {error && (
78
+ <p id={\`\${id}-error\`} className="text-sm text-destructive">
79
+ {error}
80
+ </p>
81
+ )}
82
+ </div>
83
+ );
84
+ }
85
+ `;
86
+ }
87
+ //# sourceMappingURL=number-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number-field.js","sourceRoot":"","sources":["../../../src/templates/form-fields/number-field.ts"],"names":[],"mappings":";;AAKA,wDA6EC;AAlFD;;;;GAIG;AACH,SAAgB,sBAAsB;IACpC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2ER,CAAC;AACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generates the reference field component template.
3
+ *
4
+ * This is a placeholder that will be fully implemented in a later sprint.
5
+ *
6
+ * @returns Template string for app/components/form-fields/reference-field.tsx
7
+ */
8
+ export declare function getReferenceFieldTemplate(): string;
9
+ //# sourceMappingURL=reference-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-field.d.ts","sourceRoot":"","sources":["../../../src/templates/form-fields/reference-field.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAqIlD"}
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, '__esModule', { value: true });
3
+ exports.getReferenceFieldTemplate = getReferenceFieldTemplate;
4
+ /**
5
+ * Generates the reference field component template.
6
+ *
7
+ * This is a placeholder that will be fully implemented in a later sprint.
8
+ *
9
+ * @returns Template string for app/components/form-fields/reference-field.tsx
10
+ */
11
+ function getReferenceFieldTemplate() {
12
+ return `import { Link2, X, Search } from 'lucide-react';
13
+ import type { ReferenceFieldDefinition } from '~/lib/content-types';
14
+ import { Label } from '~/components/ui/label';
15
+ import { Button } from '~/components/ui/button';
16
+ import { Badge } from '~/components/ui/badge';
17
+ import { cn } from '~/lib/utils';
18
+ import type { FieldProps } from './index';
19
+
20
+ /**
21
+ * Content reference field.
22
+ *
23
+ * Note: This is a placeholder component. Full reference picking
24
+ * functionality will be implemented in a later sprint with a
25
+ * content browser modal.
26
+ */
27
+ export function ReferenceField({
28
+ field,
29
+ value,
30
+ onChange,
31
+ error,
32
+ disabled,
33
+ }: FieldProps<ReferenceFieldDefinition>) {
34
+ const id = \`field-\${field.name}\`;
35
+ const references = field.multiple
36
+ ? (Array.isArray(value) ? value : [])
37
+ : (value ? [value as string] : []);
38
+
39
+ const handleRemove = (refId: string) => {
40
+ if (disabled) return;
41
+ if (field.multiple) {
42
+ onChange((references as string[]).filter((r) => r !== refId));
43
+ } else {
44
+ onChange('');
45
+ }
46
+ };
47
+
48
+ return (
49
+ <div className="space-y-2">
50
+ <Label className={cn(field.required && "after:content-['*'] after:ml-0.5 after:text-destructive")}>
51
+ {field.label}
52
+ </Label>
53
+ <div
54
+ className={cn(
55
+ 'border rounded-md p-4',
56
+ error && 'border-destructive'
57
+ )}
58
+ >
59
+ {references.length > 0 ? (
60
+ <div className="space-y-2">
61
+ {references.map((refId) => (
62
+ <div
63
+ key={refId}
64
+ className="flex items-center justify-between gap-2 p-2 bg-muted rounded-md"
65
+ >
66
+ <div className="flex items-center gap-2">
67
+ <Link2 className="h-4 w-4 text-muted-foreground" />
68
+ <span className="text-sm font-mono">{refId}</span>
69
+ </div>
70
+ <Button
71
+ type="button"
72
+ variant="ghost"
73
+ size="icon"
74
+ className="h-6 w-6"
75
+ onClick={() => handleRemove(refId)}
76
+ disabled={disabled}
77
+ >
78
+ <X className="h-3 w-3" />
79
+ </Button>
80
+ </div>
81
+ ))}
82
+ {field.multiple && (
83
+ <Button
84
+ type="button"
85
+ variant="outline"
86
+ size="sm"
87
+ className="w-full"
88
+ disabled
89
+ >
90
+ <Search className="h-4 w-4 mr-2" />
91
+ Add Reference
92
+ </Button>
93
+ )}
94
+ </div>
95
+ ) : (
96
+ <div className="text-center py-6">
97
+ <div className="mx-auto w-12 h-12 rounded-full bg-muted flex items-center justify-center mb-3">
98
+ <Link2 className="h-6 w-6 text-muted-foreground" />
99
+ </div>
100
+ <p className="text-sm font-medium mb-1">Content Reference</p>
101
+ <p className="text-xs text-muted-foreground mb-3">
102
+ Link to {field.contentTypes.join(', ')}
103
+ </p>
104
+ <Button type="button" variant="outline" size="sm" disabled>
105
+ <Search className="h-4 w-4 mr-2" />
106
+ Browse Content
107
+ </Button>
108
+ <p className="text-xs text-muted-foreground mt-2">
109
+ Reference browser coming soon
110
+ </p>
111
+ </div>
112
+ )}
113
+ </div>
114
+ {/* Hidden inputs for form submission */}
115
+ {field.multiple ? (
116
+ references.map((refId) => (
117
+ <input key={refId} type="hidden" name={\`\${field.name}[]\`} value={refId} />
118
+ ))
119
+ ) : references.length > 0 ? (
120
+ <input type="hidden" name={field.name} value={references[0]} />
121
+ ) : null}
122
+ <div className="flex gap-2 flex-wrap">
123
+ <span className="text-sm text-muted-foreground">Accepts:</span>
124
+ {field.contentTypes.map((type) => (
125
+ <Badge key={type} variant="outline" className="text-xs">
126
+ {type}
127
+ </Badge>
128
+ ))}
129
+ </div>
130
+ {field.description && !error && (
131
+ <p id={\`\${id}-description\`} className="text-sm text-muted-foreground">
132
+ {field.description}
133
+ </p>
134
+ )}
135
+ {error && (
136
+ <p id={\`\${id}-error\`} className="text-sm text-destructive">
137
+ {error}
138
+ </p>
139
+ )}
140
+ </div>
141
+ );
142
+ }
143
+ `;
144
+ }
145
+ //# sourceMappingURL=reference-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-field.js","sourceRoot":"","sources":["../../../src/templates/form-fields/reference-field.ts"],"names":[],"mappings":";;AAOA,8DAqIC;AA5ID;;;;;;GAMG;AACH,SAAgB,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmIR,CAAC;AACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generates the rich text field component template.
3
+ *
4
+ * Uses Tiptap-based RichTextEditor for WYSIWYG content editing.
5
+ *
6
+ * @returns Template string for app/components/form-fields/richtext-field.tsx
7
+ */
8
+ export declare function getRichtextFieldTemplate(): string;
9
+ //# sourceMappingURL=richtext-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"richtext-field.d.ts","sourceRoot":"","sources":["../../../src/templates/form-fields/richtext-field.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAgDjD"}
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, '__esModule', { value: true });
3
+ exports.getRichtextFieldTemplate = getRichtextFieldTemplate;
4
+ /**
5
+ * Generates the rich text field component template.
6
+ *
7
+ * Uses Tiptap-based RichTextEditor for WYSIWYG content editing.
8
+ *
9
+ * @returns Template string for app/components/form-fields/richtext-field.tsx
10
+ */
11
+ function getRichtextFieldTemplate() {
12
+ return `import type { RichtextFieldDefinition } from '~/lib/content-types';
13
+ import { Label } from '~/components/ui/label';
14
+ import { RichTextEditor } from '~/components/editor/rich-text-editor';
15
+ import { cn } from '~/lib/utils';
16
+ import type { FieldProps } from './index';
17
+
18
+ /**
19
+ * Rich text editor field.
20
+ *
21
+ * Provides a full WYSIWYG editing experience using Tiptap.
22
+ * Supports bold, italic, headings, lists, code blocks, links, and images.
23
+ */
24
+ export function RichtextField({
25
+ field,
26
+ value,
27
+ onChange,
28
+ error,
29
+ disabled,
30
+ }: FieldProps<RichtextFieldDefinition>) {
31
+ const id = \`field-\${field.name}\`;
32
+
33
+ return (
34
+ <div className="space-y-2">
35
+ <Label htmlFor={id} className={cn(field.required && "after:content-['*'] after:ml-0.5 after:text-destructive")}>
36
+ {field.label}
37
+ </Label>
38
+ <RichTextEditor
39
+ value={(value as string) ?? ''}
40
+ onChange={onChange}
41
+ placeholder={field.placeholder ?? 'Start writing...'}
42
+ disabled={disabled}
43
+ error={!!error}
44
+ />
45
+ {field.description && !error && (
46
+ <p id={\`\${id}-description\`} className="text-sm text-muted-foreground">
47
+ {field.description}
48
+ </p>
49
+ )}
50
+ {error && (
51
+ <p id={\`\${id}-error\`} className="text-sm text-destructive">
52
+ {error}
53
+ </p>
54
+ )}
55
+ </div>
56
+ );
57
+ }
58
+ `;
59
+ }
60
+ //# sourceMappingURL=richtext-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"richtext-field.js","sourceRoot":"","sources":["../../../src/templates/form-fields/richtext-field.ts"],"names":[],"mappings":";;AAOA,4DAgDC;AAvDD;;;;;;GAMG;AACH,SAAgB,wBAAwB;IACtC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CR,CAAC;AACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Generates the select field component template.
3
+ *
4
+ * @returns Template string for app/components/form-fields/select-field.tsx
5
+ */
6
+ export declare function getSelectFieldTemplate(): string;
7
+ //# sourceMappingURL=select-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-field.d.ts","sourceRoot":"","sources":["../../../src/templates/form-fields/select-field.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CA4D/C"}
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, '__esModule', { value: true });
3
+ exports.getSelectFieldTemplate = getSelectFieldTemplate;
4
+ /**
5
+ * Generates the select field component template.
6
+ *
7
+ * @returns Template string for app/components/form-fields/select-field.tsx
8
+ */
9
+ function getSelectFieldTemplate() {
10
+ return `import type { SelectFieldDefinition } from '~/lib/content-types';
11
+ import { Label } from '~/components/ui/label';
12
+ import { cn } from '~/lib/utils';
13
+ import type { FieldProps } from './index';
14
+
15
+ /**
16
+ * Single select dropdown field.
17
+ */
18
+ export function SelectField({
19
+ field,
20
+ value,
21
+ onChange,
22
+ error,
23
+ disabled,
24
+ }: FieldProps<SelectFieldDefinition>) {
25
+ const id = \`field-\${field.name}\`;
26
+
27
+ return (
28
+ <div className="space-y-2">
29
+ <Label htmlFor={id} className={cn(field.required && "after:content-['*'] after:ml-0.5 after:text-destructive")}>
30
+ {field.label}
31
+ </Label>
32
+ <select
33
+ id={id}
34
+ name={field.name}
35
+ value={(value as string) ?? ''}
36
+ onChange={(e) => onChange(e.target.value)}
37
+ disabled={disabled}
38
+ className={cn(
39
+ 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
40
+ error && 'border-destructive',
41
+ !value && 'text-muted-foreground'
42
+ )}
43
+ aria-invalid={!!error}
44
+ aria-describedby={error ? \`\${id}-error\` : field.description ? \`\${id}-description\` : undefined}
45
+ >
46
+ <option value="" disabled>
47
+ {field.placeholder ?? 'Select an option...'}
48
+ </option>
49
+ {field.options.map((option) => (
50
+ <option key={option.value} value={option.value}>
51
+ {option.label}
52
+ </option>
53
+ ))}
54
+ </select>
55
+ {field.description && !error && (
56
+ <p id={\`\${id}-description\`} className="text-sm text-muted-foreground">
57
+ {field.description}
58
+ </p>
59
+ )}
60
+ {error && (
61
+ <p id={\`\${id}-error\`} className="text-sm text-destructive">
62
+ {error}
63
+ </p>
64
+ )}
65
+ </div>
66
+ );
67
+ }
68
+ `;
69
+ }
70
+ //# sourceMappingURL=select-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-field.js","sourceRoot":"","sources":["../../../src/templates/form-fields/select-field.ts"],"names":[],"mappings":";;AAKA,wDA4DC;AAjED;;;;GAIG;AACH,SAAgB,sBAAsB;IACpC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DR,CAAC;AACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Generates the slug field component template.
3
+ *
4
+ * @returns Template string for app/components/form-fields/slug-field.tsx
5
+ */
6
+ export declare function getSlugFieldTemplate(): string;
7
+ //# sourceMappingURL=slug-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-field.d.ts","sourceRoot":"","sources":["../../../src/templates/form-fields/slug-field.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAqI7C"}
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, '__esModule', { value: true });
3
+ exports.getSlugFieldTemplate = getSlugFieldTemplate;
4
+ /**
5
+ * Generates the slug field component template.
6
+ *
7
+ * @returns Template string for app/components/form-fields/slug-field.tsx
8
+ */
9
+ function getSlugFieldTemplate() {
10
+ return `import { useState, useEffect, useCallback } from 'react';
11
+ import { Lock, Unlock, RefreshCw } from 'lucide-react';
12
+ import type { SlugFieldDefinition } from '~/lib/content-types';
13
+ import { Input } from '~/components/ui/input';
14
+ import { Label } from '~/components/ui/label';
15
+ import { Button } from '~/components/ui/button';
16
+ import { cn } from '~/lib/utils';
17
+ import { generateSlug } from '~/lib/slug-utils';
18
+ import type { FieldProps } from './index';
19
+
20
+ /**
21
+ * URL slug field with auto-generation from source field.
22
+ */
23
+ export function SlugField({
24
+ field,
25
+ value,
26
+ onChange,
27
+ error,
28
+ disabled,
29
+ formData,
30
+ }: FieldProps<SlugFieldDefinition>) {
31
+ const id = \`field-\${field.name}\`;
32
+ const [isLocked, setIsLocked] = useState(true);
33
+ const [hasBeenEdited, setHasBeenEdited] = useState(false);
34
+
35
+ // Get the source field value for auto-generation
36
+ const sourceValue = formData?.[field.sourceField] as string | undefined;
37
+
38
+ // Auto-generate slug from source field when locked
39
+ const regenerateSlug = useCallback(() => {
40
+ if (sourceValue) {
41
+ const newSlug = generateSlug(sourceValue);
42
+ onChange(newSlug);
43
+ }
44
+ }, [sourceValue, onChange]);
45
+
46
+ // Auto-update slug when source changes and slug is locked
47
+ useEffect(() => {
48
+ if (isLocked && !hasBeenEdited && sourceValue) {
49
+ regenerateSlug();
50
+ }
51
+ }, [isLocked, hasBeenEdited, sourceValue, regenerateSlug]);
52
+
53
+ const handleChange = (newValue: string) => {
54
+ // Normalize to valid slug characters
55
+ const normalized = newValue.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-');
56
+ onChange(normalized);
57
+ setHasBeenEdited(true);
58
+ };
59
+
60
+ const toggleLock = () => {
61
+ if (isLocked) {
62
+ setIsLocked(false);
63
+ } else {
64
+ setIsLocked(true);
65
+ setHasBeenEdited(false);
66
+ regenerateSlug();
67
+ }
68
+ };
69
+
70
+ return (
71
+ <div className="space-y-2">
72
+ <Label htmlFor={id} className={cn(field.required && "after:content-['*'] after:ml-0.5 after:text-destructive")}>
73
+ {field.label}
74
+ </Label>
75
+ <div className="flex gap-2">
76
+ {field.prefix && (
77
+ <span className="flex items-center px-3 text-sm text-muted-foreground bg-muted rounded-l-md border border-r-0">
78
+ {field.prefix}
79
+ </span>
80
+ )}
81
+ <div className="relative flex-1">
82
+ <Input
83
+ id={id}
84
+ name={field.name}
85
+ type="text"
86
+ value={(value as string) ?? ''}
87
+ onChange={(e) => handleChange(e.target.value)}
88
+ disabled={disabled || isLocked}
89
+ className={cn(
90
+ field.prefix && 'rounded-l-none',
91
+ 'pr-20',
92
+ error && 'border-destructive'
93
+ )}
94
+ aria-invalid={!!error}
95
+ aria-describedby={error ? \`\${id}-error\` : field.description ? \`\${id}-description\` : undefined}
96
+ />
97
+ <div className="absolute right-1 top-1/2 -translate-y-1/2 flex gap-1">
98
+ <Button
99
+ type="button"
100
+ variant="ghost"
101
+ size="icon"
102
+ className="h-7 w-7"
103
+ onClick={regenerateSlug}
104
+ disabled={disabled || !sourceValue}
105
+ title="Regenerate from title"
106
+ >
107
+ <RefreshCw className="h-4 w-4" />
108
+ </Button>
109
+ <Button
110
+ type="button"
111
+ variant="ghost"
112
+ size="icon"
113
+ className="h-7 w-7"
114
+ onClick={toggleLock}
115
+ disabled={disabled}
116
+ title={isLocked ? 'Unlock for manual editing' : 'Lock to auto-generate'}
117
+ >
118
+ {isLocked ? <Lock className="h-4 w-4" /> : <Unlock className="h-4 w-4" />}
119
+ </Button>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ {field.description && !error && (
124
+ <p id={\`\${id}-description\`} className="text-sm text-muted-foreground">
125
+ {field.description}
126
+ </p>
127
+ )}
128
+ {!field.description && !error && (
129
+ <p className="text-sm text-muted-foreground">
130
+ {isLocked ? 'Auto-generated from ' + field.sourceField + '. Click unlock to edit manually.' : 'Manual editing enabled. Click lock to auto-generate.'}
131
+ </p>
132
+ )}
133
+ {error && (
134
+ <p id={\`\${id}-error\`} className="text-sm text-destructive">
135
+ {error}
136
+ </p>
137
+ )}
138
+ </div>
139
+ );
140
+ }
141
+ `;
142
+ }
143
+ //# sourceMappingURL=slug-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-field.js","sourceRoot":"","sources":["../../../src/templates/form-fields/slug-field.ts"],"names":[],"mappings":";;AAKA,oDAqIC;AA1ID;;;;GAIG;AACH,SAAgB,oBAAoB;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmIR,CAAC;AACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Generates the tags field component template.
3
+ *
4
+ * @returns Template string for app/components/form-fields/tags-field.tsx
5
+ */
6
+ export declare function getTagsFieldTemplate(): string;
7
+ //# sourceMappingURL=tags-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tags-field.d.ts","sourceRoot":"","sources":["../../../src/templates/form-fields/tags-field.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAkK7C"}