@bygghammar/cms-platform 0.5.0 → 0.7.0
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/package.json +54 -3
- package/src/backoffice/PreviewLightbox.tsx +66 -0
- package/src/backoffice/ProgressBar.tsx +36 -0
- package/src/backoffice/RichTextEditor.tsx +633 -0
- package/src/backoffice/media/ConfirmDialog.tsx +77 -0
- package/src/backoffice/media/MediaCleanup.tsx +186 -0
- package/src/backoffice/media/MediaReplaceDialog.tsx +158 -0
- package/src/backoffice/media/ReuploadOverwrite.tsx +226 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bygghammar/cms-platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Shared CMS platform: blocks, block types, i18n core, UI primitives and design-token contract used by every Bygghammar/Voyara site.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -72,7 +72,14 @@
|
|
|
72
72
|
"./backoffice/forms/ConditionBuilder": "./src/backoffice/forms/ConditionBuilder.tsx",
|
|
73
73
|
"./backoffice/forms/RuleSimulator": "./src/backoffice/forms/RuleSimulator.tsx",
|
|
74
74
|
"./backoffice/forms/SmartTags": "./src/backoffice/forms/SmartTags.tsx",
|
|
75
|
-
"./backoffice/forms/ActivityLog": "./src/backoffice/forms/ActivityLog.tsx"
|
|
75
|
+
"./backoffice/forms/ActivityLog": "./src/backoffice/forms/ActivityLog.tsx",
|
|
76
|
+
"./backoffice/ProgressBar": "./src/backoffice/ProgressBar.tsx",
|
|
77
|
+
"./backoffice/PreviewLightbox": "./src/backoffice/PreviewLightbox.tsx",
|
|
78
|
+
"./backoffice/RichTextEditor": "./src/backoffice/RichTextEditor.tsx",
|
|
79
|
+
"./backoffice/media/ConfirmDialog": "./src/backoffice/media/ConfirmDialog.tsx",
|
|
80
|
+
"./backoffice/media/MediaCleanup": "./src/backoffice/media/MediaCleanup.tsx",
|
|
81
|
+
"./backoffice/media/MediaReplaceDialog": "./src/backoffice/media/MediaReplaceDialog.tsx",
|
|
82
|
+
"./backoffice/media/ReuploadOverwrite": "./src/backoffice/media/ReuploadOverwrite.tsx"
|
|
76
83
|
},
|
|
77
84
|
"dependencies": {
|
|
78
85
|
"fflate": "^0.8.3"
|
|
@@ -85,7 +92,18 @@
|
|
|
85
92
|
"sonner": ">=2",
|
|
86
93
|
"@supabase/supabase-js": ">=2",
|
|
87
94
|
"@tanstack/react-start": ">=1",
|
|
88
|
-
"@tanstack/react-router": ">=1"
|
|
95
|
+
"@tanstack/react-router": ">=1",
|
|
96
|
+
"@tiptap/react": ">=3",
|
|
97
|
+
"@tiptap/starter-kit": ">=3",
|
|
98
|
+
"@tiptap/pm": ">=3",
|
|
99
|
+
"@tiptap/extensions": ">=3",
|
|
100
|
+
"@tiptap/extension-image": ">=3",
|
|
101
|
+
"@tiptap/extension-text-align": ">=3",
|
|
102
|
+
"@tiptap/extension-highlight": ">=3",
|
|
103
|
+
"@tiptap/extension-subscript": ">=3",
|
|
104
|
+
"@tiptap/extension-superscript": ">=3",
|
|
105
|
+
"@tiptap/extension-text-style": ">=3",
|
|
106
|
+
"@tiptap/extension-table": ">=3"
|
|
89
107
|
},
|
|
90
108
|
"peerDependenciesMeta": {
|
|
91
109
|
"@supabase/supabase-js": {
|
|
@@ -96,6 +114,39 @@
|
|
|
96
114
|
},
|
|
97
115
|
"@tanstack/react-router": {
|
|
98
116
|
"optional": true
|
|
117
|
+
},
|
|
118
|
+
"@tiptap/react": {
|
|
119
|
+
"optional": true
|
|
120
|
+
},
|
|
121
|
+
"@tiptap/starter-kit": {
|
|
122
|
+
"optional": true
|
|
123
|
+
},
|
|
124
|
+
"@tiptap/pm": {
|
|
125
|
+
"optional": true
|
|
126
|
+
},
|
|
127
|
+
"@tiptap/extensions": {
|
|
128
|
+
"optional": true
|
|
129
|
+
},
|
|
130
|
+
"@tiptap/extension-image": {
|
|
131
|
+
"optional": true
|
|
132
|
+
},
|
|
133
|
+
"@tiptap/extension-text-align": {
|
|
134
|
+
"optional": true
|
|
135
|
+
},
|
|
136
|
+
"@tiptap/extension-highlight": {
|
|
137
|
+
"optional": true
|
|
138
|
+
},
|
|
139
|
+
"@tiptap/extension-subscript": {
|
|
140
|
+
"optional": true
|
|
141
|
+
},
|
|
142
|
+
"@tiptap/extension-superscript": {
|
|
143
|
+
"optional": true
|
|
144
|
+
},
|
|
145
|
+
"@tiptap/extension-text-style": {
|
|
146
|
+
"optional": true
|
|
147
|
+
},
|
|
148
|
+
"@tiptap/extension-table": {
|
|
149
|
+
"optional": true
|
|
99
150
|
}
|
|
100
151
|
}
|
|
101
152
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { Lightbox } from "../ui/Lightbox";
|
|
3
|
+
|
|
4
|
+
const DESIGN_WIDTH = 1280;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Backoffice preview lightbox: renders a route inside a scaled iframe.
|
|
8
|
+
* Closing follows the shared site behaviour — Escape, backdrop click, or a
|
|
9
|
+
* click on the preview itself (the frame is look-only, covered by a
|
|
10
|
+
* transparent click catcher because iframe clicks never bubble out).
|
|
11
|
+
*/
|
|
12
|
+
export function PreviewLightbox({
|
|
13
|
+
open,
|
|
14
|
+
onClose,
|
|
15
|
+
src,
|
|
16
|
+
title,
|
|
17
|
+
}: {
|
|
18
|
+
open: boolean;
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
src: string;
|
|
21
|
+
title: string;
|
|
22
|
+
}) {
|
|
23
|
+
const [scale, setScale] = useState(1);
|
|
24
|
+
const [frameHeight, setFrameHeight] = useState(800);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!open) return;
|
|
28
|
+
const measure = () => {
|
|
29
|
+
const availW = Math.max(320, window.innerWidth - 96);
|
|
30
|
+
const availH = Math.max(320, window.innerHeight - 120);
|
|
31
|
+
setScale(Math.min(1, availW / DESIGN_WIDTH));
|
|
32
|
+
setFrameHeight(Math.round(availH / Math.min(1, availW / DESIGN_WIDTH)));
|
|
33
|
+
};
|
|
34
|
+
measure();
|
|
35
|
+
window.addEventListener("resize", measure);
|
|
36
|
+
return () => window.removeEventListener("resize", measure);
|
|
37
|
+
}, [open]);
|
|
38
|
+
|
|
39
|
+
if (!open) return null;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Lightbox open={open} onClose={onClose} label={title} closeOnContentClick>
|
|
43
|
+
<div
|
|
44
|
+
className="relative bg-white shadow-2xl overflow-hidden"
|
|
45
|
+
style={{
|
|
46
|
+
width: DESIGN_WIDTH * scale,
|
|
47
|
+
height: frameHeight * scale,
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
<iframe
|
|
51
|
+
src={src}
|
|
52
|
+
title={title}
|
|
53
|
+
className="absolute left-0 top-0 border-0"
|
|
54
|
+
style={{
|
|
55
|
+
width: DESIGN_WIDTH,
|
|
56
|
+
height: frameHeight,
|
|
57
|
+
transform: `scale(${scale})`,
|
|
58
|
+
transformOrigin: "top left",
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
{/* Click catcher: iframe clicks can't bubble to the overlay. */}
|
|
62
|
+
<div className="absolute inset-0 cursor-pointer" onClick={onClose} />
|
|
63
|
+
</div>
|
|
64
|
+
</Lightbox>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
/** 0–100, or null for an indeterminate bar */
|
|
3
|
+
value: number | null;
|
|
4
|
+
label?: string;
|
|
5
|
+
detail?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/** Slim determinate/indeterminate progress bar used by the backoffice long-running tools. */
|
|
9
|
+
export function ProgressBar({ value, label, detail }: Props) {
|
|
10
|
+
const pct = value === null ? null : Math.max(0, Math.min(100, value));
|
|
11
|
+
return (
|
|
12
|
+
<div className="w-full">
|
|
13
|
+
{label || detail ? (
|
|
14
|
+
<div className="mb-2 flex items-baseline justify-between gap-4">
|
|
15
|
+
<span className="text-[0.65rem] uppercase tracking-[0.22em] text-ink/60">{label}</span>
|
|
16
|
+
<span className="text-[0.65rem] tabular-nums text-ink/45">
|
|
17
|
+
{detail}
|
|
18
|
+
{pct !== null ? `${detail ? " · " : ""}${Math.round(pct)}%` : ""}
|
|
19
|
+
</span>
|
|
20
|
+
</div>
|
|
21
|
+
) : null}
|
|
22
|
+
<div className="h-1.5 w-full overflow-hidden bg-ink/10">
|
|
23
|
+
{pct === null ? (
|
|
24
|
+
<div className="h-full w-1/3 animate-[progress-slide_1.2s_ease-in-out_infinite] bg-sea" />
|
|
25
|
+
) : (
|
|
26
|
+
<div
|
|
27
|
+
className="h-full bg-sea transition-[width] duration-300 ease-out"
|
|
28
|
+
style={{ width: `${pct}%` }}
|
|
29
|
+
/>
|
|
30
|
+
)}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default ProgressBar;
|