@btst/stack 2.8.1 → 2.9.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.
- package/README.md +3 -2
- package/dist/components/markdown/index.d.cts +15 -2
- package/dist/components/markdown/index.d.mts +15 -2
- package/dist/components/markdown/index.d.ts +15 -2
- package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.cjs +30 -1
- package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.mjs +30 -1
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.cjs +49 -9
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.mjs +50 -10
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor.cjs +77 -9
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor.mjs +77 -9
- package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.cjs +24 -5
- package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.mjs +24 -5
- package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.cjs +47 -13
- package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.mjs +47 -13
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.cjs +1 -1
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.mjs +1 -1
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.cjs +6 -2
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.mjs +6 -2
- package/dist/packages/stack/src/plugins/media/api/adapters/local.cjs +55 -0
- package/dist/packages/stack/src/plugins/media/api/adapters/local.mjs +37 -0
- package/dist/packages/stack/src/plugins/media/api/getters.cjs +83 -0
- package/dist/packages/stack/src/plugins/media/api/getters.mjs +78 -0
- package/dist/packages/stack/src/plugins/media/api/mutations.cjs +88 -0
- package/dist/packages/stack/src/plugins/media/api/mutations.mjs +82 -0
- package/dist/packages/stack/src/plugins/media/api/plugin.cjs +525 -0
- package/dist/packages/stack/src/plugins/media/api/plugin.mjs +523 -0
- package/dist/packages/stack/src/plugins/media/api/query-key-defs.cjs +19 -0
- package/dist/packages/stack/src/plugins/media/api/query-key-defs.mjs +16 -0
- package/dist/packages/stack/src/plugins/media/api/serializers.cjs +17 -0
- package/dist/packages/stack/src/plugins/media/api/serializers.mjs +14 -0
- package/dist/packages/stack/src/plugins/media/api/storage-adapter.cjs +15 -0
- package/dist/packages/stack/src/plugins/media/api/storage-adapter.mjs +11 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.cjs +129 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.mjs +127 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-preview-button.cjs +58 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-preview-button.mjs +56 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/browse-tab.cjs +94 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/browse-tab.mjs +92 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.cjs +171 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.mjs +168 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.cjs +308 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.mjs +305 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.cjs +104 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.mjs +102 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.cjs +70 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.mjs +68 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/utils.cjs +21 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/utils.mjs +17 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.cjs +35 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.cjs +125 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.mjs +123 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.mjs +33 -0
- package/dist/packages/stack/src/plugins/media/client/hooks/use-media.cjs +222 -0
- package/dist/packages/stack/src/plugins/media/client/hooks/use-media.mjs +214 -0
- package/dist/packages/stack/src/plugins/media/client/plugin.cjs +94 -0
- package/dist/packages/stack/src/plugins/media/client/plugin.mjs +92 -0
- package/dist/packages/stack/src/plugins/media/client/upload.cjs +121 -0
- package/dist/packages/stack/src/plugins/media/client/upload.mjs +119 -0
- package/dist/packages/stack/src/plugins/media/client/utils/image-compression.cjs +67 -0
- package/dist/packages/stack/src/plugins/media/client/utils/image-compression.mjs +65 -0
- package/dist/packages/stack/src/plugins/media/db.cjs +62 -0
- package/dist/packages/stack/src/plugins/media/db.mjs +60 -0
- package/dist/packages/stack/src/plugins/media/schemas.cjs +41 -0
- package/dist/packages/stack/src/plugins/media/schemas.mjs +35 -0
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.cjs +18 -1
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.mjs +19 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.cjs +2 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.mjs +2 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/section/five.cjs +3 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/section/five.mjs +3 -2
- package/dist/packages/ui/src/components/minimal-tiptap/minimal-tiptap.cjs +12 -5
- package/dist/packages/ui/src/components/minimal-tiptap/minimal-tiptap.mjs +12 -5
- package/dist/plugins/blog/client/index.d.cts +58 -1
- package/dist/plugins/blog/client/index.d.mts +58 -1
- package/dist/plugins/blog/client/index.d.ts +58 -1
- package/dist/plugins/cms/client/index.d.cts +73 -3
- package/dist/plugins/cms/client/index.d.mts +73 -3
- package/dist/plugins/cms/client/index.d.ts +73 -3
- package/dist/plugins/kanban/api/index.d.cts +1 -1
- package/dist/plugins/kanban/api/index.d.mts +1 -1
- package/dist/plugins/kanban/api/index.d.ts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.cts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
- package/dist/plugins/kanban/client/index.d.cts +1 -1
- package/dist/plugins/kanban/client/index.d.mts +1 -1
- package/dist/plugins/kanban/client/index.d.ts +1 -1
- package/dist/plugins/kanban/query-keys.d.cts +1 -1
- package/dist/plugins/kanban/query-keys.d.mts +1 -1
- package/dist/plugins/kanban/query-keys.d.ts +1 -1
- package/dist/plugins/media/api/adapters/s3.cjs +106 -0
- package/dist/plugins/media/api/adapters/s3.d.cts +60 -0
- package/dist/plugins/media/api/adapters/s3.d.mts +60 -0
- package/dist/plugins/media/api/adapters/s3.d.ts +60 -0
- package/dist/plugins/media/api/adapters/s3.mjs +104 -0
- package/dist/plugins/media/api/adapters/vercel-blob.cjs +53 -0
- package/dist/plugins/media/api/adapters/vercel-blob.d.cts +41 -0
- package/dist/plugins/media/api/adapters/vercel-blob.d.mts +41 -0
- package/dist/plugins/media/api/adapters/vercel-blob.d.ts +41 -0
- package/dist/plugins/media/api/adapters/vercel-blob.mjs +51 -0
- package/dist/plugins/media/api/index.cjs +26 -0
- package/dist/plugins/media/api/index.d.cts +116 -0
- package/dist/plugins/media/api/index.d.mts +116 -0
- package/dist/plugins/media/api/index.d.ts +116 -0
- package/dist/plugins/media/api/index.mjs +6 -0
- package/dist/plugins/media/client/components/index.cjs +10 -0
- package/dist/plugins/media/client/components/index.d.cts +55 -0
- package/dist/plugins/media/client/components/index.d.mts +55 -0
- package/dist/plugins/media/client/components/index.d.ts +55 -0
- package/dist/plugins/media/client/components/index.mjs +2 -0
- package/dist/plugins/media/client/hooks/index.cjs +13 -0
- package/dist/plugins/media/client/hooks/index.d.cts +53 -0
- package/dist/plugins/media/client/hooks/index.d.mts +53 -0
- package/dist/plugins/media/client/hooks/index.d.ts +53 -0
- package/dist/plugins/media/client/hooks/index.mjs +1 -0
- package/dist/plugins/media/client/index.cjs +9 -0
- package/dist/plugins/media/client/index.d.cts +242 -0
- package/dist/plugins/media/client/index.d.mts +242 -0
- package/dist/plugins/media/client/index.d.ts +242 -0
- package/dist/plugins/media/client/index.mjs +2 -0
- package/dist/plugins/media/client.css +1 -0
- package/dist/plugins/media/query-keys.cjs +72 -0
- package/dist/plugins/media/query-keys.d.cts +49 -0
- package/dist/plugins/media/query-keys.d.mts +49 -0
- package/dist/plugins/media/query-keys.d.ts +49 -0
- package/dist/plugins/media/query-keys.mjs +70 -0
- package/dist/plugins/media/style.css +1 -0
- package/dist/shared/{stack.DRpeDS6X.d.ts → stack.BMx2QYOK.d.ts} +25 -0
- package/dist/shared/stack.BttDsJJn.d.cts +109 -0
- package/dist/shared/stack.BttDsJJn.d.mts +109 -0
- package/dist/shared/stack.BttDsJJn.d.ts +109 -0
- package/dist/shared/stack.C7vfOBmO.d.mts +63 -0
- package/dist/shared/stack.CAni8dnD.d.cts +63 -0
- package/dist/shared/stack.CI8iRKKi.d.cts +286 -0
- package/dist/shared/stack.CLcnSF_b.d.cts +25 -0
- package/dist/shared/stack.CLcnSF_b.d.mts +25 -0
- package/dist/shared/stack.CLcnSF_b.d.ts +25 -0
- package/dist/shared/stack.CYSwntXC.d.ts +63 -0
- package/dist/shared/{stack.Jb0kQDJC.d.mts → stack.Cd6McBu1.d.mts} +25 -0
- package/dist/shared/stack.DJDjdG64.d.ts +286 -0
- package/dist/shared/{stack.BxFl46lB.d.cts → stack.DxQl8Wa1.d.cts} +25 -0
- package/dist/shared/stack.FgBVDSPi.d.mts +286 -0
- package/package.json +113 -4
- package/src/plugins/blog/client/components/forms/image-field.tsx +35 -4
- package/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.tsx +67 -12
- package/src/plugins/blog/client/components/forms/markdown-editor.tsx +106 -10
- package/src/plugins/blog/client/overrides.ts +58 -1
- package/src/plugins/cms/client/components/forms/content-form.tsx +26 -7
- package/src/plugins/cms/client/components/forms/file-upload.tsx +73 -15
- package/src/plugins/cms/client/overrides.ts +57 -2
- package/src/plugins/kanban/client/components/forms/board-form.tsx +1 -1
- package/src/plugins/kanban/client/components/forms/task-form.tsx +7 -1
- package/src/plugins/kanban/client/overrides.ts +25 -0
- package/src/plugins/media/__tests__/__stubs__/vercel-blob-server.ts +9 -0
- package/src/plugins/media/__tests__/getters.test.ts +274 -0
- package/src/plugins/media/__tests__/mutations.test.ts +299 -0
- package/src/plugins/media/__tests__/plugin.test.ts +752 -0
- package/src/plugins/media/__tests__/query-key-defs.test.ts +54 -0
- package/src/plugins/media/__tests__/storage-adapters.test.ts +351 -0
- package/src/plugins/media/api/adapters/local.ts +79 -0
- package/src/plugins/media/api/adapters/s3.ts +198 -0
- package/src/plugins/media/api/adapters/vercel-blob.ts +131 -0
- package/src/plugins/media/api/getters.ts +174 -0
- package/src/plugins/media/api/index.ts +41 -0
- package/src/plugins/media/api/mutations.ts +179 -0
- package/src/plugins/media/api/plugin.ts +855 -0
- package/src/plugins/media/api/query-key-defs.ts +41 -0
- package/src/plugins/media/api/serializers.ts +28 -0
- package/src/plugins/media/api/storage-adapter.ts +139 -0
- package/src/plugins/media/client/components/index.tsx +6 -0
- package/src/plugins/media/client/components/media-picker/asset-card.tsx +150 -0
- package/src/plugins/media/client/components/media-picker/asset-preview-button.tsx +67 -0
- package/src/plugins/media/client/components/media-picker/browse-tab.tsx +116 -0
- package/src/plugins/media/client/components/media-picker/folder-tree.tsx +188 -0
- package/src/plugins/media/client/components/media-picker/index.tsx +347 -0
- package/src/plugins/media/client/components/media-picker/upload-tab.tsx +108 -0
- package/src/plugins/media/client/components/media-picker/url-tab.tsx +72 -0
- package/src/plugins/media/client/components/media-picker/utils.ts +17 -0
- package/src/plugins/media/client/components/pages/library-page.internal.tsx +134 -0
- package/src/plugins/media/client/components/pages/library-page.tsx +42 -0
- package/src/plugins/media/client/hooks/index.tsx +9 -0
- package/src/plugins/media/client/hooks/use-media.tsx +289 -0
- package/src/plugins/media/client/index.ts +4 -0
- package/src/plugins/media/client/overrides.ts +127 -0
- package/src/plugins/media/client/plugin.tsx +184 -0
- package/src/plugins/media/client/upload.ts +171 -0
- package/src/plugins/media/client/utils/image-compression.ts +131 -0
- package/src/plugins/media/client.css +1 -0
- package/src/plugins/media/db.ts +62 -0
- package/src/plugins/media/query-keys.ts +96 -0
- package/src/plugins/media/schemas.ts +37 -0
- package/src/plugins/media/style.css +1 -0
- package/src/plugins/media/types.ts +26 -0
- package/dist/shared/{stack.BOokfhZD.d.cts → stack.B6S3cgwN.d.cts} +16 -16
- package/dist/shared/{stack.CWxAl9K3.d.mts → stack.Bzfx-_lq.d.mts} +16 -16
- package/dist/shared/{stack.BvCR4-9H.d.ts → stack.j5SFLC1d.d.ts} +16 -16
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
function loadImage(file) {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
const url = URL.createObjectURL(file);
|
|
4
|
+
const img = new Image();
|
|
5
|
+
img.onload = () => {
|
|
6
|
+
URL.revokeObjectURL(url);
|
|
7
|
+
resolve(img);
|
|
8
|
+
};
|
|
9
|
+
img.onerror = () => {
|
|
10
|
+
URL.revokeObjectURL(url);
|
|
11
|
+
reject(new Error(`Failed to load image: ${file.name}`));
|
|
12
|
+
};
|
|
13
|
+
img.src = url;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
const SKIP_TYPES = /* @__PURE__ */ new Set(["image/svg+xml", "image/gif"]);
|
|
17
|
+
async function compressImage(file, options = {}) {
|
|
18
|
+
if (!file.type.startsWith("image/") || SKIP_TYPES.has(file.type)) {
|
|
19
|
+
return file;
|
|
20
|
+
}
|
|
21
|
+
if (typeof document === "undefined") return file;
|
|
22
|
+
const {
|
|
23
|
+
maxWidth = 2048,
|
|
24
|
+
maxHeight = 2048,
|
|
25
|
+
quality = 0.85,
|
|
26
|
+
outputFormat
|
|
27
|
+
} = options;
|
|
28
|
+
const img = await loadImage(file);
|
|
29
|
+
let { width, height } = img;
|
|
30
|
+
const needsResize = width > maxWidth || height > maxHeight;
|
|
31
|
+
const needsFormatChange = outputFormat !== void 0 && outputFormat !== file.type;
|
|
32
|
+
if (!needsResize && !needsFormatChange) return file;
|
|
33
|
+
if (needsResize) {
|
|
34
|
+
const ratio = Math.min(maxWidth / width, maxHeight / height);
|
|
35
|
+
width = Math.round(width * ratio);
|
|
36
|
+
height = Math.round(height * ratio);
|
|
37
|
+
}
|
|
38
|
+
const canvas = document.createElement("canvas");
|
|
39
|
+
canvas.width = width;
|
|
40
|
+
canvas.height = height;
|
|
41
|
+
const ctx = canvas.getContext("2d");
|
|
42
|
+
if (!ctx) return file;
|
|
43
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
44
|
+
const mimeType = outputFormat ?? file.type;
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
canvas.toBlob(
|
|
47
|
+
(blob) => {
|
|
48
|
+
if (!blob) {
|
|
49
|
+
reject(new Error("canvas.toBlob returned null"));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
let name = file.name;
|
|
53
|
+
if (outputFormat && outputFormat !== file.type) {
|
|
54
|
+
const ext = outputFormat.split("/")[1] ?? "jpg";
|
|
55
|
+
name = name.replace(/\.[^.]+$/, `.${ext}`);
|
|
56
|
+
}
|
|
57
|
+
resolve(new File([blob], name, { type: mimeType }));
|
|
58
|
+
},
|
|
59
|
+
mimeType,
|
|
60
|
+
quality
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { compressImage };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const db = require('@btst/db');
|
|
4
|
+
|
|
5
|
+
const mediaSchema = db.createDbPlugin("media", {
|
|
6
|
+
asset: {
|
|
7
|
+
modelName: "mediaAsset",
|
|
8
|
+
fields: {
|
|
9
|
+
filename: {
|
|
10
|
+
type: "string",
|
|
11
|
+
required: true
|
|
12
|
+
},
|
|
13
|
+
originalName: {
|
|
14
|
+
type: "string",
|
|
15
|
+
required: true
|
|
16
|
+
},
|
|
17
|
+
mimeType: {
|
|
18
|
+
type: "string",
|
|
19
|
+
required: true
|
|
20
|
+
},
|
|
21
|
+
size: {
|
|
22
|
+
type: "number",
|
|
23
|
+
required: true
|
|
24
|
+
},
|
|
25
|
+
url: {
|
|
26
|
+
type: "string",
|
|
27
|
+
required: true
|
|
28
|
+
},
|
|
29
|
+
folderId: {
|
|
30
|
+
type: "string",
|
|
31
|
+
required: false
|
|
32
|
+
},
|
|
33
|
+
alt: {
|
|
34
|
+
type: "string",
|
|
35
|
+
required: false
|
|
36
|
+
},
|
|
37
|
+
createdAt: {
|
|
38
|
+
type: "date",
|
|
39
|
+
defaultValue: () => /* @__PURE__ */ new Date()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
folder: {
|
|
44
|
+
modelName: "mediaFolder",
|
|
45
|
+
fields: {
|
|
46
|
+
name: {
|
|
47
|
+
type: "string",
|
|
48
|
+
required: true
|
|
49
|
+
},
|
|
50
|
+
parentId: {
|
|
51
|
+
type: "string",
|
|
52
|
+
required: false
|
|
53
|
+
},
|
|
54
|
+
createdAt: {
|
|
55
|
+
type: "date",
|
|
56
|
+
defaultValue: () => /* @__PURE__ */ new Date()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
exports.mediaSchema = mediaSchema;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createDbPlugin } from '@btst/db';
|
|
2
|
+
|
|
3
|
+
const mediaSchema = createDbPlugin("media", {
|
|
4
|
+
asset: {
|
|
5
|
+
modelName: "mediaAsset",
|
|
6
|
+
fields: {
|
|
7
|
+
filename: {
|
|
8
|
+
type: "string",
|
|
9
|
+
required: true
|
|
10
|
+
},
|
|
11
|
+
originalName: {
|
|
12
|
+
type: "string",
|
|
13
|
+
required: true
|
|
14
|
+
},
|
|
15
|
+
mimeType: {
|
|
16
|
+
type: "string",
|
|
17
|
+
required: true
|
|
18
|
+
},
|
|
19
|
+
size: {
|
|
20
|
+
type: "number",
|
|
21
|
+
required: true
|
|
22
|
+
},
|
|
23
|
+
url: {
|
|
24
|
+
type: "string",
|
|
25
|
+
required: true
|
|
26
|
+
},
|
|
27
|
+
folderId: {
|
|
28
|
+
type: "string",
|
|
29
|
+
required: false
|
|
30
|
+
},
|
|
31
|
+
alt: {
|
|
32
|
+
type: "string",
|
|
33
|
+
required: false
|
|
34
|
+
},
|
|
35
|
+
createdAt: {
|
|
36
|
+
type: "date",
|
|
37
|
+
defaultValue: () => /* @__PURE__ */ new Date()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
folder: {
|
|
42
|
+
modelName: "mediaFolder",
|
|
43
|
+
fields: {
|
|
44
|
+
name: {
|
|
45
|
+
type: "string",
|
|
46
|
+
required: true
|
|
47
|
+
},
|
|
48
|
+
parentId: {
|
|
49
|
+
type: "string",
|
|
50
|
+
required: false
|
|
51
|
+
},
|
|
52
|
+
createdAt: {
|
|
53
|
+
type: "date",
|
|
54
|
+
defaultValue: () => /* @__PURE__ */ new Date()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export { mediaSchema };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const z = require('zod');
|
|
4
|
+
|
|
5
|
+
const AssetListQuerySchema = z.z.object({
|
|
6
|
+
folderId: z.z.string().optional(),
|
|
7
|
+
mimeType: z.z.string().optional(),
|
|
8
|
+
query: z.z.string().optional(),
|
|
9
|
+
offset: z.z.coerce.number().int().min(0).optional(),
|
|
10
|
+
limit: z.z.coerce.number().int().min(1).max(100).optional()
|
|
11
|
+
});
|
|
12
|
+
const createAssetSchema = z.z.object({
|
|
13
|
+
filename: z.z.string().min(1),
|
|
14
|
+
originalName: z.z.string().min(1),
|
|
15
|
+
mimeType: z.z.string().min(1),
|
|
16
|
+
// Allow 0 for URL-registered assets where size is unknown at registration time.
|
|
17
|
+
size: z.z.number().int().min(0),
|
|
18
|
+
url: z.z.httpUrl(),
|
|
19
|
+
folderId: z.z.string().optional(),
|
|
20
|
+
alt: z.z.string().optional()
|
|
21
|
+
});
|
|
22
|
+
const updateAssetSchema = z.z.object({
|
|
23
|
+
alt: z.z.string().optional(),
|
|
24
|
+
folderId: z.z.string().nullable().optional()
|
|
25
|
+
});
|
|
26
|
+
const createFolderSchema = z.z.object({
|
|
27
|
+
name: z.z.string().min(1),
|
|
28
|
+
parentId: z.z.string().optional()
|
|
29
|
+
});
|
|
30
|
+
const uploadTokenRequestSchema = z.z.object({
|
|
31
|
+
filename: z.z.string().min(1),
|
|
32
|
+
mimeType: z.z.string().min(1),
|
|
33
|
+
size: z.z.number().int().positive(),
|
|
34
|
+
folderId: z.z.string().optional()
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
exports.AssetListQuerySchema = AssetListQuerySchema;
|
|
38
|
+
exports.createAssetSchema = createAssetSchema;
|
|
39
|
+
exports.createFolderSchema = createFolderSchema;
|
|
40
|
+
exports.updateAssetSchema = updateAssetSchema;
|
|
41
|
+
exports.uploadTokenRequestSchema = uploadTokenRequestSchema;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
const AssetListQuerySchema = z.object({
|
|
4
|
+
folderId: z.string().optional(),
|
|
5
|
+
mimeType: z.string().optional(),
|
|
6
|
+
query: z.string().optional(),
|
|
7
|
+
offset: z.coerce.number().int().min(0).optional(),
|
|
8
|
+
limit: z.coerce.number().int().min(1).max(100).optional()
|
|
9
|
+
});
|
|
10
|
+
const createAssetSchema = z.object({
|
|
11
|
+
filename: z.string().min(1),
|
|
12
|
+
originalName: z.string().min(1),
|
|
13
|
+
mimeType: z.string().min(1),
|
|
14
|
+
// Allow 0 for URL-registered assets where size is unknown at registration time.
|
|
15
|
+
size: z.number().int().min(0),
|
|
16
|
+
url: z.httpUrl(),
|
|
17
|
+
folderId: z.string().optional(),
|
|
18
|
+
alt: z.string().optional()
|
|
19
|
+
});
|
|
20
|
+
const updateAssetSchema = z.object({
|
|
21
|
+
alt: z.string().optional(),
|
|
22
|
+
folderId: z.string().nullable().optional()
|
|
23
|
+
});
|
|
24
|
+
const createFolderSchema = z.object({
|
|
25
|
+
name: z.string().min(1),
|
|
26
|
+
parentId: z.string().optional()
|
|
27
|
+
});
|
|
28
|
+
const uploadTokenRequestSchema = z.object({
|
|
29
|
+
filename: z.string().min(1),
|
|
30
|
+
mimeType: z.string().min(1),
|
|
31
|
+
size: z.number().int().positive(),
|
|
32
|
+
folderId: z.string().optional()
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export { AssetListQuerySchema, createAssetSchema, createFolderSchema, updateAssetSchema, uploadTokenRequestSchema };
|
package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.cjs
CHANGED
|
@@ -7,6 +7,7 @@ const React = require('react');
|
|
|
7
7
|
const button = require('../../../button.cjs');
|
|
8
8
|
const label = require('../../../label.cjs');
|
|
9
9
|
const input = require('../../../input.cjs');
|
|
10
|
+
const separator = require('../../../separator.cjs');
|
|
10
11
|
|
|
11
12
|
function _interopNamespaceCompat(e) {
|
|
12
13
|
if (e && typeof e === 'object' && 'default' in e) return e;
|
|
@@ -24,7 +25,8 @@ const React__namespace = /*#__PURE__*/_interopNamespaceCompat(React);
|
|
|
24
25
|
|
|
25
26
|
const ImageEditBlock = ({
|
|
26
27
|
editor,
|
|
27
|
-
close
|
|
28
|
+
close,
|
|
29
|
+
imagePickerTrigger: ImagePickerTrigger
|
|
28
30
|
}) => {
|
|
29
31
|
const fileInputRef = React__namespace.useRef(null);
|
|
30
32
|
const [link, setLink] = React__namespace.useState("");
|
|
@@ -79,6 +81,21 @@ const ImageEditBlock = ({
|
|
|
79
81
|
] })
|
|
80
82
|
] }),
|
|
81
83
|
/* @__PURE__ */ jsxRuntime.jsx(button.Button, { type: "button", className: "w-full", onClick: handleClick, children: "Upload from your computer" }),
|
|
84
|
+
ImagePickerTrigger && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
85
|
+
/* @__PURE__ */ jsxRuntime.jsx(separator.Separator, {}),
|
|
86
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
87
|
+
/* @__PURE__ */ jsxRuntime.jsx(label.Label, { className: "text-muted-foreground text-xs uppercase tracking-wider", children: "Or browse media library" }),
|
|
88
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
89
|
+
ImagePickerTrigger,
|
|
90
|
+
{
|
|
91
|
+
onSelect: (url) => {
|
|
92
|
+
editor.commands.setImages([{ src: url }]);
|
|
93
|
+
close();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
] })
|
|
98
|
+
] }),
|
|
82
99
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
83
100
|
"input",
|
|
84
101
|
{
|
package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.mjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { Button } from '../../../button.mjs';
|
|
4
4
|
import { Label } from '../../../label.mjs';
|
|
5
5
|
import { Input } from '../../../input.mjs';
|
|
6
|
+
import { Separator } from '../../../separator.mjs';
|
|
6
7
|
|
|
7
8
|
const ImageEditBlock = ({
|
|
8
9
|
editor,
|
|
9
|
-
close
|
|
10
|
+
close,
|
|
11
|
+
imagePickerTrigger: ImagePickerTrigger
|
|
10
12
|
}) => {
|
|
11
13
|
const fileInputRef = React.useRef(null);
|
|
12
14
|
const [link, setLink] = React.useState("");
|
|
@@ -61,6 +63,21 @@ const ImageEditBlock = ({
|
|
|
61
63
|
] })
|
|
62
64
|
] }),
|
|
63
65
|
/* @__PURE__ */ jsx(Button, { type: "button", className: "w-full", onClick: handleClick, children: "Upload from your computer" }),
|
|
66
|
+
ImagePickerTrigger && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
67
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
68
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
69
|
+
/* @__PURE__ */ jsx(Label, { className: "text-muted-foreground text-xs uppercase tracking-wider", children: "Or browse media library" }),
|
|
70
|
+
/* @__PURE__ */ jsx(
|
|
71
|
+
ImagePickerTrigger,
|
|
72
|
+
{
|
|
73
|
+
onSelect: (url) => {
|
|
74
|
+
editor.commands.setImages([{ src: url }]);
|
|
75
|
+
close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
] })
|
|
80
|
+
] }),
|
|
64
81
|
/* @__PURE__ */ jsx(
|
|
65
82
|
"input",
|
|
66
83
|
{
|
package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.cjs
CHANGED
|
@@ -7,7 +7,7 @@ const toolbarButton = require('../toolbar-button.cjs');
|
|
|
7
7
|
const dialog = require('../../../dialog.cjs');
|
|
8
8
|
const imageEditBlock = require('./image-edit-block.cjs');
|
|
9
9
|
|
|
10
|
-
const ImageEditDialog = ({ editor, size, variant }) => {
|
|
10
|
+
const ImageEditDialog = ({ editor, size, variant, imagePickerTrigger }) => {
|
|
11
11
|
const [open, setOpen] = React.useState(false);
|
|
12
12
|
return /* @__PURE__ */ jsxRuntime.jsxs(dialog.Dialog, { open, onOpenChange: setOpen, children: [
|
|
13
13
|
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -26,7 +26,7 @@ const ImageEditDialog = ({ editor, size, variant }) => {
|
|
|
26
26
|
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: "Select image" }),
|
|
27
27
|
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogDescription, { className: "sr-only", children: "Upload an image from your computer" })
|
|
28
28
|
] }),
|
|
29
|
-
/* @__PURE__ */ jsxRuntime.jsx(imageEditBlock.ImageEditBlock, { editor, close: () => setOpen(false) })
|
|
29
|
+
/* @__PURE__ */ jsxRuntime.jsx(imageEditBlock.ImageEditBlock, { editor, close: () => setOpen(false), imagePickerTrigger })
|
|
30
30
|
] })
|
|
31
31
|
] });
|
|
32
32
|
};
|
package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { ToolbarButton } from '../toolbar-button.mjs';
|
|
|
5
5
|
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '../../../dialog.mjs';
|
|
6
6
|
import { ImageEditBlock } from './image-edit-block.mjs';
|
|
7
7
|
|
|
8
|
-
const ImageEditDialog = ({ editor, size, variant }) => {
|
|
8
|
+
const ImageEditDialog = ({ editor, size, variant, imagePickerTrigger }) => {
|
|
9
9
|
const [open, setOpen] = useState(false);
|
|
10
10
|
return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: setOpen, children: [
|
|
11
11
|
/* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
@@ -24,7 +24,7 @@ const ImageEditDialog = ({ editor, size, variant }) => {
|
|
|
24
24
|
/* @__PURE__ */ jsx(DialogTitle, { children: "Select image" }),
|
|
25
25
|
/* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: "Upload an image from your computer" })
|
|
26
26
|
] }),
|
|
27
|
-
/* @__PURE__ */ jsx(ImageEditBlock, { editor, close: () => setOpen(false) })
|
|
27
|
+
/* @__PURE__ */ jsx(ImageEditBlock, { editor, close: () => setOpen(false), imagePickerTrigger })
|
|
28
28
|
] })
|
|
29
29
|
] });
|
|
30
30
|
};
|
|
@@ -42,11 +42,12 @@ const SectionFive = ({
|
|
|
42
42
|
activeActions = formatActions.map((action) => action.value),
|
|
43
43
|
mainActionCount = 0,
|
|
44
44
|
size,
|
|
45
|
-
variant
|
|
45
|
+
variant,
|
|
46
|
+
imagePickerTrigger
|
|
46
47
|
}) => {
|
|
47
48
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
48
49
|
/* @__PURE__ */ jsxRuntime.jsx(linkEditPopover.LinkEditPopover, { editor, size, variant }),
|
|
49
|
-
/* @__PURE__ */ jsxRuntime.jsx(imageEditDialog.ImageEditDialog, { editor, size, variant }),
|
|
50
|
+
/* @__PURE__ */ jsxRuntime.jsx(imageEditDialog.ImageEditDialog, { editor, size, variant, imagePickerTrigger }),
|
|
50
51
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51
52
|
toolbarSection.ToolbarSection,
|
|
52
53
|
{
|
|
@@ -38,11 +38,12 @@ const SectionFive = ({
|
|
|
38
38
|
activeActions = formatActions.map((action) => action.value),
|
|
39
39
|
mainActionCount = 0,
|
|
40
40
|
size,
|
|
41
|
-
variant
|
|
41
|
+
variant,
|
|
42
|
+
imagePickerTrigger
|
|
42
43
|
}) => {
|
|
43
44
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
44
45
|
/* @__PURE__ */ jsx(LinkEditPopover, { editor, size, variant }),
|
|
45
|
-
/* @__PURE__ */ jsx(ImageEditDialog, { editor, size, variant }),
|
|
46
|
+
/* @__PURE__ */ jsx(ImageEditDialog, { editor, size, variant, imagePickerTrigger }),
|
|
46
47
|
/* @__PURE__ */ jsx(
|
|
47
48
|
ToolbarSection,
|
|
48
49
|
{
|
|
@@ -16,7 +16,10 @@ const useMinimalTiptap = require('./hooks/use-minimal-tiptap.cjs');
|
|
|
16
16
|
const measuredContainer = require('./components/measured-container.cjs');
|
|
17
17
|
const useTiptapEditor = require('./hooks/use-tiptap-editor.cjs');
|
|
18
18
|
|
|
19
|
-
const Toolbar = ({
|
|
19
|
+
const Toolbar = ({
|
|
20
|
+
editor,
|
|
21
|
+
imagePickerTrigger
|
|
22
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-border flex h-12 shrink-0 overflow-x-auto border-b p-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-max items-center gap-px", children: [
|
|
20
23
|
/* @__PURE__ */ jsxRuntime.jsx(one.SectionOne, { editor, activeLevels: [1, 2, 3, 4, 5, 6] }),
|
|
21
24
|
/* @__PURE__ */ jsxRuntime.jsx(separator.Separator, { orientation: "vertical", className: "mx-2" }),
|
|
22
25
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -51,7 +54,8 @@ const Toolbar = ({ editor }) => /* @__PURE__ */ jsxRuntime.jsx("div", { classNam
|
|
|
51
54
|
{
|
|
52
55
|
editor,
|
|
53
56
|
activeActions: ["codeBlock", "blockquote", "horizontalRule"],
|
|
54
|
-
mainActionCount: 0
|
|
57
|
+
mainActionCount: 0,
|
|
58
|
+
imagePickerTrigger
|
|
55
59
|
}
|
|
56
60
|
)
|
|
57
61
|
] }) });
|
|
@@ -60,6 +64,7 @@ const MinimalTiptapEditor = ({
|
|
|
60
64
|
onChange,
|
|
61
65
|
className,
|
|
62
66
|
editorContentClassName,
|
|
67
|
+
imagePickerTrigger,
|
|
63
68
|
...props
|
|
64
69
|
}) => {
|
|
65
70
|
const editor = useMinimalTiptap.useMinimalTiptapEditor({
|
|
@@ -75,7 +80,8 @@ const MinimalTiptapEditor = ({
|
|
|
75
80
|
{
|
|
76
81
|
editor,
|
|
77
82
|
className,
|
|
78
|
-
editorContentClassName
|
|
83
|
+
editorContentClassName,
|
|
84
|
+
imagePickerTrigger
|
|
79
85
|
}
|
|
80
86
|
) });
|
|
81
87
|
};
|
|
@@ -83,7 +89,8 @@ MinimalTiptapEditor.displayName = "MinimalTiptapEditor";
|
|
|
83
89
|
const MainMinimalTiptapEditor = ({
|
|
84
90
|
editor: providedEditor,
|
|
85
91
|
className,
|
|
86
|
-
editorContentClassName
|
|
92
|
+
editorContentClassName,
|
|
93
|
+
imagePickerTrigger
|
|
87
94
|
}) => {
|
|
88
95
|
const { editor } = useTiptapEditor.useTiptapEditor(providedEditor);
|
|
89
96
|
if (!editor) {
|
|
@@ -100,7 +107,7 @@ const MainMinimalTiptapEditor = ({
|
|
|
100
107
|
className
|
|
101
108
|
),
|
|
102
109
|
children: [
|
|
103
|
-
/* @__PURE__ */ jsxRuntime.jsx(Toolbar, { editor }),
|
|
110
|
+
/* @__PURE__ */ jsxRuntime.jsx(Toolbar, { editor, imagePickerTrigger }),
|
|
104
111
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
105
112
|
index.EditorContent,
|
|
106
113
|
{
|
|
@@ -12,7 +12,10 @@ import { useMinimalTiptapEditor } from './hooks/use-minimal-tiptap.mjs';
|
|
|
12
12
|
import { MeasuredContainer } from './components/measured-container.mjs';
|
|
13
13
|
import { useTiptapEditor } from './hooks/use-tiptap-editor.mjs';
|
|
14
14
|
|
|
15
|
-
const Toolbar = ({
|
|
15
|
+
const Toolbar = ({
|
|
16
|
+
editor,
|
|
17
|
+
imagePickerTrigger
|
|
18
|
+
}) => /* @__PURE__ */ jsx("div", { className: "border-border flex h-12 shrink-0 overflow-x-auto border-b p-2", children: /* @__PURE__ */ jsxs("div", { className: "flex w-max items-center gap-px", children: [
|
|
16
19
|
/* @__PURE__ */ jsx(SectionOne, { editor, activeLevels: [1, 2, 3, 4, 5, 6] }),
|
|
17
20
|
/* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-2" }),
|
|
18
21
|
/* @__PURE__ */ jsx(
|
|
@@ -47,7 +50,8 @@ const Toolbar = ({ editor }) => /* @__PURE__ */ jsx("div", { className: "border-
|
|
|
47
50
|
{
|
|
48
51
|
editor,
|
|
49
52
|
activeActions: ["codeBlock", "blockquote", "horizontalRule"],
|
|
50
|
-
mainActionCount: 0
|
|
53
|
+
mainActionCount: 0,
|
|
54
|
+
imagePickerTrigger
|
|
51
55
|
}
|
|
52
56
|
)
|
|
53
57
|
] }) });
|
|
@@ -56,6 +60,7 @@ const MinimalTiptapEditor = ({
|
|
|
56
60
|
onChange,
|
|
57
61
|
className,
|
|
58
62
|
editorContentClassName,
|
|
63
|
+
imagePickerTrigger,
|
|
59
64
|
...props
|
|
60
65
|
}) => {
|
|
61
66
|
const editor = useMinimalTiptapEditor({
|
|
@@ -71,7 +76,8 @@ const MinimalTiptapEditor = ({
|
|
|
71
76
|
{
|
|
72
77
|
editor,
|
|
73
78
|
className,
|
|
74
|
-
editorContentClassName
|
|
79
|
+
editorContentClassName,
|
|
80
|
+
imagePickerTrigger
|
|
75
81
|
}
|
|
76
82
|
) });
|
|
77
83
|
};
|
|
@@ -79,7 +85,8 @@ MinimalTiptapEditor.displayName = "MinimalTiptapEditor";
|
|
|
79
85
|
const MainMinimalTiptapEditor = ({
|
|
80
86
|
editor: providedEditor,
|
|
81
87
|
className,
|
|
82
|
-
editorContentClassName
|
|
88
|
+
editorContentClassName,
|
|
89
|
+
imagePickerTrigger
|
|
83
90
|
}) => {
|
|
84
91
|
const { editor } = useTiptapEditor(providedEditor);
|
|
85
92
|
if (!editor) {
|
|
@@ -96,7 +103,7 @@ const MainMinimalTiptapEditor = ({
|
|
|
96
103
|
className
|
|
97
104
|
),
|
|
98
105
|
children: [
|
|
99
|
-
/* @__PURE__ */ jsx(Toolbar, { editor }),
|
|
106
|
+
/* @__PURE__ */ jsx(Toolbar, { editor, imagePickerTrigger }),
|
|
100
107
|
/* @__PURE__ */ jsx(
|
|
101
108
|
EditorContent,
|
|
102
109
|
{
|
|
@@ -392,6 +392,17 @@ declare const BLOG_LOCALIZATION: {
|
|
|
392
392
|
};
|
|
393
393
|
type BlogLocalization = typeof BLOG_LOCALIZATION;
|
|
394
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Props for the overridable blog featured image input component.
|
|
397
|
+
*/
|
|
398
|
+
interface BlogImageInputFieldProps {
|
|
399
|
+
/** Current image URL value */
|
|
400
|
+
value: string;
|
|
401
|
+
/** Called when the image URL changes */
|
|
402
|
+
onChange: (value: string) => void;
|
|
403
|
+
/** Whether the field is required */
|
|
404
|
+
isRequired?: boolean;
|
|
405
|
+
}
|
|
395
406
|
/**
|
|
396
407
|
* Context passed to lifecycle hooks
|
|
397
408
|
*/
|
|
@@ -435,9 +446,55 @@ interface BlogPluginOverrides {
|
|
|
435
446
|
*/
|
|
436
447
|
Image?: ComponentType<React.ImgHTMLAttributes<HTMLImageElement> & Record<string, any>>;
|
|
437
448
|
/**
|
|
438
|
-
* Function used to upload
|
|
449
|
+
* Function used to upload a new image file and return its URL.
|
|
450
|
+
* This is separate from `imagePicker`, which selects an existing asset URL.
|
|
439
451
|
*/
|
|
440
452
|
uploadImage: (file: File) => Promise<string>;
|
|
453
|
+
/**
|
|
454
|
+
* Optional custom component for the featured image field.
|
|
455
|
+
*
|
|
456
|
+
* When provided it replaces the default file-upload input entirely.
|
|
457
|
+
* The component receives `value` (current URL string) and `onChange` (setter).
|
|
458
|
+
*
|
|
459
|
+
* Typical use case: render a preview when a value is set, and a media-picker
|
|
460
|
+
* trigger when no value is set.
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```tsx
|
|
464
|
+
* imageInputField: ({ value, onChange }) =>
|
|
465
|
+
* value ? (
|
|
466
|
+
* <div>
|
|
467
|
+
* <img src={value} alt="Preview" />
|
|
468
|
+
* <MediaPicker trigger={<button>Change</button>} accept={["image/*"]}
|
|
469
|
+
* onSelect={(assets) => onChange(assets[0].url)} />
|
|
470
|
+
* </div>
|
|
471
|
+
* ) : (
|
|
472
|
+
* <MediaPicker trigger={<button>Browse media</button>} accept={["image/*"]}
|
|
473
|
+
* onSelect={(assets) => onChange(assets[0].url)} />
|
|
474
|
+
* )
|
|
475
|
+
* ```
|
|
476
|
+
*/
|
|
477
|
+
imageInputField?: ComponentType<BlogImageInputFieldProps>;
|
|
478
|
+
/**
|
|
479
|
+
* Optional trigger component for a media picker.
|
|
480
|
+
* When provided, it is rendered adjacent to the Markdown editor and allows
|
|
481
|
+
* users to browse and select previously uploaded assets.
|
|
482
|
+
* Receives `onSelect(url)` — insert the chosen URL into the editor.
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```tsx
|
|
486
|
+
* imagePicker: ({ onSelect }) => (
|
|
487
|
+
* <MediaPicker
|
|
488
|
+
* trigger={<Button size="sm" variant="outline">Browse media</Button>}
|
|
489
|
+
* accept={["image/*"]}
|
|
490
|
+
* onSelect={(assets) => onSelect(assets[0].url)}
|
|
491
|
+
* />
|
|
492
|
+
* )
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
imagePicker?: ComponentType<{
|
|
496
|
+
onSelect: (url: string) => void;
|
|
497
|
+
}>;
|
|
441
498
|
/**
|
|
442
499
|
* Localization object for the blog plugin
|
|
443
500
|
*/
|
|
@@ -392,6 +392,17 @@ declare const BLOG_LOCALIZATION: {
|
|
|
392
392
|
};
|
|
393
393
|
type BlogLocalization = typeof BLOG_LOCALIZATION;
|
|
394
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Props for the overridable blog featured image input component.
|
|
397
|
+
*/
|
|
398
|
+
interface BlogImageInputFieldProps {
|
|
399
|
+
/** Current image URL value */
|
|
400
|
+
value: string;
|
|
401
|
+
/** Called when the image URL changes */
|
|
402
|
+
onChange: (value: string) => void;
|
|
403
|
+
/** Whether the field is required */
|
|
404
|
+
isRequired?: boolean;
|
|
405
|
+
}
|
|
395
406
|
/**
|
|
396
407
|
* Context passed to lifecycle hooks
|
|
397
408
|
*/
|
|
@@ -435,9 +446,55 @@ interface BlogPluginOverrides {
|
|
|
435
446
|
*/
|
|
436
447
|
Image?: ComponentType<React.ImgHTMLAttributes<HTMLImageElement> & Record<string, any>>;
|
|
437
448
|
/**
|
|
438
|
-
* Function used to upload
|
|
449
|
+
* Function used to upload a new image file and return its URL.
|
|
450
|
+
* This is separate from `imagePicker`, which selects an existing asset URL.
|
|
439
451
|
*/
|
|
440
452
|
uploadImage: (file: File) => Promise<string>;
|
|
453
|
+
/**
|
|
454
|
+
* Optional custom component for the featured image field.
|
|
455
|
+
*
|
|
456
|
+
* When provided it replaces the default file-upload input entirely.
|
|
457
|
+
* The component receives `value` (current URL string) and `onChange` (setter).
|
|
458
|
+
*
|
|
459
|
+
* Typical use case: render a preview when a value is set, and a media-picker
|
|
460
|
+
* trigger when no value is set.
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```tsx
|
|
464
|
+
* imageInputField: ({ value, onChange }) =>
|
|
465
|
+
* value ? (
|
|
466
|
+
* <div>
|
|
467
|
+
* <img src={value} alt="Preview" />
|
|
468
|
+
* <MediaPicker trigger={<button>Change</button>} accept={["image/*"]}
|
|
469
|
+
* onSelect={(assets) => onChange(assets[0].url)} />
|
|
470
|
+
* </div>
|
|
471
|
+
* ) : (
|
|
472
|
+
* <MediaPicker trigger={<button>Browse media</button>} accept={["image/*"]}
|
|
473
|
+
* onSelect={(assets) => onChange(assets[0].url)} />
|
|
474
|
+
* )
|
|
475
|
+
* ```
|
|
476
|
+
*/
|
|
477
|
+
imageInputField?: ComponentType<BlogImageInputFieldProps>;
|
|
478
|
+
/**
|
|
479
|
+
* Optional trigger component for a media picker.
|
|
480
|
+
* When provided, it is rendered adjacent to the Markdown editor and allows
|
|
481
|
+
* users to browse and select previously uploaded assets.
|
|
482
|
+
* Receives `onSelect(url)` — insert the chosen URL into the editor.
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```tsx
|
|
486
|
+
* imagePicker: ({ onSelect }) => (
|
|
487
|
+
* <MediaPicker
|
|
488
|
+
* trigger={<Button size="sm" variant="outline">Browse media</Button>}
|
|
489
|
+
* accept={["image/*"]}
|
|
490
|
+
* onSelect={(assets) => onSelect(assets[0].url)}
|
|
491
|
+
* />
|
|
492
|
+
* )
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
imagePicker?: ComponentType<{
|
|
496
|
+
onSelect: (url: string) => void;
|
|
497
|
+
}>;
|
|
441
498
|
/**
|
|
442
499
|
* Localization object for the blog plugin
|
|
443
500
|
*/
|