@rimelight/ui 0.0.6 → 0.0.8
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/LICENSE +21 -21
- package/package.json +9 -8
- package/src/components/Head.astro +153 -150
- package/src/components/astro/RLAHeader.astro +75 -75
- package/src/composables/useUi.ts +2 -2
- package/src/env.d.ts +13 -16
- package/src/integrations/ui.ts +21 -10
- package/src/integrations/vue-entrypoint.ts +6 -0
- package/src/middleware/sri.ts +2 -2
- package/src/nuxt.ts +125 -0
- package/src/styles/index.css +2 -0
- package/src/utils/index.ts +0 -2
- package/src/components/BlogPost.astro +0 -181
- package/src/components/DynamicShowcase.astro +0 -681
- package/src/components/ExtendedCarousel.astro +0 -107
- package/src/components/FormattedDate.astro +0 -22
- package/src/components/HeaderLink.astro +0 -24
- package/src/components/LanguageSwitcher.astro +0 -128
- package/src/components/MissingTranslationBanner.astro +0 -39
- package/src/components/ProjectPost.astro +0 -183
- package/src/components/RLHead.astro +0 -3
- package/src/utils/deep-merge.ts +0 -36
- package/src/utils/parse-boolean.ts +0 -14
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Daniel Marchi
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Marchi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimelight/ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Rimelight Entertainment UI Library.",
|
|
6
6
|
"homepage": "https://rimelight.com/docs",
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"src",
|
|
17
|
-
"i18n-data.json",
|
|
18
17
|
"worker-configuration.d.ts",
|
|
19
18
|
"README.md",
|
|
20
19
|
"LICENSE"
|
|
@@ -22,6 +21,7 @@
|
|
|
22
21
|
"type": "module",
|
|
23
22
|
"exports": {
|
|
24
23
|
".": "./src/integrations/index.ts",
|
|
24
|
+
"./styles/*": "./src/styles/*",
|
|
25
25
|
"./components/*": "./src/components/*",
|
|
26
26
|
"./components/astro/*": "./src/components/astro/*",
|
|
27
27
|
"./components/vue/*": "./src/components/vue/*",
|
|
@@ -31,27 +31,29 @@
|
|
|
31
31
|
"./composables/*": "./src/composables/*",
|
|
32
32
|
"./config": "./src/config/index.ts",
|
|
33
33
|
"./config/*": "./src/config/*",
|
|
34
|
-
"./domain": "./src/domain/index.ts",
|
|
35
|
-
"./domain/*": "./src/domain/*",
|
|
36
|
-
"./lib": "./src/lib/index.ts",
|
|
37
|
-
"./lib/*": "./src/lib/*",
|
|
38
34
|
"./utils": "./src/utils/index.ts",
|
|
39
35
|
"./utils/*": "./src/utils/*",
|
|
40
36
|
"./integrations": "./src/integrations/index.ts",
|
|
37
|
+
"./integrations/vue-entrypoint": "./src/integrations/vue-entrypoint.ts",
|
|
41
38
|
"./integrations/*": "./src/integrations/*",
|
|
42
39
|
"./middleware": "./src/middleware/index.ts",
|
|
43
40
|
"./middleware/*": "./src/middleware/*",
|
|
41
|
+
"./nuxt": "./src/nuxt.ts",
|
|
44
42
|
"./*": "./src/*"
|
|
45
43
|
},
|
|
46
44
|
"publishConfig": {
|
|
47
45
|
"access": "public"
|
|
48
46
|
},
|
|
49
47
|
"dependencies": {
|
|
48
|
+
"@astrojs/vue": "6.0.1",
|
|
50
49
|
"@iconify-json/lucide": "^1.2.102",
|
|
50
|
+
"@nuxt/ui": "4.6.1",
|
|
51
|
+
"@tailwindcss/vite": "^4.2.3",
|
|
51
52
|
"css-variants": "2.3.5",
|
|
52
53
|
"defu": "6.1.6",
|
|
53
54
|
"embla-carousel": "8.6.0",
|
|
54
55
|
"tailwind-merge": "^3.3.1",
|
|
56
|
+
"tailwindcss": "^4.2.3",
|
|
55
57
|
"unplugin-icons": "23.0.1"
|
|
56
58
|
},
|
|
57
59
|
"devDependencies": {
|
|
@@ -62,9 +64,8 @@
|
|
|
62
64
|
"@unocss/eslint-plugin": "66.6.8",
|
|
63
65
|
"astro": "6.1.4",
|
|
64
66
|
"eslint-plugin-regexp": "3.1.0",
|
|
65
|
-
"typescript": "6.0.2",
|
|
66
67
|
"unocss": "66.6.8",
|
|
67
|
-
"vite-plus": "0.1.
|
|
68
|
+
"vite-plus": "0.1.18"
|
|
68
69
|
},
|
|
69
70
|
"peerDependencies": {
|
|
70
71
|
"astro": ">=6.0.0",
|
|
@@ -1,150 +1,153 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { ClientRouter } from 'astro:transitions';
|
|
3
|
-
import type { SiteConfig } from '@/config';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
title
|
|
8
|
-
|
|
9
|
-
description
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
charset
|
|
28
|
-
|
|
29
|
-
viewport
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<meta
|
|
82
|
-
<meta name="
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
{
|
|
88
|
-
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<meta property="og:
|
|
98
|
-
<meta property="og:
|
|
99
|
-
|
|
100
|
-
{
|
|
101
|
-
<meta property="og:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
<meta property="twitter:
|
|
106
|
-
<meta property="twitter:
|
|
107
|
-
|
|
108
|
-
{
|
|
109
|
-
{
|
|
110
|
-
{twitterHandle && <meta property="twitter:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
1
|
+
---
|
|
2
|
+
import { ClientRouter } from 'astro:transitions';
|
|
3
|
+
import type { SiteConfig } from '@/config';
|
|
4
|
+
import { App } from '../nuxt.ts';
|
|
5
|
+
|
|
6
|
+
export interface HeadProps {
|
|
7
|
+
/** Page title */
|
|
8
|
+
title?: string;
|
|
9
|
+
/** Page description */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** Open Graph image URL */
|
|
12
|
+
ogImage?: string;
|
|
13
|
+
/** Whether to prevent indexing */
|
|
14
|
+
noindex?: boolean;
|
|
15
|
+
/** Whether this is a 404 page */
|
|
16
|
+
is404?: boolean;
|
|
17
|
+
/** Robots metadata (overrides noindex/is404 logic if provided) */
|
|
18
|
+
robots?: string;
|
|
19
|
+
/** Canonical URL */
|
|
20
|
+
canonical?: string;
|
|
21
|
+
/** Language alternates for i18n */
|
|
22
|
+
languageAlternates?: Array<{ hreflang: string; href: string }>;
|
|
23
|
+
/** Site configuration for defaults */
|
|
24
|
+
config?: SiteConfig;
|
|
25
|
+
/** Whether to enable View Transitions (ClientRouter) */
|
|
26
|
+
transitions?: boolean;
|
|
27
|
+
/** Meta charset */
|
|
28
|
+
charset?: string;
|
|
29
|
+
/** Meta viewport */
|
|
30
|
+
viewport?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const {
|
|
34
|
+
title,
|
|
35
|
+
description,
|
|
36
|
+
ogImage,
|
|
37
|
+
noindex = false,
|
|
38
|
+
is404 = false,
|
|
39
|
+
robots,
|
|
40
|
+
canonical,
|
|
41
|
+
languageAlternates = [],
|
|
42
|
+
config,
|
|
43
|
+
transitions = true,
|
|
44
|
+
charset = "utf-8",
|
|
45
|
+
viewport = "width=device-width,initial-scale=1",
|
|
46
|
+
} = Astro.props as HeadProps
|
|
47
|
+
|
|
48
|
+
// Resolve defaults from config if available
|
|
49
|
+
const siteName = config?.name || "Rimelight";
|
|
50
|
+
const siteUrl = config?.url || "";
|
|
51
|
+
const siteDescription = config?.description || "";
|
|
52
|
+
const ogImageFallback = config?.seo?.ogImageFallback || config?.ogImage || "";
|
|
53
|
+
const themeColor = config?.branding?.colors?.themeColor;
|
|
54
|
+
const favicon = config?.branding?.favicon?.svg || "/favicon.svg";
|
|
55
|
+
const twitterHandle = config?.seo?.authorHandle;
|
|
56
|
+
const titleTemplate = config?.seo?.titleTemplate || `%s | ${siteName}`;
|
|
57
|
+
|
|
58
|
+
// Calculated Values
|
|
59
|
+
const displayTitle = is404
|
|
60
|
+
? `404 - Not Found | ${siteName}`
|
|
61
|
+
: title
|
|
62
|
+
? titleTemplate.replace('%s', title)
|
|
63
|
+
: siteName;
|
|
64
|
+
|
|
65
|
+
const finalDescription = description || siteDescription;
|
|
66
|
+
const safeDescription = finalDescription && config?.seo?.maxDescriptionLength
|
|
67
|
+
? (finalDescription.length > config.seo.maxDescriptionLength
|
|
68
|
+
? finalDescription.slice(0, config.seo.maxDescriptionLength - 3) + "..."
|
|
69
|
+
: finalDescription)
|
|
70
|
+
: finalDescription;
|
|
71
|
+
|
|
72
|
+
const finalOgImage = ogImage || ogImageFallback;
|
|
73
|
+
const absoluteOgImage = finalOgImage
|
|
74
|
+
? (finalOgImage.startsWith('http') ? finalOgImage : `${siteUrl}${finalOgImage}`)
|
|
75
|
+
: undefined;
|
|
76
|
+
|
|
77
|
+
const finalCanonical = canonical || (!is404 ? Astro.url.href : undefined);
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
<!-- Basic Metadata -->
|
|
81
|
+
<meta charset={charset} />
|
|
82
|
+
<meta name="viewport" content={viewport} />
|
|
83
|
+
<meta name="generator" content={Astro.generator} />
|
|
84
|
+
|
|
85
|
+
<!-- SEO -->
|
|
86
|
+
<title>{displayTitle}</title>
|
|
87
|
+
{safeDescription && <meta name="description" content={safeDescription} />}
|
|
88
|
+
{robots ? <meta name="robots" content={robots} /> : (noindex || is404) && <meta name="robots" content="noindex, nofollow" />}
|
|
89
|
+
{finalCanonical && <link rel="canonical" href={finalCanonical} />}
|
|
90
|
+
|
|
91
|
+
<!-- i18n Alternates -->
|
|
92
|
+
{languageAlternates.map((alt) => (
|
|
93
|
+
<link rel="alternate" hreflang={alt.hreflang} href={alt.href} />
|
|
94
|
+
))}
|
|
95
|
+
|
|
96
|
+
<!-- Open Graph / Facebook -->
|
|
97
|
+
<meta property="og:type" content="website" />
|
|
98
|
+
<meta property="og:url" content={Astro.url.href} />
|
|
99
|
+
<meta property="og:title" content={title || siteName} />
|
|
100
|
+
{safeDescription && <meta property="og:description" content={safeDescription} />}
|
|
101
|
+
{absoluteOgImage && <meta property="og:image" content={absoluteOgImage} />}
|
|
102
|
+
<meta property="og:site_name" content={siteName} />
|
|
103
|
+
|
|
104
|
+
<!-- Twitter -->
|
|
105
|
+
<meta property="twitter:card" content="summary_large_image" />
|
|
106
|
+
<meta property="twitter:url" content={Astro.url.href} />
|
|
107
|
+
<meta property="twitter:title" content={title || siteName} />
|
|
108
|
+
{safeDescription && <meta property="twitter:description" content={safeDescription} />}
|
|
109
|
+
{absoluteOgImage && <meta property="twitter:image" content={absoluteOgImage} />}
|
|
110
|
+
{twitterHandle && <meta property="twitter:site" content={twitterHandle} />}
|
|
111
|
+
{twitterHandle && <meta property="twitter:creator" content={twitterHandle} />}
|
|
112
|
+
|
|
113
|
+
<!-- Icons -->
|
|
114
|
+
<link rel="icon" type="image/svg+xml" href={favicon} />
|
|
115
|
+
|
|
116
|
+
<!-- Theme -->
|
|
117
|
+
{themeColor && <meta name="theme-color" content={themeColor} />}
|
|
118
|
+
|
|
119
|
+
<script is:inline>
|
|
120
|
+
const theme = (() => {
|
|
121
|
+
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
|
122
|
+
return localStorage.getItem('theme');
|
|
123
|
+
}
|
|
124
|
+
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
125
|
+
return 'dark';
|
|
126
|
+
}
|
|
127
|
+
return 'light';
|
|
128
|
+
})();
|
|
129
|
+
|
|
130
|
+
if (theme === 'dark') {
|
|
131
|
+
document.documentElement.classList.add('dark');
|
|
132
|
+
} else {
|
|
133
|
+
document.documentElement.classList.remove('dark');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
window.localStorage.setItem('theme', theme);
|
|
137
|
+
</script>
|
|
138
|
+
|
|
139
|
+
<script>
|
|
140
|
+
document.addEventListener('astro:after-swap', () => {
|
|
141
|
+
if (localStorage.getItem('theme') === 'dark') {
|
|
142
|
+
document.documentElement.classList.add('dark');
|
|
143
|
+
} else {
|
|
144
|
+
document.documentElement.classList.remove('dark');
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
{transitions && <ClientRouter />}
|
|
150
|
+
|
|
151
|
+
<App client:load>
|
|
152
|
+
<slot />
|
|
153
|
+
</App>
|
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { scv, cx } from "css-variants";
|
|
3
|
-
import { twMerge } from "tailwind-merge";
|
|
4
|
-
import defu from "defu";
|
|
5
|
-
import { headerTheme } from "@/themes/header.theme.ts";
|
|
6
|
-
import { getUIConfig } from "virtual:rimelight-ui";
|
|
7
|
-
|
|
8
|
-
const header = scv({
|
|
9
|
-
slots: [...headerTheme.slots],
|
|
10
|
-
base: headerTheme.base,
|
|
11
|
-
variants: headerTheme.variants,
|
|
12
|
-
defaultVariants: headerTheme.defaultVariants,
|
|
13
|
-
classNameResolver: (...args) => twMerge(cx(...args)),
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
export interface RLAHeaderProps {
|
|
17
|
-
/**
|
|
18
|
-
* Whether to contain the header content in a max-width container.
|
|
19
|
-
* @default true
|
|
20
|
-
*/
|
|
21
|
-
contain?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Whether the header should be sticky at the top.
|
|
24
|
-
* @default true
|
|
25
|
-
*/
|
|
26
|
-
sticky?: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* UI overrides for individual slots, derived from the header theme.
|
|
29
|
-
*/
|
|
30
|
-
ui?: Partial<Record<keyof typeof headerTheme.base, string>>;
|
|
31
|
-
/**
|
|
32
|
-
* External class — applied to the root element.
|
|
33
|
-
*/
|
|
34
|
-
class?: string;
|
|
35
|
-
/**
|
|
36
|
-
* Standard HTML attributes for <header>
|
|
37
|
-
*/
|
|
38
|
-
[key: string]: unknown;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const {
|
|
42
|
-
contain = true,
|
|
43
|
-
sticky = true,
|
|
44
|
-
class: className,
|
|
45
|
-
ui: uiProp,
|
|
46
|
-
...rest
|
|
47
|
-
} = Astro.props as RLAHeaderProps
|
|
48
|
-
|
|
49
|
-
const globalConfig = getUIConfig();
|
|
50
|
-
|
|
51
|
-
const mergedUI = defu(
|
|
52
|
-
uiProp ?? {},
|
|
53
|
-
(globalConfig.header as Record<string, unknown>) ?? {},
|
|
54
|
-
) as Record<keyof typeof headerTheme.base, string | undefined>;
|
|
55
|
-
|
|
56
|
-
const { root, container, left, center, right } = header({ contain, sticky });
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
<header
|
|
60
|
-
class:list={[twMerge(root, mergedUI.root, className)]}
|
|
61
|
-
data-slot="rla-header"
|
|
62
|
-
{...rest}
|
|
63
|
-
>
|
|
64
|
-
<div class:list={[twMerge(container, mergedUI.container)]}>
|
|
65
|
-
<div class:list={[twMerge(left, mergedUI.left)]} data-slot="header-left">
|
|
66
|
-
<slot name="left" />
|
|
67
|
-
</div>
|
|
68
|
-
<div class:list={[twMerge(center, mergedUI.center)]} data-slot="header-center">
|
|
69
|
-
<slot name="center" />
|
|
70
|
-
</div>
|
|
71
|
-
<div class:list={[twMerge(right, mergedUI.right)]} data-slot="header-right">
|
|
72
|
-
<slot name="right" />
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</header>
|
|
1
|
+
---
|
|
2
|
+
import { scv, cx } from "css-variants";
|
|
3
|
+
import { twMerge } from "tailwind-merge";
|
|
4
|
+
import defu from "defu";
|
|
5
|
+
import { headerTheme } from "@/themes/header.theme.ts";
|
|
6
|
+
import { getUIConfig } from "virtual:rimelight-ui";
|
|
7
|
+
|
|
8
|
+
const header = scv({
|
|
9
|
+
slots: [...headerTheme.slots],
|
|
10
|
+
base: headerTheme.base,
|
|
11
|
+
variants: headerTheme.variants,
|
|
12
|
+
defaultVariants: headerTheme.defaultVariants,
|
|
13
|
+
classNameResolver: (...args) => twMerge(cx(...args)),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export interface RLAHeaderProps {
|
|
17
|
+
/**
|
|
18
|
+
* Whether to contain the header content in a max-width container.
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
contain?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the header should be sticky at the top.
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
sticky?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* UI overrides for individual slots, derived from the header theme.
|
|
29
|
+
*/
|
|
30
|
+
ui?: Partial<Record<keyof typeof headerTheme.base, string>>;
|
|
31
|
+
/**
|
|
32
|
+
* External class — applied to the root element.
|
|
33
|
+
*/
|
|
34
|
+
class?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Standard HTML attributes for <header>
|
|
37
|
+
*/
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
contain = true,
|
|
43
|
+
sticky = true,
|
|
44
|
+
class: className,
|
|
45
|
+
ui: uiProp,
|
|
46
|
+
...rest
|
|
47
|
+
} = Astro.props as RLAHeaderProps
|
|
48
|
+
|
|
49
|
+
const globalConfig = getUIConfig();
|
|
50
|
+
|
|
51
|
+
const mergedUI = defu(
|
|
52
|
+
uiProp ?? {},
|
|
53
|
+
(globalConfig.header as Record<string, unknown>) ?? {},
|
|
54
|
+
) as Record<keyof typeof headerTheme.base, string | undefined>;
|
|
55
|
+
|
|
56
|
+
const { root, container, left, center, right } = header({ contain, sticky });
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
<header
|
|
60
|
+
class:list={[twMerge(root, mergedUI.root, className)]}
|
|
61
|
+
data-slot="rla-header"
|
|
62
|
+
{...rest}
|
|
63
|
+
>
|
|
64
|
+
<div class:list={[twMerge(container, mergedUI.container)]}>
|
|
65
|
+
<div class:list={[twMerge(left, mergedUI.left)]} data-slot="header-left">
|
|
66
|
+
<slot name="left" />
|
|
67
|
+
</div>
|
|
68
|
+
<div class:list={[twMerge(center, mergedUI.center)]} data-slot="header-center">
|
|
69
|
+
<slot name="center" />
|
|
70
|
+
</div>
|
|
71
|
+
<div class:list={[twMerge(right, mergedUI.right)]} data-slot="header-right">
|
|
72
|
+
<slot name="right" />
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</header>
|
package/src/composables/useUi.ts
CHANGED
|
@@ -13,8 +13,8 @@ import type { DefaultUIConfig } from "@/themes"
|
|
|
13
13
|
* @param uiProp - The local `ui` prop from defineProps
|
|
14
14
|
* @returns ComputedRef of merged UI overrides
|
|
15
15
|
*/
|
|
16
|
-
export function useUi
|
|
17
|
-
componentName:
|
|
16
|
+
export function useUi(
|
|
17
|
+
componentName: keyof DefaultUIConfig,
|
|
18
18
|
uiProp: Record<string, unknown> | undefined
|
|
19
19
|
): ComputedRef<Record<string, unknown>> {
|
|
20
20
|
const globalConfig = getUIConfig()
|
package/src/env.d.ts
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
/// <reference types="astro/client" />
|
|
2
|
-
|
|
3
|
-
declare module "virtual:rimelight-ui" {
|
|
4
|
-
import type { DefaultUIConfig } from "../themes"
|
|
5
|
-
|
|
6
|
-
export function getUIConfig(): DefaultUIConfig
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
declare namespace App {
|
|
10
|
-
interface Locals {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
cfContext?: Record<string, unknown>
|
|
15
|
-
}
|
|
16
|
-
}
|
|
1
|
+
/// <reference types="astro/client" />
|
|
2
|
+
|
|
3
|
+
declare module "virtual:rimelight-ui" {
|
|
4
|
+
import type { DefaultUIConfig } from "../themes"
|
|
5
|
+
|
|
6
|
+
export function getUIConfig(): DefaultUIConfig
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare namespace App {
|
|
10
|
+
interface Locals {
|
|
11
|
+
cfContext?: Record<string, unknown>
|
|
12
|
+
}
|
|
13
|
+
}
|