@rimelight/ui 0.0.35 → 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 +8 -8
- 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
|
@@ -21,16 +21,110 @@ const {
|
|
|
21
21
|
const classes = resolveClasses(popover, {
|
|
22
22
|
placement
|
|
23
23
|
}, useUi("popover", uiProp), className)
|
|
24
|
-
const popoverId = `rla-popover-${Math.random().toString(36).substring(2, 9)}`
|
|
25
24
|
---
|
|
26
|
-
<div class={classes.root} data-slot="popover-container" {...rest}>
|
|
27
|
-
<div class={classes.trigger} data-slot="trigger">
|
|
25
|
+
<div class={classes.root} data-slot="popover-container" data-placement={placement} {...rest}>
|
|
26
|
+
<div class={`${classes.trigger} popover-trigger`} data-slot="trigger">
|
|
28
27
|
<slot name="trigger">
|
|
29
|
-
{triggerLabel && <button type="button" class="cursor-pointer"
|
|
28
|
+
{triggerLabel && <button type="button" class="cursor-pointer">{triggerLabel}</button>}
|
|
30
29
|
</slot>
|
|
31
30
|
</div>
|
|
32
31
|
|
|
33
|
-
<div
|
|
32
|
+
<div class={`${classes.content} popover-content hidden`} data-slot="content">
|
|
34
33
|
<slot />
|
|
35
34
|
</div>
|
|
36
35
|
</div>
|
|
36
|
+
|
|
37
|
+
<script>
|
|
38
|
+
function initPopovers() {
|
|
39
|
+
document.querySelectorAll('[data-slot="popover-container"]').forEach(container => {
|
|
40
|
+
const el = container as any;
|
|
41
|
+
if (el.dataset && el.dataset.popoverInitialized) return;
|
|
42
|
+
if (el.dataset) el.dataset.popoverInitialized = "true";
|
|
43
|
+
|
|
44
|
+
const trigger = container.querySelector('.popover-trigger');
|
|
45
|
+
const content = container.querySelector('.popover-content') as HTMLElement;
|
|
46
|
+
|
|
47
|
+
if (!trigger || !content) return;
|
|
48
|
+
|
|
49
|
+
trigger.addEventListener('click', (e) => {
|
|
50
|
+
e.stopPropagation();
|
|
51
|
+
const isOpen = !content.classList.contains('hidden');
|
|
52
|
+
|
|
53
|
+
document.querySelectorAll('.popover-content').forEach(otherContent => {
|
|
54
|
+
const other = otherContent as HTMLElement;
|
|
55
|
+
if (other !== content && !other.classList.contains('hidden')) {
|
|
56
|
+
other.classList.add('hidden');
|
|
57
|
+
other.style.left = '';
|
|
58
|
+
other.style.right = '';
|
|
59
|
+
other.style.transform = '';
|
|
60
|
+
other.dispatchEvent(new CustomEvent('popover-toggle', { detail: { open: false } }));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (isOpen) {
|
|
65
|
+
content.classList.add('hidden');
|
|
66
|
+
content.style.left = '';
|
|
67
|
+
content.style.right = '';
|
|
68
|
+
content.style.transform = '';
|
|
69
|
+
content.dispatchEvent(new CustomEvent('popover-toggle', { detail: { open: false } }));
|
|
70
|
+
} else {
|
|
71
|
+
content.classList.remove('hidden');
|
|
72
|
+
|
|
73
|
+
// Reset styles to measure original computed bounds
|
|
74
|
+
content.style.left = '';
|
|
75
|
+
content.style.right = '';
|
|
76
|
+
content.style.transform = '';
|
|
77
|
+
|
|
78
|
+
const rect = content.getBoundingClientRect();
|
|
79
|
+
const viewportWidth = window.innerWidth;
|
|
80
|
+
const placement = container.getAttribute('data-placement') || 'bottom';
|
|
81
|
+
|
|
82
|
+
let shift = 0;
|
|
83
|
+
const safetyMargin = 24;
|
|
84
|
+
if (rect.right > viewportWidth - safetyMargin) {
|
|
85
|
+
shift = (viewportWidth - safetyMargin) - rect.right;
|
|
86
|
+
} else if (rect.left < safetyMargin) {
|
|
87
|
+
shift = safetyMargin - rect.left;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (shift !== 0) {
|
|
91
|
+
const isCentered = (placement === 'top' || placement === 'bottom') &&
|
|
92
|
+
!content.classList.contains('right-0') &&
|
|
93
|
+
!content.classList.contains('left-0');
|
|
94
|
+
if (isCentered) {
|
|
95
|
+
content.style.transform = `translateX(calc(-50% + ${shift}px))`;
|
|
96
|
+
} else {
|
|
97
|
+
content.style.transform = `translateX(${shift}px)`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
content.dispatchEvent(new CustomEvent('popover-toggle', { detail: { open: true } }));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
content.addEventListener('click', (e) => {
|
|
106
|
+
e.stopPropagation();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const win = window as any;
|
|
112
|
+
if (!win.hasGlobalPopoverListener) {
|
|
113
|
+
win.hasGlobalPopoverListener = true;
|
|
114
|
+
document.addEventListener('click', () => {
|
|
115
|
+
document.querySelectorAll('.popover-content').forEach(content => {
|
|
116
|
+
const c = content as HTMLElement;
|
|
117
|
+
if (!c.classList.contains('hidden')) {
|
|
118
|
+
c.classList.add('hidden');
|
|
119
|
+
c.style.left = '';
|
|
120
|
+
c.style.right = '';
|
|
121
|
+
c.style.transform = '';
|
|
122
|
+
c.dispatchEvent(new CustomEvent('popover-toggle', { detail: { open: false } }));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
document.addEventListener('astro:page-load', initPopovers);
|
|
129
|
+
initPopovers();
|
|
130
|
+
</script>
|
|
@@ -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",
|