Skip to content

Commit b8e2334

Browse files
committed
no scroll sections
1 parent 426ca88 commit b8e2334

File tree

16 files changed

+902
-141
lines changed

16 files changed

+902
-141
lines changed

public/images/data/api.png

11.4 KB
Loading
30.6 KB
Loading

public/images/data/oidc-logos.png

31.4 KB
Loading
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
import clsx from "clsx";
3+
import ContentLimiter from "./ContentLimiter.astro";
4+
5+
interface Props {
6+
title: string | any;
7+
titleTag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
8+
className?: string;
9+
id?: string;
10+
maxWidth?: number;
11+
variant?: 'white' | 'gray';
12+
theme?: 'light' | 'dark';
13+
}
14+
15+
const {
16+
title,
17+
titleTag: TitleTag = 'h2',
18+
className,
19+
id,
20+
maxWidth = 1100,
21+
variant = 'white',
22+
theme = 'light',
23+
} = Astro.props;
24+
---
25+
26+
<section
27+
class={clsx(
28+
"flexible-section",
29+
`variant-${variant}`,
30+
`theme-${theme}`,
31+
className
32+
)}
33+
id={id}
34+
aria-labelledby={id ? `${id}-title` : undefined}
35+
>
36+
<ContentLimiter maxWidth={maxWidth}>
37+
<div class="flexible-content">
38+
<div class="header">
39+
<TitleTag class="section-title" id={id ? `${id}-title` : undefined} set:html={title}></TitleTag>
40+
</div>
41+
42+
<slot />
43+
44+
<div class="columns-container">
45+
<div class="column left-column">
46+
<slot name="left" />
47+
</div>
48+
49+
<div class="column right-column">
50+
<slot name="right" />
51+
</div>
52+
</div>
53+
</div>
54+
</ContentLimiter>
55+
</section>
56+
57+
<style lang="scss">
58+
@use "../styles/mixins" as *;
59+
60+
.flexible-section {
61+
width: 100%;
62+
padding: 4rem 0;
63+
64+
&.variant-white {
65+
background-color: var(--background-primary, white);
66+
}
67+
68+
&.variant-gray {
69+
background-color: var(--background-secondary, #f5f5f5);
70+
}
71+
72+
&.theme-light {
73+
color: var(--text-body-primary, #333);
74+
75+
.section-title {
76+
color: var(--text-heading-primary, #111);
77+
}
78+
}
79+
80+
&.theme-dark {
81+
color: var(--text-body-light, #f5f5f5);
82+
background-color: var(--background-dark, #222);
83+
84+
.section-title {
85+
color: var(--text-heading-light, white);
86+
}
87+
}
88+
89+
.flexible-content {
90+
display: flex;
91+
flex-direction: column;
92+
width: 100%;
93+
94+
.header {
95+
width: 100%;
96+
box-sizing: border-box;
97+
padding-bottom: 2rem;
98+
99+
.section-title {
100+
margin-bottom: 1rem;
101+
font-size: 2rem;
102+
font-weight: 700;
103+
line-height: 1.2;
104+
105+
@media (min-width: 768px) {
106+
font-size: 2.5rem;
107+
margin-bottom: 1.5rem;
108+
}
109+
}
110+
}
111+
112+
.columns-container {
113+
display: flex;
114+
flex-direction: column;
115+
width: 100%;
116+
gap: 2rem;
117+
align-items: flex-start;
118+
119+
@media (min-width: 768px) {
120+
flex-direction: row;
121+
gap: 2rem;
122+
}
123+
124+
.column {
125+
width: 100%;
126+
127+
@media (min-width: 768px) {
128+
flex: 1;
129+
}
130+
131+
&.left-column, &.right-column {
132+
display: flex;
133+
flex-direction: column;
134+
}
135+
}
136+
}
137+
}
138+
}
139+
</style>
140+

src/components/video/Video.astro

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
---
2+
import clsx from "clsx";
3+
import YouTubeVideo from "./YouTubeVideo.astro";
4+
5+
interface Props {
6+
src: string;
7+
type?: string;
8+
alt: string;
9+
poster?: string;
10+
controls?: boolean;
11+
autoplay?: boolean;
12+
loop?: boolean;
13+
muted?: boolean;
14+
playsInline?: boolean;
15+
preload?: "auto" | "metadata" | "none";
16+
width?: number | string;
17+
height?: number | string;
18+
className?: string;
19+
caption?: string;
20+
captionClass?: string;
21+
// YouTube specific props
22+
startAt?: number; // Start time in seconds
23+
showControls?: boolean;
24+
showInfo?: boolean;
25+
allowFullscreen?: boolean;
26+
rel?: boolean;
27+
}
28+
29+
const {
30+
src,
31+
type,
32+
alt,
33+
poster,
34+
controls = true,
35+
autoplay = false,
36+
loop = false,
37+
muted = false,
38+
playsInline = true,
39+
preload = "metadata",
40+
width,
41+
height,
42+
className,
43+
caption,
44+
captionClass,
45+
// YouTube specific props
46+
startAt = 0,
47+
showControls = true,
48+
showInfo = true,
49+
allowFullscreen = true,
50+
rel = false,
51+
} = Astro.props;
52+
53+
// Function to detect if URL is YouTube
54+
function isYouTubeUrl(url: string): boolean {
55+
const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be|youtube-nocookie\.com)/;
56+
return youtubeRegex.test(url);
57+
}
58+
59+
// Function to extract YouTube video ID
60+
function extractYouTubeId(url: string): string | null {
61+
// Match YouTube URL patterns
62+
const regExp = /^.*(youtu.be\/|v\/|e\/|u\/\w+\/|embed\/|v=)([^#\&\?]*).*/;
63+
const match = url.match(regExp);
64+
65+
return (match && match[2].length === 11) ? match[2] : null;
66+
}
67+
68+
// Check if this is a YouTube URL
69+
const isYouTube = isYouTubeUrl(src);
70+
const youtubeId = isYouTube ? extractYouTubeId(src) : null;
71+
72+
// For regular videos: determine if the source is a local file or external URL
73+
const isExternal = !isYouTube && (src.startsWith('http://') || src.startsWith('https://') || src.startsWith('//'));
74+
75+
// For regular videos: determine video type if not provided
76+
const videoType = type ? type : src.endsWith('.mp4')
77+
? 'video/mp4'
78+
: src.endsWith('.webm')
79+
? 'video/webm'
80+
: src.endsWith('.ogg')
81+
? 'video/ogg'
82+
: 'video/mp4';
83+
84+
// Set responsive styles for regular videos
85+
const videoStyles = {
86+
width: width ? (typeof width === 'number' ? `${width}px` : width) : '100%',
87+
height: height ? (typeof height === 'number' ? `${height}px` : height) : 'auto',
88+
};
89+
---
90+
91+
<div class={clsx("video-container", className)}>
92+
{isYouTube && youtubeId ? (
93+
<YouTubeVideo
94+
videoId={youtubeId}
95+
title={alt}
96+
autoplay={autoplay}
97+
startAt={startAt}
98+
showControls={showControls}
99+
showInfo={showInfo}
100+
allowFullscreen={allowFullscreen}
101+
muted={muted}
102+
rel={rel}
103+
width={width}
104+
height={height}
105+
/>
106+
) : (
107+
<video
108+
class="video-element"
109+
style={videoStyles}
110+
{controls}
111+
{autoplay}
112+
{loop}
113+
{muted}
114+
playsinline={playsInline}
115+
{preload}
116+
{poster}
117+
aria-label={alt}
118+
>
119+
{isExternal ? (
120+
<source src={src} type={videoType} />
121+
) : (
122+
<source src={src.startsWith('/') ? src : `/${src}`} type={videoType} />
123+
)}
124+
<p>Your browser doesn't support HTML video. Here is a <a href={src}>link to the video</a> instead.</p>
125+
</video>
126+
)}
127+
128+
{caption && (
129+
<div class={clsx("video-caption", captionClass)}>
130+
<p>{caption}</p>
131+
</div>
132+
)}
133+
</div>
134+
135+
<style lang="scss">
136+
@use "../../styles/mixins" as *;
137+
138+
.video-container {
139+
width: 100%;
140+
margin: 1.5rem 0;
141+
position: relative;
142+
overflow: hidden;
143+
border-radius: 6px;
144+
background-color: var(--background-secondary, #f5f5f5);
145+
box-shadow: var(--shadow-sm, 0 2px 8px rgba(0, 0, 0, 0.1));
146+
147+
.video-element {
148+
display: block;
149+
max-width: 100%;
150+
box-sizing: border-box;
151+
border-radius: 6px;
152+
}
153+
154+
.video-caption {
155+
padding: 0.75rem;
156+
background-color: var(--background-secondary, #f5f5f5);
157+
border-bottom-left-radius: 6px;
158+
border-bottom-right-radius: 6px;
159+
160+
p {
161+
@include typography(caption);
162+
margin: 0;
163+
text-align: center;
164+
color: var(--text-body-secondary, #666);
165+
}
166+
}
167+
168+
@media (max-width: 768px) {
169+
margin: 1rem 0;
170+
}
171+
}
172+
</style>
173+
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
---
2+
interface Props {
3+
videoId: string;
4+
title: string;
5+
autoplay?: boolean;
6+
startAt?: number;
7+
showControls?: boolean;
8+
showInfo?: boolean;
9+
allowFullscreen?: boolean;
10+
muted?: boolean;
11+
rel?: boolean;
12+
width?: number | string;
13+
height?: number | string;
14+
}
15+
16+
const {
17+
videoId,
18+
title,
19+
autoplay = false,
20+
startAt = 0,
21+
showControls = true,
22+
showInfo = true,
23+
allowFullscreen = true,
24+
muted = false,
25+
rel = false,
26+
width,
27+
height,
28+
} = Astro.props;
29+
30+
// Build YouTube embed URL with parameters
31+
let embedUrl = `https://www.youtube.com/embed/${videoId}?`;
32+
const params = [
33+
autoplay ? 'autoplay=1' : '',
34+
muted ? 'mute=1' : '',
35+
startAt > 0 ? `start=${startAt}` : '',
36+
!showControls ? 'controls=0' : '',
37+
!showInfo ? 'showinfo=0' : '',
38+
!rel ? 'rel=0' : '',
39+
'origin=' + encodeURIComponent(typeof window !== 'undefined' ? window.location.origin : 'https://defguard.net'),
40+
'enablejsapi=1',
41+
];
42+
43+
// Filter out empty params and join with &
44+
embedUrl += params.filter(param => param !== '').join('&');
45+
46+
// Calculate aspect ratio for responsive container (16:9 is YouTube default)
47+
const aspectRatio = '56.25%'; // 9/16 * 100%
48+
---
49+
50+
<div class="youtube-container" style={`padding-bottom: ${aspectRatio};`}>
51+
<iframe
52+
src={embedUrl}
53+
title={title}
54+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
55+
allowfullscreen={allowFullscreen}
56+
class="youtube-iframe"
57+
loading="lazy"
58+
referrerpolicy="strict-origin-when-cross-origin"
59+
></iframe>
60+
</div>
61+
62+
<style>
63+
.youtube-container {
64+
position: relative;
65+
width: 100%;
66+
height: 0;
67+
overflow: hidden;
68+
border-radius: 6px;
69+
}
70+
71+
.youtube-iframe {
72+
position: absolute;
73+
top: 0;
74+
left: 0;
75+
width: 100%;
76+
height: 100%;
77+
border: 0;
78+
border-radius: 6px;
79+
}
80+
</style>
81+

0 commit comments

Comments
 (0)