includio-cms 0.5.2 → 0.5.5
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/CHANGELOG.md +60 -0
- package/ROADMAP.md +29 -0
- package/dist/admin/api/rest/handler.d.ts +7 -0
- package/dist/admin/api/rest/handler.js +116 -0
- package/dist/admin/api/rest/middleware/apiKey.d.ts +6 -0
- package/dist/admin/api/rest/middleware/apiKey.js +45 -0
- package/dist/admin/api/rest/routes/collections.d.ts +5 -0
- package/dist/admin/api/rest/routes/collections.js +104 -0
- package/dist/admin/api/rest/routes/entries.d.ts +2 -0
- package/dist/admin/api/rest/routes/entries.js +37 -0
- package/dist/admin/api/rest/routes/languages.d.ts +1 -0
- package/dist/admin/api/rest/routes/languages.js +5 -0
- package/dist/admin/api/rest/routes/schema.d.ts +2 -0
- package/dist/admin/api/rest/routes/schema.js +78 -0
- package/dist/admin/api/rest/routes/singletons.d.ts +3 -0
- package/dist/admin/api/rest/routes/singletons.js +60 -0
- package/dist/admin/auth-client.d.ts +7 -7
- package/dist/admin/client/collection/collection-entries.svelte +56 -5
- package/dist/admin/client/collection/data-table.svelte +127 -18
- package/dist/admin/client/collection/data-table.svelte.d.ts +2 -0
- package/dist/admin/client/entry/entry-form.svelte +1 -0
- package/dist/admin/client/entry/entry.svelte +130 -123
- package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +92 -9
- package/dist/admin/components/fields/blocks-field.svelte +142 -112
- package/dist/admin/components/fields/blocks-field.svelte.d.ts +10 -30
- package/dist/admin/components/fields/boolean-field.svelte +28 -38
- package/dist/admin/components/fields/boolean-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/checkboxes-field.svelte +12 -24
- package/dist/admin/components/fields/checkboxes-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/content-field.svelte +4 -17
- package/dist/admin/components/fields/content-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/date-field.svelte +8 -21
- package/dist/admin/components/fields/date-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/datetime-field.svelte +8 -21
- package/dist/admin/components/fields/datetime-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/field-renderer.svelte +32 -19
- package/dist/admin/components/fields/field-renderer.svelte.d.ts +1 -1
- package/dist/admin/components/fields/field-value-bridge.svelte +21 -0
- package/dist/admin/components/fields/field-value-bridge.svelte.d.ts +31 -0
- package/dist/admin/components/fields/fields-form.svelte +13 -10
- package/dist/admin/components/fields/file-field.svelte +12 -27
- package/dist/admin/components/fields/file-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/image-field.svelte +13 -28
- package/dist/admin/components/fields/image-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/media-field.svelte +15 -30
- package/dist/admin/components/fields/media-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/number-field.svelte +6 -20
- package/dist/admin/components/fields/number-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/object-field.svelte +26 -29
- package/dist/admin/components/fields/object-field.svelte.d.ts +11 -31
- package/dist/admin/components/fields/radio-field.svelte +8 -20
- package/dist/admin/components/fields/radio-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/relation-field.svelte +28 -40
- package/dist/admin/components/fields/relation-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/richtext-field.svelte +4 -17
- package/dist/admin/components/fields/richtext-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/select-field.svelte +14 -28
- package/dist/admin/components/fields/select-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/seo-field.svelte +5 -12
- package/dist/admin/components/fields/seo-field.svelte.d.ts +8 -28
- package/dist/admin/components/fields/simple-array-field.svelte +29 -42
- package/dist/admin/components/fields/simple-array-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/slug-field.svelte +6 -11
- package/dist/admin/components/fields/slug-field.svelte.d.ts +6 -26
- package/dist/admin/components/fields/text-field-wrapper.svelte +22 -40
- package/dist/admin/components/fields/text-field.svelte +7 -19
- package/dist/admin/components/fields/text-field.svelte.d.ts +5 -27
- package/dist/admin/components/fields/url-field-wrapper.svelte +8 -3
- package/dist/admin/components/fields/url-field.svelte +294 -128
- package/dist/admin/components/fields/url-field.svelte.d.ts +5 -27
- package/dist/admin/components/layout/layout-renderer.svelte +8 -6
- package/dist/admin/components/layout/nav-collections.svelte +2 -1
- package/dist/admin/components/layout/nav-forms.svelte +2 -1
- package/dist/admin/components/layout/nav-singletons.svelte +2 -1
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +221 -31
- package/dist/admin/components/tiptap/content-editor.svelte +13 -2
- package/dist/admin/components/tiptap/inline-block-node.d.ts +1 -0
- package/dist/admin/components/tiptap/inline-block-node.js +18 -1
- package/dist/admin/components/tiptap/slash-command.js +2 -3
- package/dist/admin/components/tiptap/standalone-form.d.ts +7 -0
- package/dist/admin/components/tiptap/standalone-form.js +31 -0
- package/dist/admin/components/tiptap/tiptap-editor.svelte +7 -0
- package/dist/admin/remote/entry.remote.d.ts +9 -1
- package/dist/admin/remote/entry.remote.js +30 -2
- package/dist/admin/styles/admin.css +10 -0
- package/dist/admin/utils/fieldCondition.d.ts +6 -0
- package/dist/admin/utils/fieldCondition.js +20 -0
- package/dist/cli/scaffold/admin.js +8 -0
- package/dist/components/ui/switch/index.d.ts +2 -0
- package/dist/components/ui/switch/index.js +4 -0
- package/dist/components/ui/switch/switch.svelte +26 -0
- package/dist/components/ui/switch/switch.svelte.d.ts +4 -0
- package/dist/core/cms.d.ts +2 -1
- package/dist/core/cms.js +2 -0
- package/dist/core/fields/fieldSchemaToTs.js +15 -3
- package/dist/core/fields/formFieldSchemaToTs.js +22 -6
- package/dist/core/fields/urlUtils.d.ts +14 -0
- package/dist/core/fields/urlUtils.js +21 -0
- package/dist/core/server/entries/operations/get.js +2 -1
- package/dist/core/server/entries/operations/update.d.ts +1 -0
- package/dist/core/server/entries/operations/update.js +5 -1
- package/dist/core/server/fields/populateEntry.js +43 -0
- package/dist/core/server/fields/resolveImageFields.js +33 -1
- package/dist/core/server/fields/resolveRelationFields.js +46 -0
- package/dist/core/server/fields/resolveRichtextLinks.js +15 -1
- package/dist/core/server/fields/resolveUrlFields.js +65 -0
- package/dist/core/server/generator/formFieldSchemaToString.js +40 -9
- package/dist/core/server/generator/formFields.js +2 -0
- package/dist/core/server/generator/generator.js +25 -1
- package/dist/db-postgres/schema/entry.d.ts +17 -0
- package/dist/db-postgres/schema/entry.js +4 -2
- package/dist/schemas/field/url.d.ts +2 -0
- package/dist/schemas/field/url.js +4 -2
- package/dist/server/auth.d.ts +6 -6
- package/dist/sveltekit/server/handle.js +1 -0
- package/dist/types/cms.d.ts +7 -0
- package/dist/types/collections.d.ts +2 -0
- package/dist/types/entries.d.ts +7 -1
- package/dist/types/fields.d.ts +9 -0
- package/dist/types/formFields.d.ts +15 -2
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +1 -0
- package/dist/updates/0.5.3/index.d.ts +2 -0
- package/dist/updates/0.5.3/index.js +19 -0
- package/dist/updates/0.5.4/index.d.ts +2 -0
- package/dist/updates/0.5.4/index.js +15 -0
- package/dist/updates/0.5.5/index.d.ts +2 -0
- package/dist/updates/0.5.5/index.js +20 -0
- package/dist/updates/index.js +4 -1
- package/package.json +7 -1
- package/dist/admin/components/fields/standalone-field-renderer.svelte +0 -148
- package/dist/admin/components/fields/standalone-field-renderer.svelte.d.ts +0 -9
|
@@ -55,7 +55,7 @@ function generateTypesStringForForms(records) {
|
|
|
55
55
|
.join('\n')}
|
|
56
56
|
`;
|
|
57
57
|
code += `
|
|
58
|
-
export type ${recordTypeString}
|
|
58
|
+
export type ${recordTypeString}EntryMap = {
|
|
59
59
|
${records
|
|
60
60
|
.map((single) => {
|
|
61
61
|
return `${single.slug}: ${toPascalCase(single.slug)}`;
|
|
@@ -168,9 +168,33 @@ function generateSchemas(config) {
|
|
|
168
168
|
});
|
|
169
169
|
writeFileSync(filePath, code);
|
|
170
170
|
}
|
|
171
|
+
function generateRemote(config) {
|
|
172
|
+
if (!config.forms || config.forms.length === 0)
|
|
173
|
+
return;
|
|
174
|
+
const cmsDir = join(process.cwd(), 'src/lib/cms/runtime');
|
|
175
|
+
const filePath = join(cmsDir, 'remote.ts');
|
|
176
|
+
let code = `// This file is auto-generated. Do not edit directly.\n\n`;
|
|
177
|
+
code += `import { command } from '$app/server';\n`;
|
|
178
|
+
code += `import { submitForm } from './api';\n`;
|
|
179
|
+
const schemaImports = config.forms
|
|
180
|
+
.map((form) => `${form.slug}FormSchema`)
|
|
181
|
+
.join(', ');
|
|
182
|
+
code += `import { ${schemaImports} } from './schemas';\n\n`;
|
|
183
|
+
config.forms.forEach((form) => {
|
|
184
|
+
const pascalSlug = toPascalCase(form.slug);
|
|
185
|
+
code += `export const submit${pascalSlug}Command = command(\n`;
|
|
186
|
+
code += `\t${form.slug}FormSchema,\n`;
|
|
187
|
+
code += `\tasync (data) => {\n`;
|
|
188
|
+
code += `\t\tawait submitForm('${form.slug}', data);\n`;
|
|
189
|
+
code += `\t}\n`;
|
|
190
|
+
code += `);\n\n`;
|
|
191
|
+
});
|
|
192
|
+
writeFileSync(filePath, code);
|
|
193
|
+
}
|
|
171
194
|
export function generateRuntime(config) {
|
|
172
195
|
createCmsRuntimeDir();
|
|
173
196
|
generateTypes(config);
|
|
174
197
|
generateAPI(config);
|
|
175
198
|
generateSchemas(config);
|
|
199
|
+
generateRemote(config);
|
|
176
200
|
}
|
|
@@ -200,6 +200,23 @@ export declare const entriesTable: import("drizzle-orm/pg-core/table", { with: {
|
|
|
200
200
|
identity: undefined;
|
|
201
201
|
generated: undefined;
|
|
202
202
|
}, {}, {}>;
|
|
203
|
+
sortOrder: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
204
|
+
name: "sort_order";
|
|
205
|
+
tableName: "entry";
|
|
206
|
+
dataType: "number";
|
|
207
|
+
columnType: "PgInteger";
|
|
208
|
+
data: number;
|
|
209
|
+
driverParam: string | number;
|
|
210
|
+
notNull: false;
|
|
211
|
+
hasDefault: false;
|
|
212
|
+
isPrimaryKey: false;
|
|
213
|
+
isAutoincrement: false;
|
|
214
|
+
hasRuntimeDefault: false;
|
|
215
|
+
enumValues: undefined;
|
|
216
|
+
baseColumn: never;
|
|
217
|
+
identity: undefined;
|
|
218
|
+
generated: undefined;
|
|
219
|
+
}, {}, {}>;
|
|
203
220
|
};
|
|
204
221
|
dialect: "pg";
|
|
205
222
|
}>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
1
|
+
import { integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
2
2
|
import { entryVersionsTable } from './entryVersion.js';
|
|
3
3
|
export const entriesTable = pgTable('entry', {
|
|
4
4
|
id: uuid('id').primaryKey().defaultRandom(),
|
|
@@ -12,5 +12,7 @@ export const entriesTable = pgTable('entry', {
|
|
|
12
12
|
// Publish state
|
|
13
13
|
publishedAt: timestamp('published_at'),
|
|
14
14
|
publishedVersionId: uuid('published_version_id').references(() => entryVersionsTable.id, { onDelete: 'set null' }),
|
|
15
|
-
publishedBy: text('published_by')
|
|
15
|
+
publishedBy: text('published_by'),
|
|
16
|
+
// Manual ordering
|
|
17
|
+
sortOrder: integer('sort_order')
|
|
16
18
|
});
|
|
@@ -4,10 +4,12 @@ export declare const urlFieldDataSchema: z.ZodObject<{
|
|
|
4
4
|
url: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
5
5
|
text: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
6
6
|
newTab: z.ZodOptional<z.ZodBoolean>;
|
|
7
|
+
rel: z.ZodOptional<z.ZodString>;
|
|
7
8
|
}, z.z.core.$strip>;
|
|
8
9
|
export declare const urlFieldDataWithRelationSchema: z.ZodObject<{
|
|
9
10
|
id: z.ZodString;
|
|
10
11
|
url: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
11
12
|
text: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
12
13
|
newTab: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
+
rel: z.ZodOptional<z.ZodString>;
|
|
13
15
|
}, z.z.core.$strip>;
|
|
@@ -3,11 +3,13 @@ export const urlFieldDataSchema = z.object({
|
|
|
3
3
|
id: z.string().optional(),
|
|
4
4
|
url: z.record(z.string(), z.string()),
|
|
5
5
|
text: z.record(z.string(), z.string()).optional(),
|
|
6
|
-
newTab: z.boolean().optional()
|
|
6
|
+
newTab: z.boolean().optional(),
|
|
7
|
+
rel: z.string().optional()
|
|
7
8
|
});
|
|
8
9
|
export const urlFieldDataWithRelationSchema = z.object({
|
|
9
10
|
id: z.string().uuid(),
|
|
10
11
|
url: z.record(z.string(), z.string()),
|
|
11
12
|
text: z.record(z.string(), z.string()).optional(),
|
|
12
|
-
newTab: z.boolean().optional()
|
|
13
|
+
newTab: z.boolean().optional(),
|
|
14
|
+
rel: z.string().optional()
|
|
13
15
|
});
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -71,7 +71,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
|
|
|
71
71
|
<AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0: {
|
|
72
72
|
body: {
|
|
73
73
|
userId: string;
|
|
74
|
-
role: "
|
|
74
|
+
role: "admin" | "user" | ("admin" | "user")[];
|
|
75
75
|
};
|
|
76
76
|
} & {
|
|
77
77
|
method?: "POST" | undefined;
|
|
@@ -138,7 +138,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
|
|
|
138
138
|
$Infer: {
|
|
139
139
|
body: {
|
|
140
140
|
userId: string;
|
|
141
|
-
role: "
|
|
141
|
+
role: "admin" | "user" | ("admin" | "user")[];
|
|
142
142
|
};
|
|
143
143
|
};
|
|
144
144
|
};
|
|
@@ -236,7 +236,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
|
|
|
236
236
|
email: string;
|
|
237
237
|
password: string;
|
|
238
238
|
name: string;
|
|
239
|
-
role?: "
|
|
239
|
+
role?: "admin" | "user" | ("admin" | "user")[] | undefined;
|
|
240
240
|
data?: Record<string, any>;
|
|
241
241
|
};
|
|
242
242
|
} & {
|
|
@@ -302,7 +302,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
|
|
|
302
302
|
email: string;
|
|
303
303
|
password: string;
|
|
304
304
|
name: string;
|
|
305
|
-
role?: "
|
|
305
|
+
role?: "admin" | "user" | ("admin" | "user")[] | undefined;
|
|
306
306
|
data?: Record<string, any>;
|
|
307
307
|
};
|
|
308
308
|
};
|
|
@@ -1184,7 +1184,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
|
|
|
1184
1184
|
permission?: never;
|
|
1185
1185
|
}) & {
|
|
1186
1186
|
userId?: string;
|
|
1187
|
-
role?: "
|
|
1187
|
+
role?: "admin" | "user" | undefined;
|
|
1188
1188
|
};
|
|
1189
1189
|
} & {
|
|
1190
1190
|
method?: "POST" | undefined;
|
|
@@ -1287,7 +1287,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
|
|
|
1287
1287
|
permission?: never;
|
|
1288
1288
|
}) & {
|
|
1289
1289
|
userId?: string;
|
|
1290
|
-
role?: "
|
|
1290
|
+
role?: "admin" | "user" | undefined;
|
|
1291
1291
|
};
|
|
1292
1292
|
};
|
|
1293
1293
|
};
|
|
@@ -11,6 +11,7 @@ const adminGuard = async ({ event, resolve }) => {
|
|
|
11
11
|
!event.url.pathname.startsWith('/admin/accept-invite') &&
|
|
12
12
|
!event.url.pathname.startsWith('/admin/api/accept-invite') &&
|
|
13
13
|
!event.url.pathname.startsWith('/admin/reset-password') &&
|
|
14
|
+
!event.url.pathname.startsWith('/admin/api/rest/') &&
|
|
14
15
|
(!user || !session)) {
|
|
15
16
|
setFlash({
|
|
16
17
|
message: 'You must be logged in to access the admin panel.',
|
package/dist/types/cms.d.ts
CHANGED
|
@@ -27,6 +27,11 @@ export interface AuthAdapter {
|
|
|
27
27
|
} | null>;
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
+
export interface ApiKeyConfig {
|
|
31
|
+
key: string;
|
|
32
|
+
name?: string;
|
|
33
|
+
role?: 'admin' | 'editor';
|
|
34
|
+
}
|
|
30
35
|
export interface CMSConfig {
|
|
31
36
|
languages: Language[];
|
|
32
37
|
collections?: CollectionConfig[];
|
|
@@ -39,6 +44,7 @@ export interface CMSConfig {
|
|
|
39
44
|
plugins?: PluginConfig[];
|
|
40
45
|
ai?: AIAdapter;
|
|
41
46
|
media?: MediaConfig;
|
|
47
|
+
apiKeys?: ApiKeyConfig[];
|
|
42
48
|
}
|
|
43
49
|
export interface ICMS {
|
|
44
50
|
collections: Record<string, CollectionConfigWithType>;
|
|
@@ -52,4 +58,5 @@ export interface ICMS {
|
|
|
52
58
|
plugins: PluginConfig[];
|
|
53
59
|
aiAdapter: AIAdapter | null;
|
|
54
60
|
mediaConfig: MediaConfig;
|
|
61
|
+
apiKeys: ApiKeyConfig[];
|
|
55
62
|
}
|
package/dist/types/entries.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface DbEntry {
|
|
|
19
19
|
publishedAt: Date | null;
|
|
20
20
|
publishedVersionId: string | null;
|
|
21
21
|
publishedBy: string | null;
|
|
22
|
+
sortOrder: number | null;
|
|
22
23
|
}
|
|
23
24
|
export interface DbEntryInsert {
|
|
24
25
|
slug: string;
|
|
@@ -30,6 +31,7 @@ export interface DbEntryInsert {
|
|
|
30
31
|
publishedAt?: Date | null;
|
|
31
32
|
publishedVersionId?: string | null;
|
|
32
33
|
publishedBy?: string | null;
|
|
34
|
+
sortOrder?: number | null;
|
|
33
35
|
}
|
|
34
36
|
export interface RawEntry extends DbEntry {
|
|
35
37
|
collection: CollectionConfigWithType | SingleConfigWithType;
|
|
@@ -68,7 +70,7 @@ export interface PaginationOptions {
|
|
|
68
70
|
limit?: number;
|
|
69
71
|
offset?: number;
|
|
70
72
|
orderBy?: {
|
|
71
|
-
column: 'createdAt' | 'updatedAt';
|
|
73
|
+
column: 'createdAt' | 'updatedAt' | 'sortOrder';
|
|
72
74
|
direction: 'asc' | 'desc';
|
|
73
75
|
};
|
|
74
76
|
}
|
|
@@ -103,6 +105,10 @@ export interface GetEntriesOptions {
|
|
|
103
105
|
dataLike?: Record<string, unknown>;
|
|
104
106
|
language?: string;
|
|
105
107
|
status?: EntryStatus;
|
|
108
|
+
orderBy?: {
|
|
109
|
+
column: 'createdAt' | 'updatedAt' | 'sortOrder';
|
|
110
|
+
direction: 'asc' | 'desc';
|
|
111
|
+
};
|
|
106
112
|
}
|
|
107
113
|
export interface GetEntryOptions {
|
|
108
114
|
id?: string;
|
package/dist/types/fields.d.ts
CHANGED
|
@@ -3,6 +3,11 @@ import type { ImageStyle, MediaFile } from './media.js';
|
|
|
3
3
|
import type { Localized } from './languages.js';
|
|
4
4
|
import type { StructuredContentDoc } from './structured-content.js';
|
|
5
5
|
export type FieldType = 'text' | 'richtext' | 'content' | 'number' | 'boolean' | 'date' | 'datetime' | 'file' | 'image' | 'media' | 'select' | 'radio' | 'checkboxes' | 'relation' | 'object' | 'array' | 'blocks' | 'slug' | 'seo' | 'url';
|
|
6
|
+
export interface FieldCondition {
|
|
7
|
+
field: string;
|
|
8
|
+
equals?: string | string[];
|
|
9
|
+
notEquals?: string | string[];
|
|
10
|
+
}
|
|
6
11
|
export interface BaseField {
|
|
7
12
|
slug: string;
|
|
8
13
|
label?: Localized;
|
|
@@ -10,6 +15,7 @@ export interface BaseField {
|
|
|
10
15
|
description?: Localized;
|
|
11
16
|
defaultValue?: any;
|
|
12
17
|
localized?: boolean;
|
|
18
|
+
showWhen?: FieldCondition;
|
|
13
19
|
}
|
|
14
20
|
export interface TextField extends BaseField {
|
|
15
21
|
type: 'text';
|
|
@@ -144,6 +150,7 @@ export interface ObjectField extends BaseField {
|
|
|
144
150
|
thumbnail?: string;
|
|
145
151
|
}
|
|
146
152
|
export interface ObjectFieldData {
|
|
153
|
+
_id?: string;
|
|
147
154
|
slug?: string;
|
|
148
155
|
data: Record<string, unknown>;
|
|
149
156
|
}
|
|
@@ -188,11 +195,13 @@ export interface UrlField extends BaseField {
|
|
|
188
195
|
placeholder?: Localized;
|
|
189
196
|
text?: boolean;
|
|
190
197
|
newTab?: boolean;
|
|
198
|
+
rel?: boolean;
|
|
191
199
|
}
|
|
192
200
|
export type UrlFieldData = {
|
|
193
201
|
id?: string;
|
|
194
202
|
url: Record<string, string>;
|
|
195
203
|
text?: Record<string, string>;
|
|
196
204
|
newTab?: boolean;
|
|
205
|
+
rel?: string;
|
|
197
206
|
};
|
|
198
207
|
export type Field = TextField | RichtextField | ContentField | NumberField | BooleanField | DateField | DateTimeField | FileField | ImageField | MediaField | SelectField | RadioField | CheckboxesField | RelationField | ObjectField | ArrayField | BlocksField | SlugField | SeoField | UrlField;
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { Localized } from './languages.js';
|
|
2
|
-
export type FormFieldType = 'text' | 'email' | 'textarea' | 'checkbox';
|
|
2
|
+
export type FormFieldType = 'text' | 'email' | 'textarea' | 'checkbox' | 'select';
|
|
3
3
|
export interface FormBaseField {
|
|
4
4
|
slug: string;
|
|
5
5
|
label?: Localized;
|
|
6
6
|
required?: boolean;
|
|
7
7
|
description?: Localized;
|
|
8
|
+
errorMessage?: Localized;
|
|
8
9
|
defaultValue?: any;
|
|
9
10
|
showInDataTable?: boolean;
|
|
10
11
|
}
|
|
11
12
|
export interface FormTextField extends FormBaseField {
|
|
12
13
|
type: 'text';
|
|
13
14
|
defaultValue?: string;
|
|
15
|
+
minLength?: number;
|
|
16
|
+
maxLength?: number;
|
|
14
17
|
}
|
|
15
18
|
export interface FormEmailField extends FormBaseField {
|
|
16
19
|
type: 'email';
|
|
@@ -19,9 +22,19 @@ export interface FormEmailField extends FormBaseField {
|
|
|
19
22
|
export interface FormTextareaField extends FormBaseField {
|
|
20
23
|
type: 'textarea';
|
|
21
24
|
defaultValue?: string;
|
|
25
|
+
minLength?: number;
|
|
26
|
+
maxLength?: number;
|
|
22
27
|
}
|
|
23
28
|
export interface FormCheckboxField extends FormBaseField {
|
|
24
29
|
type: 'checkbox';
|
|
25
30
|
defaultValue?: boolean;
|
|
26
31
|
}
|
|
27
|
-
export
|
|
32
|
+
export interface FormSelectField extends FormBaseField {
|
|
33
|
+
type: 'select';
|
|
34
|
+
options: {
|
|
35
|
+
value: string;
|
|
36
|
+
label?: Localized;
|
|
37
|
+
}[];
|
|
38
|
+
defaultValue?: string;
|
|
39
|
+
}
|
|
40
|
+
export type FormField = FormTextField | FormEmailField | FormTextareaField | FormCheckboxField | FormSelectField;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { type ConfigBase } from './config.js';
|
|
|
7
7
|
export { type CollectionConfig } from './collections.js';
|
|
8
8
|
export { type SingleConfig } from './singles.js';
|
|
9
9
|
export { type FormConfig, type FormSubmission } from './forms.js';
|
|
10
|
-
export { type
|
|
10
|
+
export { type FormField, type FormFieldType, type FormBaseField, type FormTextField, type FormEmailField, type FormTextareaField, type FormCheckboxField, type FormSelectField } from './formFields.js';
|
|
11
|
+
export { type CMSConfig, type ApiKeyConfig } from './cms.js';
|
|
11
12
|
export { type Language, type Localized } from './languages.js';
|
|
12
13
|
export { type Layout, type LayoutNode, type LayoutPreset, type LayoutNodeType, type ColumnRatio, type SectionNode, type ColumnsNode, type CardNode, type AccordionNode, type StackNode } from './layout.js';
|
package/dist/types/index.js
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const update = {
|
|
2
|
+
version: '0.5.3',
|
|
3
|
+
date: '2026-03-04',
|
|
4
|
+
description: 'Form fields, inline blocks, hybrid preview, field components refactor',
|
|
5
|
+
features: [
|
|
6
|
+
'Form fields: select type, minLength/maxLength, errorMessage, remote commands',
|
|
7
|
+
'Conditional field visibility (showWhen)',
|
|
8
|
+
'Hybrid preview: device frames, container queries',
|
|
9
|
+
'TipTap placeholder extension + Notion-style styles',
|
|
10
|
+
'Server-side field resolution for inline blocks',
|
|
11
|
+
'Inline blocks: standalone form, full field rendering, collapse UI',
|
|
12
|
+
'URL field: rel attribute, external auto-detect, UI redesign',
|
|
13
|
+
'Switch UI component (bits-ui)',
|
|
14
|
+
'Boolean field: Switch toggle zamiast checkbox',
|
|
15
|
+
'Storybook stories for all field types'
|
|
16
|
+
],
|
|
17
|
+
fixes: ['Hybrid preview layout fix'],
|
|
18
|
+
breakingChanges: []
|
|
19
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const update = {
|
|
2
|
+
version: '0.5.4',
|
|
3
|
+
date: '2026-03-04',
|
|
4
|
+
description: 'Collection ordering (DnD), custom list columns',
|
|
5
|
+
features: [
|
|
6
|
+
'Orderable collections: DnD reordering of entries in admin list with keyboard alternative (WCAG 2.1)',
|
|
7
|
+
'Custom list columns: display arbitrary entry fields as columns in collection list',
|
|
8
|
+
'sortOrder column on entries table for persistent manual ordering',
|
|
9
|
+
'reorderEntriesCommand remote for bulk sort order updates'
|
|
10
|
+
],
|
|
11
|
+
fixes: [],
|
|
12
|
+
breakingChanges: [],
|
|
13
|
+
sql: 'ALTER TABLE entry ADD COLUMN sort_order INTEGER;',
|
|
14
|
+
notes: 'Add `orderable: true` to collections that need manual ordering. Add `listColumns: ["fieldSlug"]` to show fields in the entry list.'
|
|
15
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const update = {
|
|
2
|
+
version: '0.5.5',
|
|
3
|
+
date: '2026-03-04',
|
|
4
|
+
description: 'REST API with API key auth, orderable collection fixes',
|
|
5
|
+
features: [
|
|
6
|
+
'REST API with API key authentication — CRUD for collections, singletons, entries, schema introspection, language listing',
|
|
7
|
+
'API key configuration in CMS config (`apiKeys` array)',
|
|
8
|
+
'getEntries supports `orderBy` parameter (column + direction)',
|
|
9
|
+
'Admin scaffold generates REST API catch-all route'
|
|
10
|
+
],
|
|
11
|
+
fixes: [
|
|
12
|
+
'Orderable collections: DnD rows use proper `<tr>` element instead of `<div>` wrapper',
|
|
13
|
+
'Reorder buttons always visible (not sr-only) for better discoverability',
|
|
14
|
+
'Selection enabled for orderable collections',
|
|
15
|
+
'Relation field respects orderable collection sort order',
|
|
16
|
+
'Nav active state: exact match prevents false highlights on similarly-named routes'
|
|
17
|
+
],
|
|
18
|
+
breakingChanges: [],
|
|
19
|
+
notes: 'Add `apiKeys: [{ key: "your-secret", name: "My App" }]` to CMS config. REST API available at `/admin/api/rest/`.'
|
|
20
|
+
};
|
package/dist/updates/index.js
CHANGED
|
@@ -14,7 +14,10 @@ import { update as update022 } from './0.2.2/index.js';
|
|
|
14
14
|
import { update as update050 } from './0.5.0/index.js';
|
|
15
15
|
import { update as update051 } from './0.5.1/index.js';
|
|
16
16
|
import { update as update052 } from './0.5.2/index.js';
|
|
17
|
-
|
|
17
|
+
import { update as update053 } from './0.5.3/index.js';
|
|
18
|
+
import { update as update054 } from './0.5.4/index.js';
|
|
19
|
+
import { update as update055 } from './0.5.5/index.js';
|
|
20
|
+
export const updates = [update0065, update0066, update0067, update0068, update0069, update010, update011, update012, update013, update014, update015, update020, update022, update050, update051, update052, update053, update054, update055];
|
|
18
21
|
export const getUpdatesFrom = (fromVersion) => {
|
|
19
22
|
const fromParts = fromVersion.split('.').map(Number);
|
|
20
23
|
return updates.filter((update) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "includio-cms",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"build": "vite build && npm run prepack",
|
|
@@ -63,6 +63,11 @@
|
|
|
63
63
|
"svelte": "./dist/admin/api/*.js",
|
|
64
64
|
"node": "./dist/admin/api/*.js"
|
|
65
65
|
},
|
|
66
|
+
"./admin/api/rest/handler": {
|
|
67
|
+
"types": "./dist/admin/api/rest/handler.d.ts",
|
|
68
|
+
"svelte": "./dist/admin/api/rest/handler.js",
|
|
69
|
+
"node": "./dist/admin/api/rest/handler.js"
|
|
70
|
+
},
|
|
66
71
|
"./admin/client/account": {
|
|
67
72
|
"types": "./dist/admin/client/account/index.d.ts",
|
|
68
73
|
"svelte": "./dist/admin/client/account/index.js"
|
|
@@ -199,6 +204,7 @@
|
|
|
199
204
|
"@tiptap/extension-highlight": "^3.17.1",
|
|
200
205
|
"@tiptap/extension-image": "^3.17.1",
|
|
201
206
|
"@tiptap/extension-link": "^3.17.1",
|
|
207
|
+
"@tiptap/extension-placeholder": "^3.20.0",
|
|
202
208
|
"@tiptap/extension-table": "^3.17.1",
|
|
203
209
|
"@tiptap/extension-table-cell": "^3.17.1",
|
|
204
210
|
"@tiptap/extension-table-header": "^3.17.1",
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Field } from '../../../types/fields.js';
|
|
3
|
-
import Input from '../../../components/ui/input/input.svelte';
|
|
4
|
-
import { Checkbox } from '../../../components/ui/checkbox/index.js';
|
|
5
|
-
import * as Select from '../../../components/ui/select/index.js';
|
|
6
|
-
import Label from '../../../components/ui/label/label.svelte';
|
|
7
|
-
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
8
|
-
import { getLocalizedLabel } from '../../utils/collectionLabel.js';
|
|
9
|
-
|
|
10
|
-
type Props = {
|
|
11
|
-
fields: Field[];
|
|
12
|
-
data: Record<string, unknown>;
|
|
13
|
-
onchange: (slug: string, value: unknown) => void;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
let { fields, data, onchange }: Props = $props();
|
|
17
|
-
|
|
18
|
-
const interfaceLanguage = useInterfaceLanguage();
|
|
19
|
-
|
|
20
|
-
const SUPPORTED_TYPES = new Set([
|
|
21
|
-
'text',
|
|
22
|
-
'number',
|
|
23
|
-
'boolean',
|
|
24
|
-
'select',
|
|
25
|
-
'radio',
|
|
26
|
-
'url'
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
function handleInput(slug: string, e: Event) {
|
|
30
|
-
const target = e.target as HTMLInputElement;
|
|
31
|
-
onchange(slug, target.value);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function handleNumberInput(slug: string, e: Event) {
|
|
35
|
-
const target = e.target as HTMLInputElement;
|
|
36
|
-
const num = parseFloat(target.value);
|
|
37
|
-
onchange(slug, isNaN(num) ? 0 : num);
|
|
38
|
-
}
|
|
39
|
-
</script>
|
|
40
|
-
|
|
41
|
-
<div class="grid gap-3">
|
|
42
|
-
{#each fields as field}
|
|
43
|
-
{#if SUPPORTED_TYPES.has(field.type)}
|
|
44
|
-
<div class="space-y-1.5">
|
|
45
|
-
{#if field.type !== 'boolean'}
|
|
46
|
-
<Label class="text-xs font-medium">
|
|
47
|
-
{getLocalizedLabel(field.label, interfaceLanguage.current)}
|
|
48
|
-
{#if field.required}<span class="text-destructive">*</span>{/if}
|
|
49
|
-
</Label>
|
|
50
|
-
{/if}
|
|
51
|
-
|
|
52
|
-
{#if field.type === 'text'}
|
|
53
|
-
<Input
|
|
54
|
-
type="text"
|
|
55
|
-
value={String(data[field.slug] ?? '')}
|
|
56
|
-
oninput={(e) => handleInput(field.slug, e)}
|
|
57
|
-
placeholder={field.placeholder ? getLocalizedLabel(field.placeholder, interfaceLanguage.current) : ''}
|
|
58
|
-
class="h-8 text-sm"
|
|
59
|
-
/>
|
|
60
|
-
{:else if field.type === 'number'}
|
|
61
|
-
<Input
|
|
62
|
-
type="number"
|
|
63
|
-
value={String(data[field.slug] ?? 0)}
|
|
64
|
-
oninput={(e) => handleNumberInput(field.slug, e)}
|
|
65
|
-
min={field.min}
|
|
66
|
-
max={field.max}
|
|
67
|
-
step={field.step}
|
|
68
|
-
class="h-8 text-sm"
|
|
69
|
-
/>
|
|
70
|
-
{:else if field.type === 'boolean'}
|
|
71
|
-
<div class="flex items-center gap-2">
|
|
72
|
-
<Checkbox
|
|
73
|
-
checked={Boolean(data[field.slug])}
|
|
74
|
-
onCheckedChange={(checked) => onchange(field.slug, checked)}
|
|
75
|
-
/>
|
|
76
|
-
<Label class="text-xs font-medium">
|
|
77
|
-
{getLocalizedLabel(field.label, interfaceLanguage.current)}
|
|
78
|
-
</Label>
|
|
79
|
-
</div>
|
|
80
|
-
{:else if field.type === 'select' || field.type === 'radio'}
|
|
81
|
-
{@const currentValue = String(data[field.slug] ?? '')}
|
|
82
|
-
{@const currentLabel = field.options.find((o) => o.value === currentValue)}
|
|
83
|
-
<Select.Root
|
|
84
|
-
type="single"
|
|
85
|
-
value={currentValue}
|
|
86
|
-
onValueChange={(v) => onchange(field.slug, v)}
|
|
87
|
-
>
|
|
88
|
-
<Select.Trigger size="sm" class="w-full">
|
|
89
|
-
{#if currentLabel}
|
|
90
|
-
{getLocalizedLabel(currentLabel.label, interfaceLanguage.current)}
|
|
91
|
-
{:else}
|
|
92
|
-
<span class="text-muted-foreground">Wybierz...</span>
|
|
93
|
-
{/if}
|
|
94
|
-
</Select.Trigger>
|
|
95
|
-
<Select.Content>
|
|
96
|
-
{#each field.options as option}
|
|
97
|
-
<Select.Item value={option.value} label={getLocalizedLabel(option.label, interfaceLanguage.current)} />
|
|
98
|
-
{/each}
|
|
99
|
-
</Select.Content>
|
|
100
|
-
</Select.Root>
|
|
101
|
-
{:else if field.type === 'url'}
|
|
102
|
-
{@const urlData = (data[field.slug] as { url?: string; id?: string; text?: string; newTab?: boolean } | undefined) ?? { url: '' }}
|
|
103
|
-
<Input
|
|
104
|
-
type="url"
|
|
105
|
-
value={urlData.url ?? ''}
|
|
106
|
-
oninput={(e) => {
|
|
107
|
-
const target = e.target as HTMLInputElement;
|
|
108
|
-
onchange(field.slug, { ...urlData, url: target.value });
|
|
109
|
-
}}
|
|
110
|
-
placeholder={field.placeholder ? getLocalizedLabel(field.placeholder, interfaceLanguage.current) : 'https://...'}
|
|
111
|
-
class="h-8 text-sm"
|
|
112
|
-
/>
|
|
113
|
-
{#if field.text}
|
|
114
|
-
<Input
|
|
115
|
-
type="text"
|
|
116
|
-
value={urlData.text ?? ''}
|
|
117
|
-
oninput={(e) => {
|
|
118
|
-
const target = e.target as HTMLInputElement;
|
|
119
|
-
onchange(field.slug, { ...urlData, text: target.value });
|
|
120
|
-
}}
|
|
121
|
-
placeholder="Tekst linku"
|
|
122
|
-
class="h-8 text-sm mt-1"
|
|
123
|
-
/>
|
|
124
|
-
{/if}
|
|
125
|
-
{#if field.newTab}
|
|
126
|
-
<div class="flex items-center gap-2 mt-1">
|
|
127
|
-
<Checkbox
|
|
128
|
-
checked={urlData.newTab ?? false}
|
|
129
|
-
onCheckedChange={(checked) => onchange(field.slug, { ...urlData, newTab: checked })}
|
|
130
|
-
/>
|
|
131
|
-
<Label class="text-xs font-medium">Otwórz w nowej karcie</Label>
|
|
132
|
-
</div>
|
|
133
|
-
{/if}
|
|
134
|
-
{/if}
|
|
135
|
-
|
|
136
|
-
{#if field.description}
|
|
137
|
-
<p class="text-muted-foreground text-xs">
|
|
138
|
-
{getLocalizedLabel(field.description, interfaceLanguage.current)}
|
|
139
|
-
</p>
|
|
140
|
-
{/if}
|
|
141
|
-
</div>
|
|
142
|
-
{:else}
|
|
143
|
-
<div class="text-muted-foreground rounded-md border border-dashed p-2 text-center text-xs">
|
|
144
|
-
Pole „{getLocalizedLabel(field.label, interfaceLanguage.current)}" ({field.type}) nie jest obsługiwane w blokach inline
|
|
145
|
-
</div>
|
|
146
|
-
{/if}
|
|
147
|
-
{/each}
|
|
148
|
-
</div>
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Field } from '../../../types/fields.js';
|
|
2
|
-
type Props = {
|
|
3
|
-
fields: Field[];
|
|
4
|
-
data: Record<string, unknown>;
|
|
5
|
-
onchange: (slug: string, value: unknown) => void;
|
|
6
|
-
};
|
|
7
|
-
declare const StandaloneFieldRenderer: import("svelte").Component<Props, {}, "">;
|
|
8
|
-
type StandaloneFieldRenderer = ReturnType<typeof StandaloneFieldRenderer>;
|
|
9
|
-
export default StandaloneFieldRenderer;
|