styles
This commit is contained in:
parent
dccd07ac53
commit
3e671ccbe0
4 changed files with 212 additions and 231 deletions
|
|
@ -1,122 +1,64 @@
|
||||||
"use client";
|
'use client'
|
||||||
import { useThemeMode } from '@/hooks/useThemeMode'
|
import { useThemeMode } from '@/hooks/useThemeMode'
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
const STAR_TONES = [255, 210, 160, 110, 65];
|
const PARTICLE_COUNT = 120
|
||||||
const WAVE_TONES = [55, 90, 125, 165, 205];
|
|
||||||
|
|
||||||
const WAVE_CONFIGS = [
|
interface Particle {
|
||||||
{ amp: 26, freq: 0.011, speed: 0.35, phase: 0.0, yBase: 0.38, width: 1.6, toneIdx: 0 },
|
x: number
|
||||||
{ amp: 20, freq: 0.015, speed: 0.50, phase: 1.3, yBase: 0.52, width: 1.8, toneIdx: 1 },
|
y: number
|
||||||
{ amp: 30, freq: 0.008, speed: 0.28, phase: 2.6, yBase: 0.63, width: 2.2, toneIdx: 2 },
|
r: number
|
||||||
{ amp: 16, freq: 0.019, speed: 0.65, phase: 0.8, yBase: 0.74, width: 2.0, toneIdx: 3 },
|
vx: number
|
||||||
{ amp: 22, freq: 0.013, speed: 0.42, phase: 3.2, yBase: 0.85, width: 2.6, toneIdx: 4 },
|
vy: number
|
||||||
];
|
alpha: number
|
||||||
|
phase: number
|
||||||
interface Star {
|
twinkleSpeed: number
|
||||||
x: number; y: number; r: number;
|
|
||||||
v: number; baseAlpha: number;
|
|
||||||
phase: number; twinkleSpeed: number;
|
|
||||||
vx: number; vy: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Wave {
|
function initParticles(W: number, H: number): Particle[] {
|
||||||
amp: number; freq: number; speed: number;
|
return Array.from({ length: PARTICLE_COUNT }, () => ({
|
||||||
phase: number; yBase: number; width: number;
|
|
||||||
v: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function initStars(W: number, H: number): Star[] {
|
|
||||||
return Array.from({ length: 180 }, () => {
|
|
||||||
const toneIdx = Math.floor(Math.random() * STAR_TONES.length);
|
|
||||||
return {
|
|
||||||
x: Math.random() * W,
|
x: Math.random() * W,
|
||||||
y: Math.random() * H,
|
y: Math.random() * H,
|
||||||
r: 0.4 + Math.random() * 1.6,
|
r: 0.5 + Math.random() * 2.0,
|
||||||
v: STAR_TONES[toneIdx],
|
vx: (Math.random() - 0.5) * 0.25,
|
||||||
baseAlpha: 0.5 + toneIdx * 0.1,
|
|
||||||
phase: Math.random() * Math.PI * 2,
|
|
||||||
twinkleSpeed: 0.8 + Math.random() * 2.0,
|
|
||||||
vx: (Math.random() - 0.5) * 0.3,
|
|
||||||
vy: (Math.random() - 0.5) * 0.15,
|
vy: (Math.random() - 0.5) * 0.15,
|
||||||
};
|
alpha: 0.15 + Math.random() * 0.35,
|
||||||
});
|
phase: Math.random() * Math.PI * 2,
|
||||||
|
twinkleSpeed: 0.4 + Math.random() * 1.2,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
function initWaves(): Wave[] {
|
function drawParticles(
|
||||||
return WAVE_CONFIGS.map(c => ({ ...c, v: WAVE_TONES[c.toneIdx] }));
|
ctx: CanvasRenderingContext2D,
|
||||||
}
|
particles: Particle[],
|
||||||
|
W: number,
|
||||||
|
H: number,
|
||||||
|
isDark: boolean,
|
||||||
|
) {
|
||||||
|
const now = Date.now() / 1000
|
||||||
|
// dark mode: light particles on dark bg; light mode: mid-gray particles on light bg
|
||||||
|
const tone = isDark ? 220 : 140
|
||||||
|
|
||||||
function drawStars(ctx: CanvasRenderingContext2D, stars: Star[], W: number, H: number, ga: number) {
|
for (const p of particles) {
|
||||||
const now = Date.now() / 1000;
|
p.x += p.vx
|
||||||
for (const s of stars) {
|
p.y += p.vy
|
||||||
s.x += s.vx; s.y += s.vy;
|
if (p.x < 0) p.x = W
|
||||||
if (s.x < 0) s.x = W; if (s.x > W) s.x = 0;
|
if (p.x > W) p.x = 0
|
||||||
if (s.y < 0) s.y = H; if (s.y > H) s.y = 0;
|
if (p.y < 0) p.y = H
|
||||||
const twinkle = 0.35 + 0.65 * ((Math.sin(now * s.twinkleSpeed + s.phase) + 1) / 2);
|
if (p.y > H) p.y = 0
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
|
const twinkle = 0.4 + 0.6 * ((Math.sin(now * p.twinkleSpeed + p.phase) + 1) / 2)
|
||||||
ctx.fillStyle = `rgba(${s.v},${s.v},${s.v},${s.baseAlpha * twinkle * ga})`;
|
ctx.beginPath()
|
||||||
ctx.fill();
|
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2)
|
||||||
|
ctx.fillStyle = `rgba(${tone},${tone},${tone},${p.alpha * twinkle})`
|
||||||
|
ctx.fill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildWavePts(w: Wave, W: number, H: number, now: number) {
|
|
||||||
const pts: { x: number; y: number }[] = [];
|
|
||||||
for (let x = 0; x <= W; x += 8) {
|
|
||||||
pts.push({ x, y: H * w.yBase + Math.sin(x * w.freq + w.phase + now * w.speed) * w.amp });
|
|
||||||
}
|
|
||||||
return pts;
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawWavePath(ctx: CanvasRenderingContext2D, pts: { x: number; y: number }[], W: number, H: number) {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(pts[0].x, pts[0].y);
|
|
||||||
for (let i = 0; i < pts.length - 1; i++) {
|
|
||||||
const mx = (pts[i].x + pts[i + 1].x) / 2;
|
|
||||||
const my = (pts[i].y + pts[i + 1].y) / 2;
|
|
||||||
ctx.quadraticCurveTo(pts[i].x, pts[i].y, mx, my);
|
|
||||||
}
|
|
||||||
ctx.lineTo(pts[pts.length - 1].x, pts[pts.length - 1].y);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawWaves(ctx: CanvasRenderingContext2D, waves: Wave[], W: number, H: number, ga: number, bgR: number) {
|
|
||||||
const now = Date.now() / 1000;
|
|
||||||
const bg = Math.round(bgR);
|
|
||||||
|
|
||||||
for (const w of waves) {
|
|
||||||
const v = w.v;
|
|
||||||
const yBase = H * w.yBase;
|
|
||||||
const pts = buildWavePts(w, W, H, now);
|
|
||||||
|
|
||||||
const grad = ctx.createLinearGradient(0, yBase - w.amp, 0, yBase + w.amp * 3);
|
|
||||||
grad.addColorStop(0, `rgba(${v},${v},${v},${0.030 * ga})`);
|
|
||||||
grad.addColorStop(0.4, `rgba(${v},${v},${v},${0.012 * ga})`);
|
|
||||||
grad.addColorStop(1, `rgba(${bg},${bg},${bg},0)`);
|
|
||||||
|
|
||||||
drawWavePath(ctx, pts, W, H);
|
|
||||||
ctx.lineTo(W, H); ctx.lineTo(0, H); ctx.closePath();
|
|
||||||
ctx.fillStyle = grad;
|
|
||||||
ctx.fill();
|
|
||||||
|
|
||||||
drawWavePath(ctx, pts, W, H);
|
|
||||||
ctx.strokeStyle = `rgba(${v},${v},${v},${0.85 * ga})`;
|
|
||||||
ctx.lineWidth = w.width;
|
|
||||||
ctx.lineJoin = "round";
|
|
||||||
ctx.lineCap = "round";
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Component ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
interface HeroBackgroundProps {
|
interface HeroBackgroundProps {
|
||||||
/** Controlled from outside if you have a global theme toggle. */
|
isDark?: boolean
|
||||||
isDark?: boolean;
|
onToggle?: (isDark: boolean) => void
|
||||||
/** Fires when the internal toggle is clicked (omit to hide the button). */
|
showToggle?: boolean
|
||||||
onToggle?: (isDark: boolean) => void;
|
|
||||||
/** Show the built-in toggle button. Defaults to true. */
|
|
||||||
showToggle?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HeroBackground({
|
export default function HeroBackground({
|
||||||
|
|
@ -124,108 +66,89 @@ export default function HeroBackground({
|
||||||
onToggle,
|
onToggle,
|
||||||
showToggle = true,
|
showToggle = true,
|
||||||
}: HeroBackgroundProps) {
|
}: HeroBackgroundProps) {
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||||
// REPLACE WITH:
|
const { isDark: hookDark, toggle } = useThemeMode()
|
||||||
const { isDark: hookDark, toggle } = useThemeMode();
|
const isDark = externalIsDark !== undefined ? externalIsDark : hookDark
|
||||||
|
|
||||||
// Use external control if provided, otherwise hook
|
function handleToggle() {
|
||||||
const isDark = externalIsDark !== undefined ? externalIsDark : hookDark;
|
toggle()
|
||||||
|
onToggle?.(!isDark)
|
||||||
|
}
|
||||||
|
|
||||||
// REPLACE WITH:
|
const darkRef = useRef(isDark)
|
||||||
function handleToggle() {
|
useEffect(() => {
|
||||||
toggle();
|
darkRef.current = isDark
|
||||||
onToggle?.(!isDark);
|
}, [isDark])
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvas = canvasRef.current;
|
const canvas = canvasRef.current
|
||||||
if (!canvas) return;
|
if (!canvas) return
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext('2d')
|
||||||
if (!ctx) return;
|
if (!ctx) return
|
||||||
|
|
||||||
let W = 0, H = 0;
|
let W = 0,
|
||||||
let stars: Star[] = [];
|
H = 0
|
||||||
let waves: Wave[] = [];
|
let particles: Particle[] = []
|
||||||
let rafId: number;
|
let rafId: number
|
||||||
|
let bgR = 0
|
||||||
// Animated bg colour
|
|
||||||
let bgR = 0, bgG = 0, bgB = 0;
|
|
||||||
let crossfade = 0;
|
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
W = canvas!.width = canvas!.offsetWidth;
|
W = canvas!.width = canvas!.offsetWidth
|
||||||
H = canvas!.height = canvas!.offsetHeight;
|
H = canvas!.height = canvas!.offsetHeight
|
||||||
stars = initStars(W, H);
|
particles = initParticles(W, H)
|
||||||
waves = initWaves();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loop() {
|
function loop() {
|
||||||
// Read current target from closure — updated via ref below
|
const dark = darkRef.current
|
||||||
const dark = darkRef.current;
|
const targetBg = dark ? 0 : 255
|
||||||
const targetBg = dark ? 0 : 255;
|
bgR += (targetBg - bgR) * 0.04
|
||||||
const targetCross = dark ? 0 : 1;
|
|
||||||
|
|
||||||
bgR += (targetBg - bgR) * 0.04;
|
const bg = Math.round(bgR)
|
||||||
bgG += (targetBg - bgG) * 0.04;
|
ctx!.fillStyle = `rgb(${bg},${bg},${bg})`
|
||||||
bgB += (targetBg - bgB) * 0.04;
|
ctx!.fillRect(0, 0, W, H)
|
||||||
crossfade += (targetCross - crossfade) * 0.03;
|
|
||||||
|
|
||||||
ctx!.fillStyle = `rgb(${Math.round(bgR)},${Math.round(bgG)},${Math.round(bgB)})`;
|
drawParticles(ctx!, particles, W, H, dark)
|
||||||
ctx!.fillRect(0, 0, W, H);
|
|
||||||
|
|
||||||
const sa = 1 - crossfade;
|
rafId = requestAnimationFrame(loop)
|
||||||
const wa = crossfade;
|
|
||||||
if (sa > 0.01) drawStars(ctx!, stars, W, H, sa);
|
|
||||||
if (wa > 0.01) drawWaves(ctx!, waves, W, H, wa, bgR);
|
|
||||||
|
|
||||||
rafId = requestAnimationFrame(loop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ro = new ResizeObserver(resize);
|
const ro = new ResizeObserver(resize)
|
||||||
ro.observe(canvas);
|
ro.observe(canvas)
|
||||||
resize();
|
resize()
|
||||||
rafId = requestAnimationFrame(loop);
|
rafId = requestAnimationFrame(loop)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelAnimationFrame(rafId);
|
cancelAnimationFrame(rafId)
|
||||||
ro.disconnect();
|
ro.disconnect()
|
||||||
};
|
}
|
||||||
}, []);
|
}, [])
|
||||||
|
|
||||||
// Keep a ref so the loop closure always reads the latest isDark
|
|
||||||
const darkRef = useRef(isDark);
|
|
||||||
useEffect(() => { darkRef.current = isDark; }, [isDark]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ position: "relative", width: "100%", height: "100%" }}>
|
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
|
||||||
<canvas
|
<canvas ref={canvasRef} style={{ display: 'block', width: '100%', height: '100%' }} />
|
||||||
ref={canvasRef}
|
|
||||||
style={{ display: "block", width: "100%", height: "100%" }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{showToggle && (
|
{showToggle && (
|
||||||
<button
|
<button
|
||||||
onClick={handleToggle}
|
onClick={handleToggle}
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: 'absolute',
|
||||||
top: 20,
|
top: 20,
|
||||||
right: 20,
|
right: 20,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
gap: 8,
|
gap: 8,
|
||||||
padding: "8px 16px",
|
padding: '8px 16px',
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
cursor: "pointer",
|
cursor: 'pointer',
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
border: `1px solid ${isDark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.18)"}`,
|
border: `1px solid ${isDark ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.18)'}`,
|
||||||
background: isDark ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.07)",
|
background: isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.07)',
|
||||||
color: isDark ? "#fff" : "#111",
|
color: isDark ? '#fff' : '#111',
|
||||||
transition: "background 0.4s, color 0.4s, border-color 0.4s",
|
transition: 'background 0.4s, color 0.4s, border-color 0.4s',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isDark ? "☀" : "☾"} {isDark ? "Light mode" : "Dark mode"}
|
{isDark ? '☀' : '☾'} {isDark ? 'Light mode' : 'Dark mode'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,15 @@ type LowImpactHeroType =
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
richText?: never
|
richText?: never
|
||||||
theme?: never
|
theme?: never
|
||||||
|
heroBackground?: never
|
||||||
|
heroBackgroundOpacity?: never
|
||||||
}
|
}
|
||||||
| (Omit<Page['hero'], 'richText'> & {
|
| (Omit<Page['hero'], 'richText'> & {
|
||||||
children?: never
|
children?: never
|
||||||
richText?: Page['hero']['richText']
|
richText?: Page['hero']['richText']
|
||||||
theme?: string
|
theme?: string
|
||||||
|
heroBackground?: any
|
||||||
|
heroBackgroundOpacity?: number
|
||||||
})
|
})
|
||||||
|
|
||||||
const bgMap: Record<string, string> = {
|
const bgMap: Record<string, string> = {
|
||||||
|
|
@ -23,12 +27,34 @@ const bgMap: Record<string, string> = {
|
||||||
secondary: 'bg-secondary',
|
secondary: 'bg-secondary',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LowImpactHero: React.FC<LowImpactHeroType> = ({ children, richText, theme }) => {
|
export const LowImpactHero: React.FC<LowImpactHeroType> = ({
|
||||||
|
children,
|
||||||
|
richText,
|
||||||
|
theme,
|
||||||
|
heroBackground,
|
||||||
|
heroBackgroundOpacity,
|
||||||
|
}) => {
|
||||||
|
const isImage = theme === 'image' && heroBackground
|
||||||
|
const overlayOpacity = (heroBackgroundOpacity ?? 60) / 100
|
||||||
const bg = bgMap[theme ?? 'default'] ?? 'bg-transparent'
|
const bg = bgMap[theme ?? 'default'] ?? 'bg-transparent'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={bg}>
|
<div
|
||||||
<div className="container mt-16">
|
className={isImage ? 'relative' : bg}
|
||||||
|
style={
|
||||||
|
isImage && typeof heroBackground === 'object'
|
||||||
|
? {
|
||||||
|
backgroundImage: `url(${heroBackground.url})`,
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundPosition: 'center',
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isImage && (
|
||||||
|
<div className="absolute inset-0 bg-background" style={{ opacity: overlayOpacity }} />
|
||||||
|
)}
|
||||||
|
<div className="container mt-16 relative z-10">
|
||||||
<div className="max-w-[48rem] [&_h3]:opacity-60">
|
<div className="max-w-[48rem] [&_h3]:opacity-60">
|
||||||
{children || (richText && <RichText data={richText} enableGutter={false} />)}
|
{children || (richText && <RichText data={richText} enableGutter={false} />)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,30 @@ export const hero: Field = {
|
||||||
{ label: 'Muted', value: 'muted' },
|
{ label: 'Muted', value: 'muted' },
|
||||||
{ label: 'Card', value: 'card' },
|
{ label: 'Card', value: 'card' },
|
||||||
{ label: 'Secondary', value: 'secondary' },
|
{ label: 'Secondary', value: 'secondary' },
|
||||||
|
{ label: 'Image', value: 'image' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'heroBackground',
|
||||||
|
type: 'upload',
|
||||||
|
relationTo: 'media',
|
||||||
|
admin: {
|
||||||
|
condition: (_, { type, theme } = {}) => type === 'lowImpact' && theme === 'image',
|
||||||
|
description: 'Background image for the hero section',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'heroBackgroundOpacity',
|
||||||
|
type: 'number',
|
||||||
|
defaultValue: 60,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
admin: {
|
||||||
|
condition: (_, { type, theme } = {}) => type === 'lowImpact' && theme === 'image',
|
||||||
|
description: 'Overlay opacity 0 (transparent) to 100 (solid)',
|
||||||
|
step: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'richText',
|
name: 'richText',
|
||||||
type: 'richText',
|
type: 'richText',
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,15 @@ export interface Page {
|
||||||
title: string;
|
title: string;
|
||||||
hero: {
|
hero: {
|
||||||
type: 'none' | 'highImpact' | 'mediumImpact' | 'lowImpact';
|
type: 'none' | 'highImpact' | 'mediumImpact' | 'lowImpact';
|
||||||
theme?: ('default' | 'muted' | 'card' | 'secondary') | null;
|
theme?: ('default' | 'muted' | 'card' | 'secondary' | 'image') | null;
|
||||||
|
/**
|
||||||
|
* Background image for the hero section
|
||||||
|
*/
|
||||||
|
heroBackground?: (string | null) | Media;
|
||||||
|
/**
|
||||||
|
* Overlay opacity 0 (transparent) to 100 (solid)
|
||||||
|
*/
|
||||||
|
heroBackgroundOpacity?: number | null;
|
||||||
richText?: {
|
richText?: {
|
||||||
root: {
|
root: {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -273,56 +281,6 @@ export interface Page {
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
_status?: ('draft' | 'published') | null;
|
_status?: ('draft' | 'published') | null;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
|
||||||
* via the `definition` "posts".
|
|
||||||
*/
|
|
||||||
export interface Post {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
heroImage?: (string | null) | Media;
|
|
||||||
content: {
|
|
||||||
root: {
|
|
||||||
type: string;
|
|
||||||
children: {
|
|
||||||
type: any;
|
|
||||||
version: number;
|
|
||||||
[k: string]: unknown;
|
|
||||||
}[];
|
|
||||||
direction: ('ltr' | 'rtl') | null;
|
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
|
||||||
indent: number;
|
|
||||||
version: number;
|
|
||||||
};
|
|
||||||
[k: string]: unknown;
|
|
||||||
};
|
|
||||||
relatedPosts?: (string | Post)[] | null;
|
|
||||||
categories?: (string | Category)[] | null;
|
|
||||||
meta?: {
|
|
||||||
title?: string | null;
|
|
||||||
/**
|
|
||||||
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
|
||||||
*/
|
|
||||||
image?: (string | null) | Media;
|
|
||||||
description?: string | null;
|
|
||||||
};
|
|
||||||
publishedAt?: string | null;
|
|
||||||
authors?: (string | User)[] | null;
|
|
||||||
populatedAuthors?:
|
|
||||||
| {
|
|
||||||
id?: string | null;
|
|
||||||
name?: string | null;
|
|
||||||
}[]
|
|
||||||
| null;
|
|
||||||
/**
|
|
||||||
* When enabled, the slug will auto-generate from the title field on save and autosave.
|
|
||||||
*/
|
|
||||||
generateSlug?: boolean | null;
|
|
||||||
slug: string;
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
_status?: ('draft' | 'published') | null;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "media".
|
* via the `definition` "media".
|
||||||
|
|
@ -442,6 +400,56 @@ export interface FolderInterface {
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "posts".
|
||||||
|
*/
|
||||||
|
export interface Post {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
heroImage?: (string | null) | Media;
|
||||||
|
content: {
|
||||||
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: any;
|
||||||
|
version: number;
|
||||||
|
[k: string]: unknown;
|
||||||
|
}[];
|
||||||
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
};
|
||||||
|
relatedPosts?: (string | Post)[] | null;
|
||||||
|
categories?: (string | Category)[] | null;
|
||||||
|
meta?: {
|
||||||
|
title?: string | null;
|
||||||
|
/**
|
||||||
|
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
|
||||||
|
*/
|
||||||
|
image?: (string | null) | Media;
|
||||||
|
description?: string | null;
|
||||||
|
};
|
||||||
|
publishedAt?: string | null;
|
||||||
|
authors?: (string | User)[] | null;
|
||||||
|
populatedAuthors?:
|
||||||
|
| {
|
||||||
|
id?: string | null;
|
||||||
|
name?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
/**
|
||||||
|
* When enabled, the slug will auto-generate from the title field on save and autosave.
|
||||||
|
*/
|
||||||
|
generateSlug?: boolean | null;
|
||||||
|
slug: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
_status?: ('draft' | 'published') | null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "categories".
|
* via the `definition` "categories".
|
||||||
|
|
@ -1117,6 +1125,8 @@ export interface PagesSelect<T extends boolean = true> {
|
||||||
| {
|
| {
|
||||||
type?: T;
|
type?: T;
|
||||||
theme?: T;
|
theme?: T;
|
||||||
|
heroBackground?: T;
|
||||||
|
heroBackgroundOpacity?: T;
|
||||||
richText?: T;
|
richText?: T;
|
||||||
links?:
|
links?:
|
||||||
| T
|
| T
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue