@rimelight/ui 0.0.25 → 0.0.27
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 +9 -7
- package/src/components/ThemeToggle.astro +2 -2
- package/src/components/astro/RLAButton.astro +104 -104
- package/src/components/astro/RLAEmbedYoutube.astro +188 -0
- package/src/components/astro/RLAFooter.astro +8 -8
- package/src/components/astro/RLAHeader.astro +8 -7
- package/src/components/astro/RLAIcon.astro +5 -5
- package/src/components/astro/RLALogo.astro +6 -5
- package/src/components/astro/RLAPlaceholder.astro +4 -3
- package/src/components/astro/RLAScrollToTop.astro +3 -3
- package/src/components/astro/RLASection.astro +210 -181
- package/src/components/vue/RLVButton.vue +7 -13
- package/src/components/vue/RLVFooter.vue +8 -8
- package/src/components/vue/RLVHeader.vue +9 -9
- package/src/components/vue/RLVIcon.vue +5 -10
- package/src/components/vue/RLVLogo.vue +4 -3
- package/src/components/vue/RLVPlaceholder.vue +4 -3
- package/src/components/vue/RLVScrollToTop.vue +3 -3
- package/src/components/vue/RLVSection.vue +102 -34
- package/src/themes/button.theme.ts +10 -10
- package/src/themes/section.theme.ts +37 -35
- package/src/types/componentVariant.ts +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimelight/ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Rimelight Entertainment UI Library.",
|
|
6
6
|
"keywords": [
|
|
@@ -56,6 +56,12 @@
|
|
|
56
56
|
"publishConfig": {
|
|
57
57
|
"access": "public"
|
|
58
58
|
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"prepare": "vp config",
|
|
61
|
+
"knip": "knip",
|
|
62
|
+
"astro": "astro",
|
|
63
|
+
"docs:meta": "tsx scripts/extract-meta.ts"
|
|
64
|
+
},
|
|
59
65
|
"dependencies": {
|
|
60
66
|
"@astrojs/prism": "^4.0.1",
|
|
61
67
|
"@astrojs/vue": "6.0.1",
|
|
@@ -97,9 +103,5 @@
|
|
|
97
103
|
"optional": true
|
|
98
104
|
}
|
|
99
105
|
},
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
"astro": "astro",
|
|
103
|
-
"docs:meta": "tsx scripts/extract-meta.ts"
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
+
"packageManager": "pnpm@11.1.2"
|
|
107
|
+
}
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import RLAButton from "./astro/RLAButton.astro";
|
|
3
3
|
|
|
4
4
|
export interface ThemeToggleProps {
|
|
5
|
-
class?: string;
|
|
6
5
|
size?: "sm" | "md" | "lg";
|
|
6
|
+
class?: any;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
type Props = ThemeToggleProps;
|
|
10
10
|
|
|
11
|
-
const {
|
|
11
|
+
const { size = "md", class: className } = Astro.props as Props
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
<RLAButton
|
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { scv, cx } from "css-variants"
|
|
3
|
-
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
-
import type { ComponentVariants } from "../../types"
|
|
5
|
-
import buttonTheme from "../../themes/button.theme"
|
|
6
|
-
|
|
7
|
-
const button = scv(buttonTheme)
|
|
8
|
-
type ButtonVariants = ComponentVariants<typeof buttonTheme>
|
|
9
|
-
|
|
10
|
-
export interface RLAButtonProps {
|
|
11
|
-
variant?: ButtonVariants['variant'];
|
|
12
|
-
color?: ButtonVariants['color'];
|
|
13
|
-
size?: ButtonVariants['size'];
|
|
14
|
-
href?: string;
|
|
15
|
-
label?: string;
|
|
16
|
-
leadingIcon?: string;
|
|
17
|
-
trailingIcon?: string;
|
|
18
|
-
loading?: boolean;
|
|
19
|
-
square?: boolean;
|
|
20
|
-
block?: boolean;
|
|
21
|
-
active?: boolean;
|
|
22
|
-
activeColor?: ButtonVariants['color'];
|
|
23
|
-
activeVariant?: ButtonVariants['variant'];
|
|
24
|
-
ui?:
|
|
25
|
-
class?:
|
|
26
|
-
[key: string]: unknown;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const {
|
|
30
|
-
variant = "solid",
|
|
31
|
-
color = "primary",
|
|
32
|
-
size = "md",
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
...rest
|
|
46
|
-
} = Astro.props as RLAButtonProps
|
|
47
|
-
|
|
48
|
-
const isLeading = !!(leadingIcon || Astro.slots.has("leading")) || loading
|
|
49
|
-
const isTrailing = !!(trailingIcon || Astro.slots.has("trailing")) || (loading && !isLeading)
|
|
50
|
-
|
|
51
|
-
const showLeadingIcon = loading
|
|
52
|
-
? "i-lucide-loader-circle"
|
|
53
|
-
: leadingIcon
|
|
54
|
-
|
|
55
|
-
const showTrailingIcon = loading
|
|
56
|
-
? "i-lucide-loader-circle"
|
|
57
|
-
: trailingIcon
|
|
58
|
-
|
|
59
|
-
const leadingIconClass = loading ? "animate-spin" : ""
|
|
60
|
-
const trailingIconClass = loading ? "animate-spin" : ""
|
|
61
|
-
|
|
62
|
-
const classes = resolveClasses(button, {
|
|
63
|
-
variant: active && activeVariant ? activeVariant : variant,
|
|
64
|
-
color: active && activeColor ? activeColor : color,
|
|
65
|
-
size,
|
|
66
|
-
block,
|
|
67
|
-
square: square || (!label && !Astro.slots.has("default")),
|
|
68
|
-
leading: isLeading,
|
|
69
|
-
trailing: isTrailing,
|
|
70
|
-
loading,
|
|
71
|
-
active
|
|
72
|
-
}, useUi("button", uiProp), className);
|
|
73
|
-
|
|
74
|
-
const Tag = href ? "a" : "button";
|
|
75
|
-
---
|
|
76
|
-
<Tag
|
|
77
|
-
class={classes.root}
|
|
78
|
-
data-slot="
|
|
79
|
-
{...(Tag === "a" ? { href } : {})}
|
|
80
|
-
{...rest}
|
|
81
|
-
>
|
|
82
|
-
<slot name="leading" {...{ ui: classes }}>
|
|
83
|
-
{isLeading && (
|
|
84
|
-
<span class={classes.leadingIcon} data-slot="leading-icon">
|
|
85
|
-
<span class={cx(showLeadingIcon, leadingIconClass)} aria-hidden="true" />
|
|
86
|
-
</span>
|
|
87
|
-
)}
|
|
88
|
-
</slot>
|
|
89
|
-
|
|
90
|
-
{label !== undefined && label !== null && (
|
|
91
|
-
<span class={classes.label} data-slot="label">
|
|
92
|
-
{label}
|
|
93
|
-
</span>
|
|
94
|
-
)}
|
|
95
|
-
{!label && <slot />}
|
|
96
|
-
|
|
97
|
-
<slot name="trailing" {...{ ui: classes }}>
|
|
98
|
-
{isTrailing && (
|
|
99
|
-
<span class={classes.trailingIcon} data-slot="trailing-icon">
|
|
100
|
-
<span class={cx(showTrailingIcon, trailingIconClass)} aria-hidden="true" />
|
|
101
|
-
</span>
|
|
102
|
-
)}
|
|
103
|
-
</slot>
|
|
104
|
-
</Tag>
|
|
1
|
+
---
|
|
2
|
+
import { scv, cx } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { ComponentVariants, ComponentSlots } from "../../types"
|
|
5
|
+
import buttonTheme from "../../themes/button.theme"
|
|
6
|
+
|
|
7
|
+
const button = scv(buttonTheme)
|
|
8
|
+
type ButtonVariants = ComponentVariants<typeof buttonTheme>
|
|
9
|
+
|
|
10
|
+
export interface RLAButtonProps {
|
|
11
|
+
variant?: ButtonVariants['variant'];
|
|
12
|
+
color?: ButtonVariants['color'];
|
|
13
|
+
size?: ButtonVariants['size'];
|
|
14
|
+
href?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
leadingIcon?: string;
|
|
17
|
+
trailingIcon?: string;
|
|
18
|
+
loading?: boolean;
|
|
19
|
+
square?: boolean;
|
|
20
|
+
block?: boolean;
|
|
21
|
+
active?: boolean;
|
|
22
|
+
activeColor?: ButtonVariants['color'];
|
|
23
|
+
activeVariant?: ButtonVariants['variant'];
|
|
24
|
+
ui?: ComponentSlots<typeof buttonTheme>;
|
|
25
|
+
class?: any;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
variant = "solid",
|
|
31
|
+
color = "primary",
|
|
32
|
+
size = "md",
|
|
33
|
+
href,
|
|
34
|
+
label,
|
|
35
|
+
leadingIcon,
|
|
36
|
+
trailingIcon,
|
|
37
|
+
loading = false,
|
|
38
|
+
square = false,
|
|
39
|
+
block = false,
|
|
40
|
+
active = false,
|
|
41
|
+
activeColor,
|
|
42
|
+
activeVariant,
|
|
43
|
+
ui: uiProp,
|
|
44
|
+
class: className,
|
|
45
|
+
...rest
|
|
46
|
+
} = Astro.props as RLAButtonProps
|
|
47
|
+
|
|
48
|
+
const isLeading = !!(leadingIcon || Astro.slots.has("leading")) || loading
|
|
49
|
+
const isTrailing = !!(trailingIcon || Astro.slots.has("trailing")) || (loading && !isLeading)
|
|
50
|
+
|
|
51
|
+
const showLeadingIcon = loading
|
|
52
|
+
? "i-lucide-loader-circle"
|
|
53
|
+
: leadingIcon
|
|
54
|
+
|
|
55
|
+
const showTrailingIcon = loading
|
|
56
|
+
? "i-lucide-loader-circle"
|
|
57
|
+
: trailingIcon
|
|
58
|
+
|
|
59
|
+
const leadingIconClass = loading ? "animate-spin" : ""
|
|
60
|
+
const trailingIconClass = loading ? "animate-spin" : ""
|
|
61
|
+
|
|
62
|
+
const classes = resolveClasses(button, {
|
|
63
|
+
variant: active && activeVariant ? activeVariant : variant,
|
|
64
|
+
color: active && activeColor ? activeColor : color,
|
|
65
|
+
size,
|
|
66
|
+
block,
|
|
67
|
+
square: square || (!label && !Astro.slots.has("default")),
|
|
68
|
+
leading: isLeading,
|
|
69
|
+
trailing: isTrailing,
|
|
70
|
+
loading,
|
|
71
|
+
active
|
|
72
|
+
}, useUi("button", uiProp), className);
|
|
73
|
+
|
|
74
|
+
const Tag = href ? "a" : "button";
|
|
75
|
+
---
|
|
76
|
+
<Tag
|
|
77
|
+
class={classes.root}
|
|
78
|
+
data-slot="root"
|
|
79
|
+
{...(Tag === "a" ? { href } : {})}
|
|
80
|
+
{...rest}
|
|
81
|
+
>
|
|
82
|
+
<slot name="leading" {...{ ui: classes }}>
|
|
83
|
+
{isLeading && (
|
|
84
|
+
<span class={classes.leadingIcon} data-slot="leading-icon">
|
|
85
|
+
<span class={cx(showLeadingIcon, leadingIconClass)} aria-hidden="true" />
|
|
86
|
+
</span>
|
|
87
|
+
)}
|
|
88
|
+
</slot>
|
|
89
|
+
|
|
90
|
+
{label !== undefined && label !== null && (
|
|
91
|
+
<span class={classes.label} data-slot="label">
|
|
92
|
+
{label}
|
|
93
|
+
</span>
|
|
94
|
+
)}
|
|
95
|
+
{!label && <slot />}
|
|
96
|
+
|
|
97
|
+
<slot name="trailing" {...{ ui: classes }}>
|
|
98
|
+
{isTrailing && (
|
|
99
|
+
<span class={classes.trailingIcon} data-slot="trailing-icon">
|
|
100
|
+
<span class={cx(showTrailingIcon, trailingIconClass)} aria-hidden="true" />
|
|
101
|
+
</span>
|
|
102
|
+
)}
|
|
103
|
+
</slot>
|
|
104
|
+
</Tag>
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
const PLACEHOLDER_THUMBNAIL = "/assets/blog-placeholder-1.jpg";
|
|
3
|
+
|
|
4
|
+
const YOUTUBE_EMBED_BASE = "https://www.youtube.com/embed/";
|
|
5
|
+
|
|
6
|
+
const VALID_ID_PATTERN = /^[\w-]{11}$/;
|
|
7
|
+
|
|
8
|
+
const KNOWN_PREFIXES = ["www.", "m.", "music.", "gaming."];
|
|
9
|
+
|
|
10
|
+
const YOUTUBE_THUMB_BASE = "https://i3.ytimg.com/vi/";
|
|
11
|
+
|
|
12
|
+
const MAXRES_THUMBNAIL = "maxresdefault.jpg";
|
|
13
|
+
|
|
14
|
+
const DEFAULT_THUMBNAIL = "hqdefault.jpg";
|
|
15
|
+
|
|
16
|
+
const thumbnailAvailabilityCache = new Map<string, boolean>();
|
|
17
|
+
|
|
18
|
+
function normalizeHost(host: string): string {
|
|
19
|
+
for (const prefix of KNOWN_PREFIXES) {
|
|
20
|
+
if (host.startsWith(prefix)) {
|
|
21
|
+
return host.slice(prefix.length);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return host;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function sanitizeVideoId(id: string | null): string | null {
|
|
28
|
+
if (!id) return null;
|
|
29
|
+
return VALID_ID_PATTERN.test(id) ? id : null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function extractYoutubeVideoId(rawUrl: string): string | null {
|
|
33
|
+
if (!rawUrl) return null;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const url = new URL(rawUrl);
|
|
37
|
+
const host = normalizeHost(url.hostname);
|
|
38
|
+
|
|
39
|
+
if (host === "youtu.be") {
|
|
40
|
+
const id = url.pathname.split("/").filter(Boolean)[0] ?? null;
|
|
41
|
+
return sanitizeVideoId(id);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (host.endsWith("youtube.com")) {
|
|
45
|
+
const paramsId = sanitizeVideoId(url.searchParams.get("v"));
|
|
46
|
+
if (paramsId) return paramsId;
|
|
47
|
+
|
|
48
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
49
|
+
if (!segments.length) return null;
|
|
50
|
+
|
|
51
|
+
if (segments[0] === "embed" || segments[0] === "shorts" || segments[0] === "live") {
|
|
52
|
+
return sanitizeVideoId(segments[1] ?? null);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return sanitizeVideoId(segments[segments.length - 1] ?? null);
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
// If it's just an 11-character ID passed directly, return it validated
|
|
59
|
+
if (VALID_ID_PATTERN.test(rawUrl)) {
|
|
60
|
+
return rawUrl;
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function isThumbnailAvailable(url: string): Promise<boolean> {
|
|
69
|
+
if (thumbnailAvailabilityCache.has(url)) {
|
|
70
|
+
return thumbnailAvailabilityCache.get(url)!;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeof fetch !== "function") {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetch(url, { method: "HEAD" });
|
|
79
|
+
let available = response.ok;
|
|
80
|
+
|
|
81
|
+
if (!available && response.status === 405) {
|
|
82
|
+
const getResponse = await fetch(url, { method: "GET" });
|
|
83
|
+
available = getResponse.ok;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
thumbnailAvailabilityCache.set(url, available);
|
|
87
|
+
return available;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function getYoutubeThumbnail(rawUrl: string): Promise<string> {
|
|
94
|
+
const id = extractYoutubeVideoId(rawUrl);
|
|
95
|
+
if (!id) return PLACEHOLDER_THUMBNAIL;
|
|
96
|
+
|
|
97
|
+
const baseUrl = `${YOUTUBE_THUMB_BASE}${id}`;
|
|
98
|
+
const maxResUrl = `${baseUrl}/${MAXRES_THUMBNAIL}`;
|
|
99
|
+
|
|
100
|
+
if (await isThumbnailAvailable(maxResUrl)) {
|
|
101
|
+
return maxResUrl;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return `${baseUrl}/${DEFAULT_THUMBNAIL}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getYoutubeEmbedUrl(rawUrl: string): string | null {
|
|
108
|
+
const id = extractYoutubeVideoId(rawUrl);
|
|
109
|
+
return id ? `${YOUTUBE_EMBED_BASE}${id}?autoplay=1` : null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface Props {
|
|
113
|
+
src: string;
|
|
114
|
+
title?: string;
|
|
115
|
+
class?: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const { src, title = "YouTube video player", class: className } = Astro.props;
|
|
119
|
+
|
|
120
|
+
const embedUrl = getYoutubeEmbedUrl(src);
|
|
121
|
+
const thumbnail = await getYoutubeThumbnail(src);
|
|
122
|
+
const isPlayable = Boolean(embedUrl);
|
|
123
|
+
const ariaLabel = isPlayable ? "Play YouTube video" : "YouTube video unavailable";
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
<div class:list={["relative aspect-video h-full w-full cursor-pointer overflow-hidden", className]} data-youtube-video>
|
|
127
|
+
<button
|
|
128
|
+
type="button"
|
|
129
|
+
class="absolute inset-0 aspect-video h-full w-full overflow-hidden group"
|
|
130
|
+
data-youtube-trigger
|
|
131
|
+
aria-label={ariaLabel}
|
|
132
|
+
data-embed-url={isPlayable ? embedUrl : undefined}
|
|
133
|
+
disabled={!isPlayable}
|
|
134
|
+
data-video-title={title}
|
|
135
|
+
>
|
|
136
|
+
<img
|
|
137
|
+
src={thumbnail}
|
|
138
|
+
class="h-full w-full object-cover"
|
|
139
|
+
alt={title}
|
|
140
|
+
width="1280"
|
|
141
|
+
height="720"
|
|
142
|
+
loading="lazy"
|
|
143
|
+
/>
|
|
144
|
+
|
|
145
|
+
<span class="pointer-events-none absolute inset-0 flex items-center justify-center bg-black/10 group-hover:bg-black/30 transition-colors">
|
|
146
|
+
<svg class="h-20 w-20 text-white opacity-85 transition-all transform group-hover:opacity-100 group-hover:scale-110 duration-300" viewBox="0 0 24 24" fill="currentColor">
|
|
147
|
+
<path d="M8 5v14l11-7z"/>
|
|
148
|
+
</svg>
|
|
149
|
+
</span>
|
|
150
|
+
</button>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<script is:inline>
|
|
154
|
+
(() => {
|
|
155
|
+
const root = document.currentScript?.previousElementSibling;
|
|
156
|
+
if (!(root instanceof HTMLElement)) return;
|
|
157
|
+
|
|
158
|
+
const trigger = root.querySelector("[data-youtube-trigger]");
|
|
159
|
+
if (!(trigger instanceof HTMLButtonElement)) return;
|
|
160
|
+
|
|
161
|
+
const embedUrl = trigger.dataset.embedUrl;
|
|
162
|
+
const title = trigger.dataset.videoTitle;
|
|
163
|
+
if (!embedUrl) return;
|
|
164
|
+
|
|
165
|
+
const activate = () => {
|
|
166
|
+
const iframe = document.createElement("iframe");
|
|
167
|
+
iframe.src = embedUrl;
|
|
168
|
+
iframe.title = title;
|
|
169
|
+
iframe.allow =
|
|
170
|
+
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share";
|
|
171
|
+
iframe.allowFullscreen = true;
|
|
172
|
+
iframe.loading = "lazy";
|
|
173
|
+
iframe.referrerPolicy = "strict-origin-when-cross-origin";
|
|
174
|
+
iframe.classList.add("w-full", "h-full", "absolute", "inset-0");
|
|
175
|
+
|
|
176
|
+
root.replaceChildren(iframe);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// click and accessibility activation
|
|
180
|
+
trigger.addEventListener("click", activate, { once: true });
|
|
181
|
+
trigger.addEventListener("keydown", (event) => {
|
|
182
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
activate();
|
|
185
|
+
}
|
|
186
|
+
}, { once: true });
|
|
187
|
+
})();
|
|
188
|
+
</script>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { scv } from "css-variants"
|
|
3
3
|
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
-
import type { ComponentVariants } from "../../types"
|
|
4
|
+
import type { ComponentVariants, ComponentSlots } from "../../types"
|
|
5
5
|
import footerTheme from "../../themes/footer.theme"
|
|
6
6
|
|
|
7
7
|
const footer = scv(footerTheme)
|
|
@@ -9,15 +9,15 @@ type FooterVariants = ComponentVariants<typeof footerTheme>
|
|
|
9
9
|
|
|
10
10
|
export interface RLAFooterProps {
|
|
11
11
|
contain?: FooterVariants['contain'];
|
|
12
|
-
ui?:
|
|
13
|
-
class?:
|
|
12
|
+
ui?: ComponentSlots<typeof footerTheme>;
|
|
13
|
+
class?: any;
|
|
14
14
|
[key: string]: unknown;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const {
|
|
18
18
|
contain = false,
|
|
19
|
-
class: className,
|
|
20
19
|
ui: uiProp,
|
|
20
|
+
class: className,
|
|
21
21
|
...rest
|
|
22
22
|
} = Astro.props as RLAFooterProps
|
|
23
23
|
|
|
@@ -26,17 +26,17 @@ const classes = resolveClasses(footer, { contain }, useUi("footer", uiProp), cla
|
|
|
26
26
|
|
|
27
27
|
<footer
|
|
28
28
|
class:list={[classes.root]}
|
|
29
|
-
data-slot="
|
|
29
|
+
data-slot="root"
|
|
30
30
|
{...rest}
|
|
31
31
|
>
|
|
32
32
|
<div class:list={[classes.container]}>
|
|
33
|
-
<div class:list={[classes.left]} data-slot="
|
|
33
|
+
<div class:list={[classes.left]} data-slot="left">
|
|
34
34
|
<slot name="left" />
|
|
35
35
|
</div>
|
|
36
|
-
<div class:list={[classes.center]} data-slot="
|
|
36
|
+
<div class:list={[classes.center]} data-slot="center">
|
|
37
37
|
<slot name="center" />
|
|
38
38
|
</div>
|
|
39
|
-
<div class:list={[classes.right]} data-slot="
|
|
39
|
+
<div class:list={[classes.right]} data-slot="right">
|
|
40
40
|
<slot name="right" />
|
|
41
41
|
</div>
|
|
42
42
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { scv } from "css-variants"
|
|
3
3
|
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
-
import type { ComponentVariants } from "../../types"
|
|
4
|
+
import type { ComponentVariants, ComponentSlots } from "../../types"
|
|
5
5
|
import headerTheme from "../../themes/header.theme"
|
|
6
6
|
|
|
7
7
|
const header = scv(headerTheme)
|
|
@@ -13,8 +13,8 @@ export interface RLAHeaderProps {
|
|
|
13
13
|
fixed?: HeaderVariants['fixed'];
|
|
14
14
|
stackIndex?: number;
|
|
15
15
|
hideOnScroll?: boolean;
|
|
16
|
-
ui?:
|
|
17
|
-
class?:
|
|
16
|
+
ui?: ComponentSlots<typeof headerTheme>;
|
|
17
|
+
class?: any;
|
|
18
18
|
[key: string]: unknown;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -24,8 +24,8 @@ const {
|
|
|
24
24
|
fixed = false,
|
|
25
25
|
stackIndex = 0,
|
|
26
26
|
hideOnScroll = false,
|
|
27
|
-
class: className,
|
|
28
27
|
ui: uiProp,
|
|
28
|
+
class: className,
|
|
29
29
|
...rest
|
|
30
30
|
} = Astro.props as RLAHeaderProps;
|
|
31
31
|
|
|
@@ -41,6 +41,7 @@ const zIndex = 100 - stackIndex;
|
|
|
41
41
|
|
|
42
42
|
<header
|
|
43
43
|
id={headerId}
|
|
44
|
+
data-slot="root"
|
|
44
45
|
class:list={[classes.root]}
|
|
45
46
|
data-rla-header={fixed ? "true" : undefined}
|
|
46
47
|
data-stack-index={stackIndex}
|
|
@@ -50,13 +51,13 @@ const zIndex = 100 - stackIndex;
|
|
|
50
51
|
>
|
|
51
52
|
<div class:list={[classes.content]} data-header-content>
|
|
52
53
|
<div class:list={[classes.container]}>
|
|
53
|
-
<div class:list={[classes.left]} data-slot="
|
|
54
|
+
<div class:list={[classes.left]} data-slot="left">
|
|
54
55
|
<slot name="left" />
|
|
55
56
|
</div>
|
|
56
|
-
<div class:list={[classes.center]} data-slot="
|
|
57
|
+
<div class:list={[classes.center]} data-slot="center">
|
|
57
58
|
<slot name="center" />
|
|
58
59
|
</div>
|
|
59
|
-
<div class:list={[classes.right]} data-slot="
|
|
60
|
+
<div class:list={[classes.right]} data-slot="right">
|
|
60
61
|
<slot name="right" />
|
|
61
62
|
</div>
|
|
62
63
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { scv } from "css-variants"
|
|
3
3
|
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
-
import type { ComponentVariants } from "../../types"
|
|
4
|
+
import type { ComponentVariants, ComponentSlots } from "../../types"
|
|
5
5
|
import iconTheme from "../../themes/icon.theme"
|
|
6
6
|
|
|
7
7
|
const icon = scv(iconTheme)
|
|
@@ -10,16 +10,16 @@ type IconVariants = ComponentVariants<typeof iconTheme>
|
|
|
10
10
|
export interface RLAIconProps {
|
|
11
11
|
name: string
|
|
12
12
|
size?: IconVariants['size']
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
ui?: ComponentSlots<typeof iconTheme>
|
|
14
|
+
class?: any
|
|
15
15
|
[key: string]: unknown
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const {
|
|
19
19
|
name,
|
|
20
20
|
size = "md",
|
|
21
|
-
class: className,
|
|
22
21
|
ui: uiProp,
|
|
22
|
+
class: className,
|
|
23
23
|
...rest
|
|
24
24
|
} = Astro.props as RLAIconProps
|
|
25
25
|
|
|
@@ -28,7 +28,7 @@ const classes = resolveClasses(icon, { size }, useUi("icon", uiProp), className)
|
|
|
28
28
|
|
|
29
29
|
<span
|
|
30
30
|
class={classes.root}
|
|
31
|
-
data-slot="
|
|
31
|
+
data-slot="root"
|
|
32
32
|
aria-hidden="true"
|
|
33
33
|
{...rest}
|
|
34
34
|
>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { scv } from "css-variants"
|
|
3
3
|
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { ComponentSlots } from "../../types"
|
|
4
5
|
import logoTheme from "../../themes/logo.theme"
|
|
5
6
|
import { getUIConfig } from "virtual:rimelight-ui"
|
|
6
7
|
|
|
@@ -10,9 +11,9 @@ export interface RLALogoProps {
|
|
|
10
11
|
variant?: string;
|
|
11
12
|
mode?: "color" | "white" | "black";
|
|
12
13
|
href?: string;
|
|
13
|
-
ui?: Partial<Record<string, string>>;
|
|
14
|
-
class?: string;
|
|
15
14
|
alt?: string;
|
|
15
|
+
ui?: ComponentSlots<typeof logoTheme>;
|
|
16
|
+
class?: any;
|
|
16
17
|
[key: string]: unknown;
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -20,9 +21,9 @@ const {
|
|
|
20
21
|
variant = "logomark",
|
|
21
22
|
mode,
|
|
22
23
|
href = "/",
|
|
23
|
-
class: className,
|
|
24
|
-
ui: uiProp,
|
|
25
24
|
alt,
|
|
25
|
+
ui: uiProp,
|
|
26
|
+
class: className,
|
|
26
27
|
...rest
|
|
27
28
|
} = Astro.props as RLALogoProps;
|
|
28
29
|
|
|
@@ -50,7 +51,7 @@ const isIcon = logoSrc && (logoSrc.includes(":") || logoSrc.startsWith("i-"));
|
|
|
50
51
|
class={classes.root}
|
|
51
52
|
href={href}
|
|
52
53
|
aria-label={alt || variant}
|
|
53
|
-
data-slot="
|
|
54
|
+
data-slot="root"
|
|
54
55
|
{...rest}
|
|
55
56
|
>
|
|
56
57
|
{logoSrc ? (
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { scv } from "css-variants"
|
|
3
3
|
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { ComponentSlots } from "../../types"
|
|
4
5
|
import placeholderTheme from "../../themes/placeholder.theme"
|
|
5
6
|
|
|
6
7
|
const placeholder = scv(placeholderTheme)
|
|
7
8
|
|
|
8
9
|
export interface RLAPlaceholderProps {
|
|
9
|
-
ui?:
|
|
10
|
-
class?:
|
|
10
|
+
ui?: ComponentSlots<typeof placeholderTheme>
|
|
11
|
+
class?: any
|
|
11
12
|
[key: string]: unknown
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const {
|
|
15
|
-
class: className,
|
|
16
16
|
ui: uiProp,
|
|
17
|
+
class: className,
|
|
17
18
|
...rest
|
|
18
19
|
} = Astro.props as RLAPlaceholderProps
|
|
19
20
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { scv } from "css-variants"
|
|
3
3
|
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
-
import type { ComponentVariants } from "../../types"
|
|
4
|
+
import type { ComponentVariants, ComponentSlots } from "../../types"
|
|
5
5
|
import scrollToTopTheme from "../../themes/scroll-to-top.theme"
|
|
6
6
|
|
|
7
7
|
const scrollToTop = scv(scrollToTopTheme)
|
|
@@ -13,8 +13,8 @@ export interface RLAScrollToTopProps {
|
|
|
13
13
|
duration?: number;
|
|
14
14
|
threshold?: number;
|
|
15
15
|
showProgress?: boolean;
|
|
16
|
-
ui?:
|
|
17
|
-
class?:
|
|
16
|
+
ui?: ComponentSlots<typeof scrollToTopTheme>;
|
|
17
|
+
class?: any;
|
|
18
18
|
[key: string]: unknown;
|
|
19
19
|
}
|
|
20
20
|
|