@rimelight/ui 0.0.36 → 0.0.37
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 +24 -20
- package/src/components/astro/RLAButton.astro +58 -26
- package/src/components/astro/RLACarousel.astro +202 -26
- package/src/components/astro/RLALink.astro +10 -12
- package/src/components/astro/RLALocaleSelector.astro +2 -1
- package/src/components/astro/RLAPopover.astro +99 -5
- package/src/components/astro/RLAScrollToTop.astro +10 -19
- package/src/components/astro/RLASelect.astro +52 -47
- package/src/components/astro/RLASidebar.astro +1 -1
- package/src/components/astro/RLATableOfContents.astro +76 -27
- package/src/components/astro/RLAThemeSelector.astro +1 -0
- package/src/components/astro/RLAToast.astro +2 -2
- package/src/components/vue/RLVScrollToTop.vue +12 -10
- package/src/components/vue/RLVToast.vue +1 -1
- package/src/components/vue/RLVToaster.vue +15 -3
- package/src/config/index.ts +0 -1
- package/src/integrations/index.ts +0 -1
- package/src/integrations/ui.ts +12 -11
- package/src/themes/link-group.theme.ts +1 -1
- package/src/themes/locale-selector.theme.ts +1 -1
- package/src/themes/popover.theme.ts +2 -5
- package/src/themes/select.theme.ts +1 -1
- package/src/themes/toast.theme.ts +9 -3
- package/src/types/components/button.ts +2 -1
- package/src/types/components/link.ts +0 -10
- package/src/types/components/sidebar.ts +1 -1
- package/src/types/components/table-of-contents.ts +5 -0
- package/src/types/components/toast.ts +1 -1
- package/src/utils/index.ts +2 -0
- package/src/utils/merge.ts +62 -0
- package/src/utils/scroll.ts +41 -0
- package/src/utils/toast.ts +37 -0
- package/src/utils/useUi.ts +2 -2
- package/src/components/HttpObservatory.astro +0 -103
- package/src/components/LighthouseScores.astro +0 -204
- package/src/composables/index.ts +0 -4
- package/src/composables/useHeaderStore.ts +0 -14
- package/src/composables/useScrollToTop.ts +0 -62
- package/src/composables/useShortcuts.ts +0 -11
- package/src/composables/useToast.ts +0 -43
- package/src/config/security.ts +0 -227
- package/src/integrations/sri.ts +0 -92
- package/src/middleware/index.ts +0 -1
- package/src/middleware/security.ts +0 -210
|
@@ -64,9 +64,7 @@ const classes = resolveClasses(scrollToTop, { color, theme }, useUi("scrollToTop
|
|
|
64
64
|
</rla-scroll-to-top>
|
|
65
65
|
|
|
66
66
|
<script>
|
|
67
|
-
|
|
68
|
-
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
69
|
-
}
|
|
67
|
+
import { trackScroll, scrollToTop } from "../../utils/scroll"
|
|
70
68
|
|
|
71
69
|
class RLAScrollToTop extends HTMLElement {
|
|
72
70
|
private cleanupScroll: (() => void) | null = null;
|
|
@@ -95,12 +93,12 @@ const classes = resolveClasses(scrollToTop, { color, theme }, useUi("scrollToTop
|
|
|
95
93
|
return this.styleEl;
|
|
96
94
|
};
|
|
97
95
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const scrollPercentage = scrollHeight > 0 ? (scrollTop / scrollHeight) * 100 : 0;
|
|
96
|
+
innerElement.dataset.rlaInstance = String(id);
|
|
97
|
+
|
|
98
|
+
button.addEventListener('click', scrollToTop);
|
|
102
99
|
|
|
103
|
-
|
|
100
|
+
const unsubscribe = trackScroll({ threshold }, (state) => {
|
|
101
|
+
if (state.isVisible) {
|
|
104
102
|
innerElement.classList.add('visible');
|
|
105
103
|
} else {
|
|
106
104
|
innerElement.classList.remove('visible');
|
|
@@ -113,23 +111,16 @@ const classes = resolveClasses(scrollToTop, { color, theme }, useUi("scrollToTop
|
|
|
113
111
|
background-color: #000;
|
|
114
112
|
}
|
|
115
113
|
.rla-scroll-to-top-inner[data-rla-instance="${id}"] .gauge-progress {
|
|
116
|
-
--gauge-progress: ${scrollPercentage};
|
|
114
|
+
--gauge-progress: ${state.scrollPercentage};
|
|
117
115
|
--gauge-stroke-width: ${gaugeBorderWidth}px;
|
|
118
116
|
--gauge-color: var(--color-${color}-500);
|
|
119
117
|
}
|
|
120
118
|
`;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
innerElement.dataset.rlaInstance = String(id);
|
|
124
|
-
|
|
125
|
-
button.addEventListener('click', onClick);
|
|
126
|
-
|
|
127
|
-
window.addEventListener('scroll', updateStyles, { passive: true });
|
|
128
|
-
updateStyles();
|
|
119
|
+
});
|
|
129
120
|
|
|
130
121
|
this.cleanupScroll = () => {
|
|
131
|
-
button.removeEventListener('click',
|
|
132
|
-
|
|
122
|
+
button.removeEventListener('click', scrollToTop);
|
|
123
|
+
unsubscribe();
|
|
133
124
|
if (this.styleEl) {
|
|
134
125
|
this.styleEl.remove();
|
|
135
126
|
this.styleEl = null;
|
|
@@ -51,7 +51,7 @@ const initialLabel = selectedItem ? selectedItem.label : ""
|
|
|
51
51
|
data-select-trigger
|
|
52
52
|
>
|
|
53
53
|
<span data-select-value>{initialLabel || placeholder}</span>
|
|
54
|
-
<span class="i-lucide-chevron-down opacity-50" />
|
|
54
|
+
<span class="i-lucide-chevron-down ml-2 size-4 opacity-50" />
|
|
55
55
|
</button>
|
|
56
56
|
|
|
57
57
|
<div class={classes.content + " hidden"} data-select-dropdown data-slot="content">
|
|
@@ -78,61 +78,66 @@ const initialLabel = selectedItem ? selectedItem.label : ""
|
|
|
78
78
|
<script>
|
|
79
79
|
class RLASelect extends HTMLElement {
|
|
80
80
|
private selectCleanup: (() => void) | null = null;
|
|
81
|
+
private initTimeout: any = null;
|
|
81
82
|
|
|
82
83
|
connectedCallback() {
|
|
83
84
|
this.style.display = 'contents';
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
e
|
|
97
|
-
dropdown.classList.toggle("hidden");
|
|
98
|
-
};
|
|
99
|
-
trigger.addEventListener("click", onTriggerClick);
|
|
100
|
-
|
|
101
|
-
const itemCleanups: (() => void)[] = [];
|
|
102
|
-
items.forEach(item => {
|
|
103
|
-
const onItemClick = (e: MouseEvent) => {
|
|
85
|
+
this.initTimeout = setTimeout(() => {
|
|
86
|
+
const wrapper = this.querySelector("[data-select-wrapper]") as HTMLElement | null;
|
|
87
|
+
if (!wrapper) return;
|
|
88
|
+
|
|
89
|
+
const trigger = wrapper.querySelector("[data-select-trigger]") as HTMLButtonElement;
|
|
90
|
+
const dropdown = wrapper.querySelector("[data-select-dropdown]") as HTMLDivElement;
|
|
91
|
+
const items = wrapper.querySelectorAll<HTMLElement>("[data-select-item]");
|
|
92
|
+
const valueSpan = wrapper.querySelector("[data-select-value]");
|
|
93
|
+
const hiddenInput = this.querySelector("[data-select-hidden-input]") as HTMLInputElement | null;
|
|
94
|
+
|
|
95
|
+
if (!trigger || !dropdown) return;
|
|
96
|
+
|
|
97
|
+
const onTriggerClick = (e: MouseEvent) => {
|
|
104
98
|
e.stopPropagation();
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
99
|
+
dropdown.classList.toggle("hidden");
|
|
100
|
+
};
|
|
101
|
+
trigger.addEventListener("click", onTriggerClick);
|
|
102
|
+
|
|
103
|
+
const itemCleanups: (() => void)[] = [];
|
|
104
|
+
items.forEach(item => {
|
|
105
|
+
const onItemClick = (e: MouseEvent) => {
|
|
106
|
+
e.stopPropagation();
|
|
107
|
+
const val = (item as HTMLElement).dataset.value || "";
|
|
108
|
+
const label = (item as HTMLElement).dataset.label || "";
|
|
109
|
+
if (hiddenInput) {
|
|
110
|
+
hiddenInput.value = val;
|
|
111
|
+
hiddenInput.dispatchEvent(new Event("change", { bubbles: true }));
|
|
112
|
+
}
|
|
113
|
+
if (valueSpan) valueSpan.textContent = label;
|
|
114
|
+
|
|
115
|
+
items.forEach(i => i.querySelector("[data-check-icon]")?.classList.add("hidden"));
|
|
116
|
+
item.querySelector("[data-check-icon]")?.classList.remove("hidden");
|
|
117
|
+
|
|
118
|
+
dropdown.classList.add("hidden");
|
|
119
|
+
};
|
|
120
|
+
item.addEventListener("click", onItemClick);
|
|
121
|
+
itemCleanups.push(() => item.removeEventListener("click", onItemClick));
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const onDocumentClick = () => {
|
|
117
125
|
dropdown.classList.add("hidden");
|
|
118
126
|
};
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
this.selectCleanup = () => {
|
|
129
|
-
trigger.removeEventListener("click", onTriggerClick);
|
|
130
|
-
itemCleanups.forEach(fn => fn());
|
|
131
|
-
document.removeEventListener("click", onDocumentClick);
|
|
132
|
-
};
|
|
127
|
+
document.addEventListener("click", onDocumentClick);
|
|
128
|
+
|
|
129
|
+
this.selectCleanup = () => {
|
|
130
|
+
trigger.removeEventListener("click", onTriggerClick);
|
|
131
|
+
itemCleanups.forEach(fn => fn());
|
|
132
|
+
document.removeEventListener("click", onDocumentClick);
|
|
133
|
+
};
|
|
134
|
+
}, 0);
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
disconnectedCallback() {
|
|
138
|
+
if (this.initTimeout) {
|
|
139
|
+
clearTimeout(this.initTimeout);
|
|
140
|
+
}
|
|
136
141
|
if (this.selectCleanup) {
|
|
137
142
|
this.selectCleanup();
|
|
138
143
|
this.selectCleanup = null;
|
|
@@ -41,7 +41,7 @@ function normalizeBadge(badge: any) {
|
|
|
41
41
|
const v = badge.variant.toLowerCase()
|
|
42
42
|
if (v === "tip" || v === "success") { color = "success"; variant = "soft" }
|
|
43
43
|
else if (v === "caution" || v === "warning") { color = "warning"; variant = "soft" }
|
|
44
|
-
else if (v === "
|
|
44
|
+
else if (v === "error") { color = "error"; variant = "soft" }
|
|
45
45
|
else if (v === "note" || v === "default") { color = "primary"; variant = "soft" }
|
|
46
46
|
}
|
|
47
47
|
return { text: badge.text || "", color, variant: variant as "solid" | "outline" | "soft", class: badge.class || "" }
|
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
tableOfContents: tableOfContentsConfig,
|
|
16
16
|
ui: uiProp,
|
|
17
17
|
class: className,
|
|
18
|
+
collapsible = false,
|
|
18
19
|
containerSelector = ".markdown-content",
|
|
19
20
|
...rest
|
|
20
21
|
} = Astro.props as TableOfContentsProps
|
|
@@ -36,32 +37,62 @@ const headingSelector = Array.from({ length: maxLevel - minLevel + 1 }, (_, i) =
|
|
|
36
37
|
---
|
|
37
38
|
|
|
38
39
|
{tableOfContentsConfig !== false && filteredHeadings.length > 0 && (
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
40
|
+
collapsible ? (
|
|
41
|
+
<details class:list={[className]} data-rla-toc-collapsible data-container-selector={containerSelector} data-heading-selector={headingSelector} {...rest}>
|
|
42
|
+
<summary class="flex items-center justify-between gap-3 px-4 h-10 text-sm font-medium text-foreground cursor-pointer select-none hover:bg-muted/40 transition-colors list-none">
|
|
43
|
+
<span class="flex items-center gap-2">
|
|
44
|
+
<span class="i-lucide-list size-4 text-primary shrink-0" />
|
|
45
|
+
<span>On this page</span>
|
|
46
|
+
</span>
|
|
47
|
+
<span class="rla-toc-chevron i-lucide-chevron-down size-4 text-muted-foreground shrink-0 transition-transform duration-200" />
|
|
48
|
+
</summary>
|
|
49
|
+
<div class="px-4 pt-3 max-h-[40vh] overflow-y-auto border-t border-border/50 bg-background">
|
|
50
|
+
<ul class="flex flex-col gap-0.5 pb-8">
|
|
51
|
+
{filteredHeadings.map(heading => (
|
|
52
|
+
<li class:list={[heading.depth === 3 && "pl-4"]}>
|
|
53
|
+
<a
|
|
54
|
+
href={`#${heading.slug}`}
|
|
55
|
+
class:list={[
|
|
56
|
+
"toc-link block py-1.5 text-sm transition-colors hover:text-primary no-underline",
|
|
57
|
+
heading.depth === 2 ? "text-foreground font-medium" : "text-muted-foreground"
|
|
58
|
+
]}
|
|
59
|
+
data-rla-toc-link
|
|
60
|
+
>
|
|
61
|
+
{heading.text}
|
|
62
|
+
</a>
|
|
63
|
+
</li>
|
|
64
|
+
))}
|
|
65
|
+
</ul>
|
|
66
|
+
</div>
|
|
67
|
+
</details>
|
|
68
|
+
) : (
|
|
69
|
+
<div class={classes.root} data-slot="root" data-container-selector={containerSelector} data-heading-selector={headingSelector} {...rest}>
|
|
70
|
+
<div class={classes.container} data-slot="container">
|
|
71
|
+
<h4 class={classes.title} data-slot="title">On this page</h4>
|
|
72
|
+
<ul class:list={[classes.list, "mt-2xl flex flex-col gap-3xs"]} data-slot="list">
|
|
73
|
+
{filteredHeadings.map(heading => (
|
|
74
|
+
<li
|
|
75
|
+
class:list={[
|
|
76
|
+
classes.item,
|
|
77
|
+
heading.depth === 2 && "pl-3xs",
|
|
78
|
+
heading.depth === 3 && "pl-6",
|
|
79
|
+
heading.depth >= 4 && "pl-9"
|
|
80
|
+
]}
|
|
81
|
+
data-slot="item"
|
|
57
82
|
>
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
83
|
+
<a
|
|
84
|
+
href={`#${heading.slug}`}
|
|
85
|
+
class:list={["toc-link", classes.link]}
|
|
86
|
+
data-slot="link"
|
|
87
|
+
>
|
|
88
|
+
<span class={classes.linkText} data-slot="link-text">{heading.text}</span>
|
|
89
|
+
</a>
|
|
90
|
+
</li>
|
|
91
|
+
))}
|
|
92
|
+
</ul>
|
|
93
|
+
</div>
|
|
63
94
|
</div>
|
|
64
|
-
|
|
95
|
+
)
|
|
65
96
|
)}
|
|
66
97
|
|
|
67
98
|
<style is:global>
|
|
@@ -73,10 +104,23 @@ const headingSelector = Array.from({ length: maxLevel - minLevel + 1 }, (_, i) =
|
|
|
73
104
|
|
|
74
105
|
<script is:inline>
|
|
75
106
|
(function() {
|
|
76
|
-
|
|
77
|
-
|
|
107
|
+
// ── Collapsible TOC behaviour (chevron + close-on-link-click) ──
|
|
108
|
+
function initCollapsibleTocs() {
|
|
109
|
+
document.querySelectorAll('[data-rla-toc-collapsible]').forEach(function(details) {
|
|
110
|
+
var chevron = details.querySelector('.rla-toc-chevron');
|
|
111
|
+
details.addEventListener('toggle', function() {
|
|
112
|
+
if (chevron) chevron.style.transform = details.open ? 'rotate(180deg)' : '';
|
|
113
|
+
});
|
|
114
|
+
details.querySelectorAll('[data-rla-toc-link]').forEach(function(link) {
|
|
115
|
+
link.addEventListener('click', function() { details.open = false; });
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
78
119
|
|
|
120
|
+
// ── Active-heading tracker (desktop sidebar) ──
|
|
79
121
|
function updateActiveHeading() {
|
|
122
|
+
var root = document.querySelector('[data-slot="root"][data-container-selector]');
|
|
123
|
+
if (!root) return;
|
|
80
124
|
var links = root.querySelectorAll(".toc-link");
|
|
81
125
|
if (links.length === 0) return;
|
|
82
126
|
|
|
@@ -102,8 +146,13 @@ const headingSelector = Array.from({ length: maxLevel - minLevel + 1 }, (_, i) =
|
|
|
102
146
|
}
|
|
103
147
|
}
|
|
104
148
|
|
|
149
|
+
initCollapsibleTocs();
|
|
105
150
|
updateActiveHeading();
|
|
151
|
+
|
|
106
152
|
window.addEventListener("scroll", updateActiveHeading, { passive: true });
|
|
107
|
-
document.addEventListener("astro:after-swap",
|
|
153
|
+
document.addEventListener("astro:after-swap", function() {
|
|
154
|
+
initCollapsibleTocs();
|
|
155
|
+
updateActiveHeading();
|
|
156
|
+
});
|
|
108
157
|
})();
|
|
109
158
|
</script>
|
|
@@ -24,12 +24,12 @@ const {
|
|
|
24
24
|
} = Astro.props as ToastProps
|
|
25
25
|
|
|
26
26
|
const classes = resolveClasses(toast, {
|
|
27
|
-
variant:
|
|
27
|
+
variant: ["success", "error", "warning", "info"].includes(variant ?? "") ? variant : "default"
|
|
28
28
|
}, useUi("toast", uiProp), className)
|
|
29
29
|
|
|
30
30
|
const variantIcons: Record<string, string> = {
|
|
31
31
|
default: "i-lucide-bell",
|
|
32
|
-
|
|
32
|
+
error: "i-lucide-alert-circle",
|
|
33
33
|
success: "i-lucide-check-circle-2",
|
|
34
34
|
warning: "i-lucide-alert-triangle",
|
|
35
35
|
info: "i-lucide-info",
|
|
@@ -3,8 +3,8 @@ import { scv } from "css-variants"
|
|
|
3
3
|
import { useUi, resolveClasses } from "../../utils"
|
|
4
4
|
import type { ScrollToTopProps } from "../../types/components/scroll-to-top"
|
|
5
5
|
import scrollToTopTheme from "../../themes/scroll-to-top.theme"
|
|
6
|
-
import {
|
|
7
|
-
import { computed, onMounted, onUnmounted, watch, useAttrs } from "vue"
|
|
6
|
+
import { trackScroll, scrollToTop as scrollToTopFn } from "../../utils"
|
|
7
|
+
import { ref, computed, onMounted, onUnmounted, watch, useAttrs } from "vue"
|
|
8
8
|
|
|
9
9
|
const attrs = useAttrs()
|
|
10
10
|
const scrollToTop = scv(scrollToTopTheme)
|
|
@@ -21,13 +21,8 @@ const {
|
|
|
21
21
|
|
|
22
22
|
const classes = resolveClasses(scrollToTop, { color }, useUi("scrollToTop", uiProp), className)
|
|
23
23
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
scrollPercentage,
|
|
27
|
-
scrollToTop: scrollToTopFn
|
|
28
|
-
} = useScrollToTop({
|
|
29
|
-
threshold
|
|
30
|
-
})
|
|
24
|
+
const isVisible = ref(false)
|
|
25
|
+
const scrollPercentage = ref(0)
|
|
31
26
|
|
|
32
27
|
const instanceId = `rla-vue-${Math.random().toString(36).slice(2, 8)}`
|
|
33
28
|
const gaugeBorderWidth = progressWidth * 0.5
|
|
@@ -49,16 +44,23 @@ function updateCssVars() {
|
|
|
49
44
|
|
|
50
45
|
watch(scrollPercentage, updateCssVars)
|
|
51
46
|
|
|
47
|
+
let unsubscribe: (() => void) | null = null
|
|
48
|
+
|
|
52
49
|
onMounted(() => {
|
|
53
50
|
const nonce = document.querySelector('meta[name="csp-nonce"]')?.getAttribute("content") ?? ""
|
|
54
51
|
styleEl = document.createElement("style")
|
|
55
52
|
if (nonce) styleEl.setAttribute("nonce", nonce)
|
|
56
53
|
document.head.appendChild(styleEl)
|
|
57
|
-
|
|
54
|
+
|
|
55
|
+
unsubscribe = trackScroll({ threshold }, (state) => {
|
|
56
|
+
isVisible.value = state.isVisible
|
|
57
|
+
scrollPercentage.value = state.scrollPercentage
|
|
58
|
+
})
|
|
58
59
|
})
|
|
59
60
|
|
|
60
61
|
onUnmounted(() => {
|
|
61
62
|
if (styleEl) styleEl.remove()
|
|
63
|
+
unsubscribe?.()
|
|
62
64
|
})
|
|
63
65
|
</script>
|
|
64
66
|
|
|
@@ -22,7 +22,7 @@ const resolvedClasses = computed(() =>
|
|
|
22
22
|
|
|
23
23
|
const variantIcons: Record<string, string> = {
|
|
24
24
|
default: "i-lucide-bell",
|
|
25
|
-
|
|
25
|
+
error: "i-lucide-alert-circle",
|
|
26
26
|
success: "i-lucide-check-circle-2",
|
|
27
27
|
warning: "i-lucide-alert-triangle",
|
|
28
28
|
info: "i-lucide-info"
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { ref, onMounted, onUnmounted } from "vue"
|
|
3
|
+
import { toastsStore, removeToast } from "../../utils"
|
|
3
4
|
import RLVToast from "./RLVToast.vue"
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
+
const toasts = ref(toastsStore.get())
|
|
7
|
+
let unsubscribe: (() => void) | null = null
|
|
8
|
+
|
|
9
|
+
onMounted(() => {
|
|
10
|
+
unsubscribe = toastsStore.subscribe((val) => {
|
|
11
|
+
toasts.value = val
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
onUnmounted(() => {
|
|
16
|
+
unsubscribe?.()
|
|
17
|
+
})
|
|
6
18
|
</script>
|
|
7
19
|
|
|
8
20
|
<template>
|
|
@@ -22,7 +34,7 @@ const { toasts, remove } = useToast()
|
|
|
22
34
|
:key="toast.id"
|
|
23
35
|
v-bind="toast"
|
|
24
36
|
class="pointer-events-auto"
|
|
25
|
-
@close="
|
|
37
|
+
@close="removeToast"
|
|
26
38
|
/>
|
|
27
39
|
</TransitionGroup>
|
|
28
40
|
</div>
|
package/src/config/index.ts
CHANGED
package/src/integrations/ui.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AstroIntegration } from "astro"
|
|
2
2
|
import UnoCSS from "unocss/astro"
|
|
3
3
|
import vue from "@astrojs/vue"
|
|
4
|
-
import
|
|
4
|
+
import { merge } from "../utils/merge"
|
|
5
5
|
import { defaultUIConfig } from "../themes"
|
|
6
6
|
import type { ComponentTheme } from "../utils/defineTheme"
|
|
7
7
|
import rimelightUiPreset, {
|
|
@@ -11,7 +11,6 @@ import rimelightUiPreset, {
|
|
|
11
11
|
type RimelightPresetOptions
|
|
12
12
|
} from "../presets"
|
|
13
13
|
import { extractAllMeta } from "../docs/extractAll"
|
|
14
|
-
import virtual from "vite-plugin-virtual"
|
|
15
14
|
|
|
16
15
|
export interface CategoryConfig {
|
|
17
16
|
label: string
|
|
@@ -44,8 +43,8 @@ export interface UIOptions {
|
|
|
44
43
|
}
|
|
45
44
|
>
|
|
46
45
|
/**
|
|
47
|
-
* Global UI overrides applied to all components. Merged with defaults via
|
|
48
|
-
* wins).
|
|
46
|
+
* Global UI overrides applied to all components. Merged with defaults via our custom recursive
|
|
47
|
+
* merge utility (first arg wins).
|
|
49
48
|
*/
|
|
50
49
|
ui?: UIConfigOverrides
|
|
51
50
|
/**
|
|
@@ -106,7 +105,7 @@ export interface UIOptions {
|
|
|
106
105
|
* })
|
|
107
106
|
*/
|
|
108
107
|
export function ui(options: UIOptions = {}): AstroIntegration[] {
|
|
109
|
-
const resolvedUI =
|
|
108
|
+
const resolvedUI = merge(options.ui ?? {}, defaultUIConfig)
|
|
110
109
|
const coreColors = [
|
|
111
110
|
"primary",
|
|
112
111
|
"secondary",
|
|
@@ -149,22 +148,24 @@ export function ui(options: UIOptions = {}): AstroIntegration[] {
|
|
|
149
148
|
},
|
|
150
149
|
plugins: [
|
|
151
150
|
{
|
|
152
|
-
name: "vite-plugin-rimelight-ui-
|
|
151
|
+
name: "vite-plugin-rimelight-ui-virtual",
|
|
153
152
|
enforce: "pre",
|
|
154
153
|
resolveId(id: string) {
|
|
155
|
-
if (id === virtualModuleId)
|
|
154
|
+
if (id === virtualModuleId || id === "virtual:rimelight-ui-docs-meta") {
|
|
155
|
+
return "\0" + id
|
|
156
|
+
}
|
|
156
157
|
return null
|
|
157
158
|
},
|
|
158
159
|
load(id: string) {
|
|
159
160
|
if (id === resolvedVirtualModuleId) {
|
|
160
161
|
return `export const getUIConfig = () => (${JSON.stringify(resolvedConfig)});`
|
|
161
162
|
}
|
|
163
|
+
if (id === "\0virtual:rimelight-ui-docs-meta") {
|
|
164
|
+
return `export default ${JSON.stringify(docsMeta)}`
|
|
165
|
+
}
|
|
162
166
|
return null
|
|
163
167
|
}
|
|
164
|
-
}
|
|
165
|
-
virtual({
|
|
166
|
-
"virtual:rimelight-ui-docs-meta": `export default ${JSON.stringify(docsMeta)}`
|
|
167
|
-
})
|
|
168
|
+
}
|
|
168
169
|
]
|
|
169
170
|
}
|
|
170
171
|
})
|
|
@@ -5,7 +5,7 @@ export const createLinkGroupTheme = () =>
|
|
|
5
5
|
slots: ["root", "title", "list", "item", "link"],
|
|
6
6
|
base: {
|
|
7
7
|
root: "flex flex-col gap-4",
|
|
8
|
-
title: "font-bold text-sm uppercase tracking-wider text-neutral-500
|
|
8
|
+
title: "font-bold text-sm uppercase tracking-wider text-neutral-500",
|
|
9
9
|
list: "flex flex-col gap-2",
|
|
10
10
|
item: "",
|
|
11
11
|
link: "text-sm text-muted-foreground hover:text-accent-foreground transition-colors"
|
|
@@ -5,7 +5,7 @@ export const createLocaleSelectorTheme = () =>
|
|
|
5
5
|
base: {
|
|
6
6
|
root: "inline-flex flex-col gap-1.5",
|
|
7
7
|
label: "text-xs font-semibold text-muted select-none",
|
|
8
|
-
wrapper: "relative inline-block
|
|
8
|
+
wrapper: "relative inline-block",
|
|
9
9
|
select:
|
|
10
10
|
"w-full appearance-none font-medium rounded-lg transition-all focus:outline-none focus:ring-2 focus:ring-emerald-500/40 cursor-pointer pr-10",
|
|
11
11
|
option: "bg-neutral-900 text-white selection:bg-neutral-800",
|
|
@@ -5,13 +5,10 @@ export const createPopoverTheme = (_colors: readonly string[] = defaultColors) =
|
|
|
5
5
|
defineTheme({
|
|
6
6
|
slots: ["root", "trigger", "content"],
|
|
7
7
|
base: {
|
|
8
|
-
root: "relative inline-block
|
|
8
|
+
root: "relative inline-block",
|
|
9
9
|
trigger: "",
|
|
10
10
|
content: [
|
|
11
|
-
"absolute z-50 w-72 rounded-
|
|
12
|
-
"transition-all duration-200 invisible opacity-0 scale-95 origin-center",
|
|
13
|
-
"group-hover/popover:visible group-hover/popover:opacity-100 group-hover/popover:scale-100",
|
|
14
|
-
"focus-within:visible focus-within:opacity-100 focus-within:scale-100"
|
|
11
|
+
"absolute z-50 w-72 rounded-xl border border-border bg-background p-4 text-foreground shadow-lg outline-none"
|
|
15
12
|
]
|
|
16
13
|
},
|
|
17
14
|
variants: {
|
|
@@ -9,7 +9,7 @@ export const createSelectTheme = () =>
|
|
|
9
9
|
trigger:
|
|
10
10
|
"flex h-9 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-all focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
11
11
|
content:
|
|
12
|
-
"absolute z-50 min-w-[8rem] overflow-hidden rounded-
|
|
12
|
+
"absolute z-50 min-w-[8rem] overflow-hidden rounded-lg border border-border bg-background p-1 text-foreground shadow-lg transition-all w-full mt-1 max-h-60 overflow-y-auto",
|
|
13
13
|
item: "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 px-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
14
14
|
itemText: "truncate",
|
|
15
15
|
itemIcon: "ml-auto size-4"
|
|
@@ -8,18 +8,24 @@ export const createToastTheme = () =>
|
|
|
8
8
|
title: "text-sm font-semibold",
|
|
9
9
|
description: "text-xs opacity-90",
|
|
10
10
|
action:
|
|
11
|
-
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-xs font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.
|
|
11
|
+
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-xs font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.error]:border-muted/40 group-[.error]:hover:border-error-500/30 group-[.error]:hover:bg-error-500 group-[.error]:hover:text-white group-[.error]:focus:ring-error-500",
|
|
12
12
|
close:
|
|
13
13
|
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100"
|
|
14
14
|
},
|
|
15
15
|
variants: {
|
|
16
16
|
variant: {
|
|
17
17
|
default: { root: "bg-background border-border" },
|
|
18
|
-
|
|
19
|
-
root: "
|
|
18
|
+
error: {
|
|
19
|
+
root: "error group border-error-500/20 bg-error-500/10 text-error-700 dark:text-error-400"
|
|
20
20
|
},
|
|
21
21
|
success: {
|
|
22
22
|
root: "border-success-500/20 bg-success-500/10 text-success-700 dark:text-success-400"
|
|
23
|
+
},
|
|
24
|
+
warning: {
|
|
25
|
+
root: "border-warning-500/20 bg-warning-500/10 text-warning-700 dark:text-warning-400"
|
|
26
|
+
},
|
|
27
|
+
info: {
|
|
28
|
+
root: "border-info-500/20 bg-info-500/10 text-info-700 dark:text-info-400"
|
|
23
29
|
}
|
|
24
30
|
}
|
|
25
31
|
},
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { ThemeColor } from "../theme"
|
|
2
|
-
|
|
3
1
|
export interface LinkProps {
|
|
4
2
|
to?: string | object
|
|
5
3
|
href?: string
|
|
@@ -13,14 +11,6 @@ export interface LinkProps {
|
|
|
13
11
|
exactHash?: boolean
|
|
14
12
|
inactiveClass?: string
|
|
15
13
|
activeClass?: string
|
|
16
|
-
variant?: "solid" | "outline" | "soft" | "subtle" | "ghost" | "link"
|
|
17
|
-
color?: ThemeColor
|
|
18
|
-
/**
|
|
19
|
-
* Styles the link visually as a button
|
|
20
|
-
*
|
|
21
|
-
* @default false
|
|
22
|
-
*/
|
|
23
|
-
isButton?: boolean
|
|
24
14
|
/**
|
|
25
15
|
* Screen reader accessibility label. Will automatically append " (opens in a new tab)" for
|
|
26
16
|
* external links if not customized
|