@rimelight/ui 0.0.34 → 0.0.35
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/README.md +1 -0
- package/package.json +19 -24
- package/src/components/astro/RLAAccordion.astro +94 -18
- package/src/components/astro/RLAAvatar.astro +135 -68
- package/src/components/astro/RLABadge.astro +4 -2
- package/src/components/astro/RLABanner.astro +138 -23
- package/src/components/astro/RLABreadcrumb.astro +13 -0
- package/src/components/astro/RLAButton.astro +8 -4
- package/src/components/astro/RLACallout.astro +112 -0
- package/src/components/astro/RLACard.astro +6 -5
- package/src/components/astro/RLACarousel.astro +104 -75
- package/src/components/astro/RLAChart.astro +4 -1
- package/src/components/astro/RLAChip.astro +11 -5
- package/src/components/astro/RLACollapsible.astro +4 -1
- package/src/components/astro/RLAColorArea.astro +432 -0
- package/src/components/astro/RLAColorField.astro +300 -0
- package/src/components/astro/RLAColorSlider.astro +413 -0
- package/src/components/astro/RLAConfirm.astro +13 -11
- package/src/components/astro/RLACopyMarkdown.astro +78 -0
- package/src/components/astro/RLADate.astro +30 -0
- package/src/components/astro/RLADrawer.astro +50 -3
- package/src/components/astro/RLADropdownMenu.astro +78 -51
- package/src/components/astro/RLAFieldGroup.astro +4 -1
- package/src/components/astro/RLAFileUpload.astro +114 -100
- package/src/components/astro/RLAFooter.astro +2 -1
- package/src/components/astro/RLAFormField.astro +10 -6
- package/src/components/astro/RLAGrid.astro +31 -39
- package/src/components/astro/RLAHead.astro +139 -39
- package/src/components/astro/RLAHeader.astro +2 -1
- package/src/components/astro/RLAImage.astro +274 -251
- package/src/components/astro/RLAInput.astro +14 -9
- package/src/components/astro/RLAInputMenu.astro +119 -83
- package/src/components/astro/RLAKbd.astro +5 -5
- package/src/components/astro/RLALayoutGrid.astro +395 -0
- package/src/components/astro/RLALink.astro +23 -1
- package/src/components/astro/RLALinkGroup.astro +99 -99
- package/src/components/astro/RLALocaleSelector.astro +29 -35
- package/src/components/astro/RLALogo.astro +117 -90
- package/src/components/astro/RLAModal.astro +48 -18
- package/src/components/astro/RLANavigationMenu.astro +381 -326
- package/src/components/astro/{RLADocsPackageManagers.astro → RLAPackageManagers.astro} +81 -87
- package/src/components/astro/RLAPageSection.astro +4 -2
- package/src/components/astro/RLAPopover.astro +4 -3
- package/src/components/astro/RLAPost.astro +11 -2
- package/src/components/astro/RLAScrollArea.astro +4 -1
- package/src/components/astro/RLAScrollToTop.astro +200 -172
- package/src/components/astro/RLASearch.astro +282 -0
- package/src/components/astro/RLASelect.astro +78 -55
- package/src/components/astro/RLASeparator.astro +11 -6
- package/src/components/astro/RLAShare.astro +60 -0
- package/src/components/astro/RLAShortcuts.astro +429 -0
- package/src/components/astro/RLASidebar.astro +158 -212
- package/src/components/astro/RLASlideover.astro +47 -17
- package/src/components/astro/RLASplitter.astro +194 -141
- package/src/components/astro/RLASteps.astro +118 -71
- package/src/components/astro/RLASurround.astro +39 -0
- package/src/components/astro/RLATable.astro +4 -1
- package/src/components/astro/RLATableOfContents.astro +109 -23
- package/src/components/astro/RLATabs.astro +147 -133
- package/src/components/astro/RLATextarea.astro +4 -1
- package/src/components/astro/RLAThemeSelector.astro +105 -76
- package/src/components/astro/RLAToast.astro +35 -21
- package/src/components/vue/RLVBanner.vue +230 -0
- package/src/components/vue/RLVButton.vue +3 -2
- package/src/components/vue/RLVCheckbox.vue +4 -2
- package/src/components/vue/RLVFooter.vue +3 -2
- package/src/components/vue/RLVHeader.vue +3 -2
- package/src/components/vue/RLVIcon.vue +4 -2
- package/src/components/vue/RLVInput.vue +5 -4
- package/src/components/vue/RLVLogo.vue +25 -3
- package/src/components/vue/RLVPlaceholder.vue +3 -2
- package/src/components/vue/RLVPost.vue +8 -2
- package/src/components/vue/RLVProgress.vue +60 -0
- package/src/components/vue/RLVScrollToTop.vue +65 -52
- package/src/components/vue/RLVSection.vue +14 -13
- package/src/components/vue/RLVToast.vue +17 -4
- package/src/composables/index.ts +1 -0
- package/src/composables/useHeaderStore.ts +1 -0
- package/src/composables/useShortcuts.ts +11 -0
- package/src/config/security.ts +43 -36
- package/src/config/site.config.ts +1 -0
- package/src/docs/components/ApiChangelog.astro +147 -0
- package/src/{plugins/starlightDocsApi → docs}/components/ApiEmits.astro +4 -4
- package/src/{plugins/starlightDocsApi → docs}/components/ApiProps.astro +4 -4
- package/src/{plugins/starlightDocsApi → docs}/components/ApiSlots.astro +4 -4
- package/src/docs/components/ApiTheme.astro +100 -0
- package/src/docs/extractAll.ts +94 -0
- package/src/docs/extractors/extractAstro.ts +69 -0
- package/src/docs/extractors/extractTheme.ts +56 -0
- package/src/{plugins/starlightDocsApi/extract-vue.ts → docs/extractors/extractVue.ts} +164 -15
- package/src/{plugins/starlightDocsApi → docs}/utils.ts +1 -2
- package/src/env.d.ts +0 -12
- package/src/integrations/ui.ts +35 -19
- package/src/middleware/security.ts +75 -19
- package/src/presets/index.ts +118 -293
- package/src/styles/index.css +0 -2
- package/src/themes/accordion.theme.ts +29 -3
- package/src/themes/aspect-ratio.theme.ts +1 -1
- package/src/themes/avatar-group.theme.ts +1 -1
- package/src/themes/avatar.theme.ts +2 -2
- package/src/themes/badge.theme.ts +2 -3
- package/src/themes/banner.theme.ts +40 -4
- package/src/themes/breadcrumb.theme.ts +1 -1
- package/src/themes/button.theme.ts +21 -22
- package/src/themes/{alert.theme.ts → callout.theme.ts} +12 -39
- package/src/themes/card.theme.ts +17 -9
- package/src/themes/carousel.theme.ts +1 -1
- package/src/themes/checkbox.theme.ts +1 -2
- package/src/themes/chip.theme.ts +2 -3
- package/src/themes/color-area.theme.ts +33 -0
- package/src/themes/color-field.theme.ts +32 -0
- package/src/themes/color-slider.theme.ts +34 -0
- package/src/themes/confirm.theme.ts +1 -1
- package/src/themes/container.theme.ts +1 -1
- package/src/themes/drawer.theme.ts +1 -1
- package/src/themes/footer.theme.ts +2 -2
- package/src/themes/grid.theme.ts +38 -0
- package/src/themes/header.theme.ts +4 -3
- package/src/themes/hover-card.theme.ts +1 -1
- package/src/themes/image.theme.ts +11 -1
- package/src/themes/input.theme.ts +7 -1
- package/src/themes/kbd.theme.ts +2 -3
- package/src/themes/label.theme.ts +1 -1
- package/src/themes/layout-grid.theme.ts +16 -0
- package/src/themes/link-group.theme.ts +0 -4
- package/src/themes/marquee.theme.ts +1 -1
- package/src/themes/modal.theme.ts +1 -1
- package/src/themes/navigation-menu.theme.ts +2 -2
- package/src/themes/page-section.theme.ts +4 -2
- package/src/themes/pagination.theme.ts +1 -1
- package/src/themes/pin-input.theme.ts +1 -1
- package/src/themes/popover.theme.ts +1 -1
- package/src/themes/post.theme.ts +23 -3
- package/src/themes/progress.theme.ts +1 -2
- package/src/themes/scroll-to-top.theme.ts +2 -3
- package/src/themes/search.theme.ts +64 -0
- package/src/themes/separator.theme.ts +1 -1
- package/src/themes/slideover.theme.ts +1 -1
- package/src/themes/slider.theme.ts +1 -2
- package/src/themes/spinner.theme.ts +1 -2
- package/src/themes/{megamenu.theme.ts → steps.theme.ts} +3 -3
- package/src/themes/sticky-surface.theme.ts +1 -1
- package/src/themes/switch.theme.ts +1 -2
- package/src/themes/table-of-contents.theme.ts +21 -2
- package/src/themes/timeline.theme.ts +7 -2
- package/src/themes/toast.theme.ts +1 -1
- package/src/types/components/accordion.ts +35 -2
- package/src/types/components/avatar.ts +16 -0
- package/src/types/components/banner.ts +42 -0
- package/src/types/components/button.ts +3 -4
- package/src/types/components/{alert.ts → callout.ts} +6 -6
- package/src/types/components/color-area.ts +19 -0
- package/src/types/components/color-field.ts +21 -0
- package/src/types/components/color-slider.ts +20 -0
- package/src/types/components/grid.ts +23 -0
- package/src/types/components/input.ts +4 -0
- package/src/types/components/layout-grid.ts +145 -0
- package/src/types/components/link-group.ts +4 -2
- package/src/types/components/link.ts +15 -0
- package/src/types/components/logo.ts +3 -3
- package/src/types/components/post.ts +12 -0
- package/src/types/components/search.ts +29 -0
- package/src/types/components/sidebar.ts +2 -6
- package/src/types/components/{megamenu.ts → steps.ts} +7 -1
- package/src/types/components/table-of-contents.ts +22 -0
- package/src/types/components/toast.ts +4 -0
- package/src/types/docs.ts +40 -0
- package/src/types/index.ts +151 -1
- package/src/types/theme.ts +6 -6
- package/src/utils/color.ts +165 -0
- package/src/utils/docs.ts +260 -110
- package/src/utils/headerStack.ts +24 -4
- package/src/utils/index.ts +2 -0
- package/src/utils/shortcuts.ts +322 -0
- package/src/components/TableOfContents.astro +0 -107
- package/src/components/astro/RLAAlert.astro +0 -93
- package/src/components/astro/RLAChangelog.astro +0 -23
- package/src/components/astro/RLAMegamenu.astro +0 -23
- package/src/plugins/index.ts +0 -2
- package/src/plugins/starlightDocsApi/components/ApiTheme.astro +0 -27
- package/src/plugins/starlightDocsApi/index.ts +0 -272
- package/src/styles/prism.css +0 -42
- package/src/themes/changelog.theme.ts +0 -13
- package/src/types/components/index.ts +0 -134
- /package/src/{plugins/starlightDocsApi → docs}/types.ts +0 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { SearchProps } from "../../types"
|
|
5
|
+
import searchThemeDefault, { createSearchTheme } from "../../themes/search.theme"
|
|
6
|
+
import { getUIConfig } from "virtual:rimelight-ui"
|
|
7
|
+
import RLAInput from "./RLAInput.astro"
|
|
8
|
+
import RLAKbd from "./RLAKbd.astro"
|
|
9
|
+
import RLAButton from "./RLAButton.astro"
|
|
10
|
+
|
|
11
|
+
const uiConfig = getUIConfig()
|
|
12
|
+
const searchTheme = uiConfig.colors ? createSearchTheme() : searchThemeDefault
|
|
13
|
+
|
|
14
|
+
const search = scv(searchTheme)
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
endpoint = "/api/search",
|
|
18
|
+
placeholder = "Search...",
|
|
19
|
+
messages = {},
|
|
20
|
+
ui: uiProp,
|
|
21
|
+
class: className,
|
|
22
|
+
...rest
|
|
23
|
+
} = Astro.props as SearchProps
|
|
24
|
+
|
|
25
|
+
const classes = resolveClasses(search, {}, useUi("search", uiProp), className)
|
|
26
|
+
|
|
27
|
+
// Default messages
|
|
28
|
+
const searchPlaceholder = messages.searchPlaceholder || placeholder
|
|
29
|
+
const noResultsLabel = messages.noResultsLabel || "No results found"
|
|
30
|
+
const footerCloseHint = messages.footerCloseHint || "Escape to close"
|
|
31
|
+
const footerBackendHint = messages.footerBackendHint || "Postgres Full-Text Search"
|
|
32
|
+
|
|
33
|
+
// Generate unique IDs for this instance to avoid collisions if multiple search bars exist
|
|
34
|
+
const uniqueId = Math.random().toString(36).slice(2, 8)
|
|
35
|
+
const triggerId = `search-trigger-${uniqueId}`
|
|
36
|
+
const modalId = `search-modal-${uniqueId}`
|
|
37
|
+
const inputId = `search-input-${uniqueId}`
|
|
38
|
+
const resultsId = `search-results-${uniqueId}`
|
|
39
|
+
const emptyId = `search-empty-${uniqueId}`
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
<rla-search
|
|
43
|
+
class:list={[classes.root, "rla-search-wrapper"]}
|
|
44
|
+
data-slot="root"
|
|
45
|
+
data-trigger-id={triggerId}
|
|
46
|
+
data-modal-id={modalId}
|
|
47
|
+
data-input-id={inputId}
|
|
48
|
+
data-results-id={resultsId}
|
|
49
|
+
data-empty-id={emptyId}
|
|
50
|
+
data-endpoint={endpoint}
|
|
51
|
+
{...rest}
|
|
52
|
+
>
|
|
53
|
+
{Astro.slots.has("trigger") ? (
|
|
54
|
+
<div id={triggerId} class="cursor-pointer">
|
|
55
|
+
<slot name="trigger" />
|
|
56
|
+
</div>
|
|
57
|
+
) : (
|
|
58
|
+
<RLAButton
|
|
59
|
+
id={triggerId}
|
|
60
|
+
variant="ghost"
|
|
61
|
+
color="neutral"
|
|
62
|
+
square
|
|
63
|
+
aria-label="Open Search"
|
|
64
|
+
>
|
|
65
|
+
<span class="i-lucide-search w-5 h-5" />
|
|
66
|
+
</RLAButton>
|
|
67
|
+
)}
|
|
68
|
+
|
|
69
|
+
<!-- Search modal -->
|
|
70
|
+
<div id={modalId} class={classes.modal} data-slot="modal">
|
|
71
|
+
<div class={classes.dialog} data-slot="dialog">
|
|
72
|
+
<!-- Search header -->
|
|
73
|
+
<div class={classes.header} data-slot="header">
|
|
74
|
+
<RLAInput
|
|
75
|
+
variant="ghost"
|
|
76
|
+
type="text"
|
|
77
|
+
id={inputId}
|
|
78
|
+
placeholder={searchPlaceholder}
|
|
79
|
+
autocomplete="off"
|
|
80
|
+
ui={{
|
|
81
|
+
root: "relative flex items-center w-full",
|
|
82
|
+
input: "pl-8 pr-20 py-1.5 text-base outline-none text-white placeholder-neutral-500",
|
|
83
|
+
leading: "absolute left-0 text-neutral-400 flex items-center",
|
|
84
|
+
trailing: "absolute right-0 flex items-center gap-2"
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<span slot="leading" class={classes.headerIcon} />
|
|
88
|
+
<div slot="trailing" class="flex items-center gap-2">
|
|
89
|
+
<RLAKbd size="sm">Esc</RLAKbd>
|
|
90
|
+
<button id={`${modalId}-close`} class={classes.close} aria-label="Close search">
|
|
91
|
+
<span class={classes.closeIcon}></span>
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
</RLAInput>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<!-- Results list -->
|
|
98
|
+
<div id={resultsId} class={classes.results} data-slot="results"></div>
|
|
99
|
+
|
|
100
|
+
<!-- Empty state -->
|
|
101
|
+
<div id={emptyId} class={classes.empty} data-slot="empty">
|
|
102
|
+
{noResultsLabel}
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<!-- Footer -->
|
|
106
|
+
<div class={classes.footer} data-slot="footer">
|
|
107
|
+
<span>{footerCloseHint}</span>
|
|
108
|
+
<span>{footerBackendHint}</span>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</rla-search>
|
|
113
|
+
|
|
114
|
+
<script>
|
|
115
|
+
import { defineShortcuts } from "../../utils/shortcuts"
|
|
116
|
+
|
|
117
|
+
class RLASearch extends HTMLElement {
|
|
118
|
+
private searchCleanup: (() => void) | null = null;
|
|
119
|
+
|
|
120
|
+
connectedCallback() {
|
|
121
|
+
this.style.display = 'contents';
|
|
122
|
+
const triggerId = this.dataset.triggerId;
|
|
123
|
+
const modalId = this.dataset.modalId;
|
|
124
|
+
const inputId = this.dataset.inputId;
|
|
125
|
+
const resultsId = this.dataset.resultsId;
|
|
126
|
+
const emptyId = this.dataset.emptyId;
|
|
127
|
+
const endpoint = this.dataset.endpoint;
|
|
128
|
+
|
|
129
|
+
const trigger = this.querySelector(`#${triggerId}`);
|
|
130
|
+
const modal = this.querySelector(`#${modalId}`) as HTMLElement | null;
|
|
131
|
+
const closeBtn = this.querySelector(`#${modalId}-close`);
|
|
132
|
+
const input = this.querySelector(`#${inputId}`) as HTMLInputElement | null;
|
|
133
|
+
const resultsContainer = this.querySelector(`#${resultsId}`) as HTMLElement | null;
|
|
134
|
+
const emptyState = this.querySelector(`#${emptyId}`) as HTMLElement | null;
|
|
135
|
+
|
|
136
|
+
// Teleport modal to body to avoid containing block/transform clipping from header containers
|
|
137
|
+
if (modal && modal.parentNode !== document.body) {
|
|
138
|
+
document.body.appendChild(modal);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let escapeCleanup: (() => void) | null = null;
|
|
142
|
+
|
|
143
|
+
const openModal = () => {
|
|
144
|
+
modal?.classList.remove("hidden");
|
|
145
|
+
modal?.classList.add("flex");
|
|
146
|
+
input?.focus();
|
|
147
|
+
document.body.style.overflow = "hidden";
|
|
148
|
+
|
|
149
|
+
escapeCleanup = defineShortcuts({
|
|
150
|
+
escape: {
|
|
151
|
+
handler: () => closeModal(),
|
|
152
|
+
usingInput: true
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const closeModal = () => {
|
|
158
|
+
modal?.classList.add("hidden");
|
|
159
|
+
modal?.classList.remove("flex");
|
|
160
|
+
document.body.style.overflow = "";
|
|
161
|
+
if (input) input.value = "";
|
|
162
|
+
if (resultsContainer) resultsContainer.innerHTML = "";
|
|
163
|
+
emptyState?.classList.add("hidden");
|
|
164
|
+
|
|
165
|
+
if (escapeCleanup) {
|
|
166
|
+
escapeCleanup();
|
|
167
|
+
escapeCleanup = null;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const onTriggerClick = () => openModal();
|
|
172
|
+
const onCloseClick = () => closeModal();
|
|
173
|
+
const onModalClick = (e: MouseEvent) => {
|
|
174
|
+
const target = e.target as HTMLElement | null;
|
|
175
|
+
if (target && (target === modal || !target.closest('[data-slot="dialog"]'))) {
|
|
176
|
+
closeModal();
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
trigger?.addEventListener("click", onTriggerClick);
|
|
181
|
+
closeBtn?.addEventListener("click", onCloseClick);
|
|
182
|
+
modal?.addEventListener("click", onModalClick);
|
|
183
|
+
|
|
184
|
+
// Register global shortcut key Cmd+K / Ctrl+K
|
|
185
|
+
const metaKCleanup = defineShortcuts({
|
|
186
|
+
meta_k: {
|
|
187
|
+
handler: () => openModal(),
|
|
188
|
+
label: "Open Search",
|
|
189
|
+
description: "Open the global site search modal",
|
|
190
|
+
category: "navigation"
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
let debounceTimer: any;
|
|
195
|
+
const onInput = () => {
|
|
196
|
+
clearTimeout(debounceTimer);
|
|
197
|
+
const query = input?.value.trim();
|
|
198
|
+
if (!query) {
|
|
199
|
+
if (resultsContainer) resultsContainer.innerHTML = "";
|
|
200
|
+
emptyState?.classList.add("hidden");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
debounceTimer = setTimeout(async () => {
|
|
205
|
+
try {
|
|
206
|
+
console.log("[Search UI] Fetching search results for:", query);
|
|
207
|
+
const res = await fetch(`${endpoint}?q=${encodeURIComponent(query)}`);
|
|
208
|
+
|
|
209
|
+
if (!res.ok) {
|
|
210
|
+
throw new Error(`HTTP status: ${res.status}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const results = await res.json();
|
|
214
|
+
console.log("[Search UI] Received results:", results);
|
|
215
|
+
|
|
216
|
+
if (resultsContainer) resultsContainer.innerHTML = "";
|
|
217
|
+
|
|
218
|
+
if (!Array.isArray(results) || results.length === 0) {
|
|
219
|
+
emptyState?.classList.remove("hidden");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
emptyState?.classList.add("hidden");
|
|
224
|
+
results.forEach((item) => {
|
|
225
|
+
const div = document.createElement("a");
|
|
226
|
+
div.href = item.url || "#";
|
|
227
|
+
div.className = "flex flex-col gap-1 p-3 hover:bg-neutral-800 rounded-lg transition-colors border border-transparent hover:border-neutral-700 text-left decoration-none";
|
|
228
|
+
|
|
229
|
+
div.innerHTML = `
|
|
230
|
+
<div class="flex items-center justify-between">
|
|
231
|
+
<span class="text-sm font-semibold text-white">${item.title || "Untitled"}</span>
|
|
232
|
+
${item.sourceType ? `<span class="text-[10px] uppercase tracking-wider px-1.5 py-0.5 rounded bg-neutral-800 text-neutral-400 font-mono">${item.sourceType}</span>` : ""}
|
|
233
|
+
</div>
|
|
234
|
+
${item.snippet ? `<p class="text-xs text-neutral-400 line-clamp-2 m-0">${item.snippet}</p>` : ""}
|
|
235
|
+
`;
|
|
236
|
+
resultsContainer?.appendChild(div);
|
|
237
|
+
});
|
|
238
|
+
} catch (err) {
|
|
239
|
+
console.error("[Search UI] Search failed:", err);
|
|
240
|
+
if (resultsContainer) resultsContainer.innerHTML = "";
|
|
241
|
+
emptyState?.classList.remove("hidden");
|
|
242
|
+
}
|
|
243
|
+
}, 200);
|
|
244
|
+
};
|
|
245
|
+
input?.addEventListener("input", onInput);
|
|
246
|
+
|
|
247
|
+
this.searchCleanup = () => {
|
|
248
|
+
clearTimeout(debounceTimer);
|
|
249
|
+
trigger?.removeEventListener("click", onTriggerClick);
|
|
250
|
+
closeBtn?.removeEventListener("click", onCloseClick);
|
|
251
|
+
modal?.removeEventListener("click", onModalClick);
|
|
252
|
+
input?.removeEventListener("input", onInput);
|
|
253
|
+
if (escapeCleanup) escapeCleanup();
|
|
254
|
+
metaKCleanup();
|
|
255
|
+
if (modal && modal.parentNode === document.body) {
|
|
256
|
+
modal.remove();
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
disconnectedCallback() {
|
|
262
|
+
if (this.searchCleanup) {
|
|
263
|
+
this.searchCleanup();
|
|
264
|
+
this.searchCleanup = null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!customElements.get('rla-search')) {
|
|
270
|
+
customElements.define('rla-search', RLASearch);
|
|
271
|
+
}
|
|
272
|
+
</script>
|
|
273
|
+
|
|
274
|
+
<style is:global>
|
|
275
|
+
#search-results mark {
|
|
276
|
+
background-color: rgba(168, 85, 247, 0.3) !important;
|
|
277
|
+
color: rgb(233, 213, 255) !important;
|
|
278
|
+
padding-left: 2px;
|
|
279
|
+
padding-right: 2px;
|
|
280
|
+
border-radius: 4px;
|
|
281
|
+
}
|
|
282
|
+
</style>
|
|
@@ -4,7 +4,10 @@ import { useUi, resolveClasses } from "../../utils"
|
|
|
4
4
|
import type { SelectProps } from "../../types"
|
|
5
5
|
import selectThemeDefault, { createSelectTheme } from "../../themes/select.theme"
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { getUIConfig } from "virtual:rimelight-ui"
|
|
8
|
+
|
|
9
|
+
const uiConfig = getUIConfig()
|
|
10
|
+
const selectTheme = uiConfig.colors ? createSelectTheme() : selectThemeDefault
|
|
8
11
|
const select = scv(selectTheme)
|
|
9
12
|
|
|
10
13
|
const {
|
|
@@ -35,63 +38,69 @@ const normalizedItems = items.map(item => {
|
|
|
35
38
|
const selectedItem = normalizedItems.find(item => item.value === value)
|
|
36
39
|
const initialLabel = selectedItem ? selectedItem.label : ""
|
|
37
40
|
---
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
41
|
+
<rla-select {...rest}>
|
|
42
|
+
<div class={classes.root} data-slot="root" data-disabled={disabled ? "" : undefined}>
|
|
43
|
+
{label && <label class={classes.label} data-slot="label">{label}</label>}
|
|
44
|
+
|
|
45
|
+
<div class="relative w-full" data-select-wrapper>
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
class={classes.trigger}
|
|
49
|
+
data-slot="trigger"
|
|
50
|
+
disabled={disabled}
|
|
51
|
+
data-select-trigger
|
|
52
|
+
>
|
|
53
|
+
<span data-select-value>{initialLabel || placeholder}</span>
|
|
54
|
+
<span class="i-lucide-chevron-down opacity-50" />
|
|
55
|
+
</button>
|
|
56
|
+
|
|
57
|
+
<div class={classes.content + " hidden"} data-select-dropdown data-slot="content">
|
|
58
|
+
<div class="py-1" data-select-items>
|
|
59
|
+
{normalizedItems.map(item => (
|
|
60
|
+
<div
|
|
61
|
+
class={classes.item}
|
|
62
|
+
data-slot="item"
|
|
63
|
+
data-value={item.value}
|
|
64
|
+
data-label={item.label}
|
|
65
|
+
data-select-item
|
|
66
|
+
>
|
|
67
|
+
<span class={classes.itemText}>{item.label}</span>
|
|
68
|
+
<span class={classes.itemIcon + " hidden i-lucide-check"} data-check-icon />
|
|
69
|
+
</div>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
68
72
|
</div>
|
|
69
73
|
</div>
|
|
74
|
+
<input type="hidden" name={name} value={value} data-select-hidden-input />
|
|
70
75
|
</div>
|
|
71
|
-
|
|
72
|
-
</div>
|
|
76
|
+
</rla-select>
|
|
73
77
|
|
|
74
78
|
<script>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
class RLASelect extends HTMLElement {
|
|
80
|
+
private selectCleanup: (() => void) | null = null;
|
|
81
|
+
|
|
82
|
+
connectedCallback() {
|
|
83
|
+
this.style.display = 'contents';
|
|
84
|
+
const wrapper = this.querySelector("[data-select-wrapper]") as HTMLElement | null;
|
|
85
|
+
if (!wrapper) return;
|
|
79
86
|
|
|
80
87
|
const trigger = wrapper.querySelector("[data-select-trigger]") as HTMLButtonElement;
|
|
81
88
|
const dropdown = wrapper.querySelector("[data-select-dropdown]") as HTMLDivElement;
|
|
82
|
-
const items = wrapper.querySelectorAll("[data-select-item]");
|
|
89
|
+
const items = wrapper.querySelectorAll<HTMLElement>("[data-select-item]");
|
|
83
90
|
const valueSpan = wrapper.querySelector("[data-select-value]");
|
|
84
|
-
const hiddenInput =
|
|
91
|
+
const hiddenInput = this.querySelector("[data-select-hidden-input]") as HTMLInputElement | null;
|
|
85
92
|
|
|
86
93
|
if (!trigger || !dropdown) return;
|
|
87
94
|
|
|
88
|
-
|
|
95
|
+
const onTriggerClick = (e: MouseEvent) => {
|
|
89
96
|
e.stopPropagation();
|
|
90
97
|
dropdown.classList.toggle("hidden");
|
|
91
|
-
}
|
|
98
|
+
};
|
|
99
|
+
trigger.addEventListener("click", onTriggerClick);
|
|
92
100
|
|
|
101
|
+
const itemCleanups: (() => void)[] = [];
|
|
93
102
|
items.forEach(item => {
|
|
94
|
-
|
|
103
|
+
const onItemClick = (e: MouseEvent) => {
|
|
95
104
|
e.stopPropagation();
|
|
96
105
|
const val = (item as HTMLElement).dataset.value || "";
|
|
97
106
|
const label = (item as HTMLElement).dataset.label || "";
|
|
@@ -106,18 +115,32 @@ const initialLabel = selectedItem ? selectedItem.label : ""
|
|
|
106
115
|
item.querySelector("[data-check-icon]")?.classList.remove("hidden");
|
|
107
116
|
|
|
108
117
|
dropdown.classList.add("hidden");
|
|
109
|
-
}
|
|
118
|
+
};
|
|
119
|
+
item.addEventListener("click", onItemClick);
|
|
120
|
+
itemCleanups.push(() => item.removeEventListener("click", onItemClick));
|
|
110
121
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
|
|
123
|
+
const onDocumentClick = () => {
|
|
124
|
+
dropdown.classList.add("hidden");
|
|
125
|
+
};
|
|
126
|
+
document.addEventListener("click", onDocumentClick);
|
|
127
|
+
|
|
128
|
+
this.selectCleanup = () => {
|
|
129
|
+
trigger.removeEventListener("click", onTriggerClick);
|
|
130
|
+
itemCleanups.forEach(fn => fn());
|
|
131
|
+
document.removeEventListener("click", onDocumentClick);
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
disconnectedCallback() {
|
|
136
|
+
if (this.selectCleanup) {
|
|
137
|
+
this.selectCleanup();
|
|
138
|
+
this.selectCleanup = null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!customElements.get('rla-select')) {
|
|
144
|
+
customElements.define('rla-select', RLASelect);
|
|
145
|
+
}
|
|
123
146
|
</script>
|
|
@@ -8,7 +8,7 @@ import RLAAvatar from "./RLAAvatar.astro"
|
|
|
8
8
|
import { cx } from "css-variants"
|
|
9
9
|
|
|
10
10
|
const uiConfig = getUIConfig()
|
|
11
|
-
const separatorTheme = uiConfig.colors ? createSeparatorTheme() : separatorThemeDefault
|
|
11
|
+
const separatorTheme = uiConfig.colors ? createSeparatorTheme(uiConfig.colors) : separatorThemeDefault
|
|
12
12
|
|
|
13
13
|
const separator = scv(separatorTheme)
|
|
14
14
|
|
|
@@ -25,14 +25,19 @@ const {
|
|
|
25
25
|
ui: uiProp,
|
|
26
26
|
class: className,
|
|
27
27
|
...rest
|
|
28
|
-
} = Astro.props as SeparatorProps
|
|
28
|
+
} = Astro.props as SeparatorProps & {
|
|
29
|
+
color?: string
|
|
30
|
+
size?: "xs" | "sm" | "md" | "lg" | "xl"
|
|
31
|
+
type?: "solid" | "dashed" | "dotted"
|
|
32
|
+
position?: "start" | "center" | "end"
|
|
33
|
+
}
|
|
29
34
|
|
|
30
35
|
const classes = resolveClasses(separator, {
|
|
31
36
|
orientation,
|
|
32
|
-
color,
|
|
33
|
-
size,
|
|
34
|
-
type,
|
|
35
|
-
position
|
|
37
|
+
color: color as "neutral",
|
|
38
|
+
size: size as "xs",
|
|
39
|
+
type: type as "solid",
|
|
40
|
+
position: position as "center"
|
|
36
41
|
}, useUi("separator", uiProp), className)
|
|
37
42
|
---
|
|
38
43
|
<div
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
import RLAButton from "./RLAButton.astro"
|
|
3
|
+
import type { ButtonProps } from "../../types"
|
|
4
|
+
|
|
5
|
+
interface Props extends Omit<ButtonProps, "href" | "label" | "leadingIcon" | "trailingIcon" | "loading" | "loadingAuto" | "square" | "active" | "activeColor" | "activeVariant"> {}
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
...rest
|
|
9
|
+
} = Astro.props
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<RLAButton
|
|
13
|
+
id="rl-share"
|
|
14
|
+
color="neutral"
|
|
15
|
+
variant="outline"
|
|
16
|
+
size="sm"
|
|
17
|
+
leadingIcon="i-lucide-share-2"
|
|
18
|
+
label="Share"
|
|
19
|
+
{...rest}
|
|
20
|
+
/>
|
|
21
|
+
|
|
22
|
+
<script>
|
|
23
|
+
function setupShare() {
|
|
24
|
+
const shareBtn = document.getElementById("rl-share") as (HTMLButtonElement & { rlShareRegistered?: boolean }) | null;
|
|
25
|
+
if (!shareBtn) return;
|
|
26
|
+
|
|
27
|
+
if (shareBtn.rlShareRegistered) return;
|
|
28
|
+
shareBtn.rlShareRegistered = true;
|
|
29
|
+
|
|
30
|
+
shareBtn.addEventListener("click", async () => {
|
|
31
|
+
if (navigator.share) {
|
|
32
|
+
try {
|
|
33
|
+
await navigator.share({ title: document.title, url: window.location.href });
|
|
34
|
+
} catch (err) {
|
|
35
|
+
const error = err as { name?: string };
|
|
36
|
+
if (error.name !== "AbortError") {
|
|
37
|
+
console.error("Share failed:", err);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
try {
|
|
42
|
+
await navigator.clipboard.writeText(window.location.href);
|
|
43
|
+
const labelEl = shareBtn.querySelector('[data-slot="label"]');
|
|
44
|
+
if (labelEl) {
|
|
45
|
+
const origText = labelEl.textContent;
|
|
46
|
+
labelEl.textContent = "Link Copied!";
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
labelEl.textContent = origText;
|
|
49
|
+
}, 2000);
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error("Failed to copy link:", err);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setupShare();
|
|
59
|
+
document.addEventListener("astro:after-swap", setupShare);
|
|
60
|
+
</script>
|