@rimelight/ui 0.0.30 → 0.0.31
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 +1 -1
- package/src/components/astro/RLAAlert.astro +60 -0
- package/src/components/astro/RLAAlertDialog.astro +86 -0
- package/src/components/astro/RLAAspectRatio.astro +23 -0
- package/src/components/astro/RLAAvatar.astro +56 -0
- package/src/components/astro/RLAAvatarGroup.astro +19 -0
- package/src/components/astro/RLABadge.astro +31 -0
- package/src/components/astro/RLABreadcrumb.astro +44 -0
- package/src/components/astro/RLACard.astro +74 -0
- package/src/components/astro/RLACarousel.astro +71 -0
- package/src/components/astro/RLAChart.astro +215 -0
- package/src/components/astro/RLACheckbox.astro +51 -0
- package/src/components/astro/RLACollapsible.astro +38 -0
- package/src/components/astro/RLACombobox.astro +131 -0
- package/src/components/astro/RLADialog.astro +95 -0
- package/src/components/astro/RLADrawer.astro +74 -0
- package/src/components/astro/RLADropdownMenu.astro +82 -0
- package/src/components/astro/RLADropzone.astro +134 -0
- package/src/components/astro/RLAField.astro +43 -0
- package/src/components/{Head.astro → astro/RLAHead.astro} +181 -199
- package/src/components/astro/RLAHoverCard.astro +31 -0
- package/src/components/astro/RLAInput.astro +60 -0
- package/src/components/astro/RLAInputGroup.astro +35 -0
- package/src/components/astro/RLAKbd.astro +22 -0
- package/src/components/astro/RLALabel.astro +23 -0
- package/src/components/astro/RLALocaleSelector.astro +110 -0
- package/src/components/astro/RLAMarquee.astro +32 -0
- package/src/components/astro/RLANativeSelect.astro +51 -0
- package/src/components/astro/RLANavigationMenu.astro +136 -0
- package/src/components/astro/RLAPagination.astro +94 -0
- package/src/components/astro/RLAPopover.astro +31 -0
- package/src/components/astro/RLAProgress.astro +43 -0
- package/src/components/astro/RLAResizable.astro +177 -0
- package/src/components/astro/RLAScrollArea.astro +157 -0
- package/src/components/astro/RLASelect.astro +109 -0
- package/src/components/astro/RLASeparator.astro +27 -0
- package/src/components/astro/RLASheet.astro +72 -0
- package/src/components/astro/RLASidebar.astro +135 -0
- package/src/components/astro/RLASkeleton.astro +20 -0
- package/src/components/astro/RLASlider.astro +53 -0
- package/src/components/astro/RLASpinner.astro +25 -0
- package/src/components/astro/RLAStickySurface.astro +27 -0
- package/src/components/astro/RLASwitch.astro +49 -0
- package/src/components/astro/RLATable.astro +60 -0
- package/src/components/astro/RLATabs.astro +92 -0
- package/src/components/astro/RLATextarea.astro +42 -0
- package/src/components/astro/RLAThemeSelector.astro +73 -0
- package/src/components/astro/RLAToast.astro +107 -0
- package/src/components/astro/RLAToggle.astro +38 -0
- package/src/components/astro/RLAToggleGroup.astro +30 -0
- package/src/components/astro/RLATooltip.astro +31 -0
- package/src/integrations/ui.ts +1 -0
- package/src/themes/alert.theme.ts +51 -0
- package/src/themes/alertDialog.theme.ts +33 -0
- package/src/themes/aspectRatio.theme.ts +11 -0
- package/src/themes/avatar.theme.ts +28 -0
- package/src/themes/avatarGroup.theme.ts +10 -0
- package/src/themes/badge.theme.ts +61 -0
- package/src/themes/breadcrumb.theme.ts +15 -0
- package/src/themes/card.theme.ts +34 -0
- package/src/themes/carousel.theme.ts +32 -0
- package/src/themes/chart.theme.ts +21 -0
- package/src/themes/checkbox.theme.ts +42 -0
- package/src/themes/collapsible.theme.ts +22 -0
- package/src/themes/combobox.theme.ts +28 -0
- package/src/themes/dialog.theme.ts +37 -0
- package/src/themes/drawer.theme.ts +44 -0
- package/src/themes/dropdownMenu.theme.ts +27 -0
- package/src/themes/dropzone.theme.ts +24 -0
- package/src/themes/field.theme.ts +24 -0
- package/src/themes/hoverCard.theme.ts +27 -0
- package/src/themes/input.theme.ts +36 -0
- package/src/themes/inputGroup.theme.ts +23 -0
- package/src/themes/kbd.theme.ts +24 -0
- package/src/themes/label.theme.ts +20 -0
- package/src/themes/localeSelector.theme.ts +50 -0
- package/src/themes/marquee.theme.ts +27 -0
- package/src/themes/nativeSelect.theme.ts +24 -0
- package/src/themes/navigationMenu.theme.ts +19 -0
- package/src/themes/pagination.theme.ts +26 -0
- package/src/themes/popover.theme.ts +28 -0
- package/src/themes/progress.theme.ts +26 -0
- package/src/themes/resizable.theme.ts +22 -0
- package/src/themes/scrollArea.theme.ts +24 -0
- package/src/themes/section.theme.ts +1 -2
- package/src/themes/select.theme.ts +27 -0
- package/src/themes/separator.theme.ts +19 -0
- package/src/themes/sheet.theme.ts +43 -0
- package/src/themes/sidebar.theme.ts +33 -0
- package/src/themes/skeleton.theme.ts +20 -0
- package/src/themes/slider.theme.ts +28 -0
- package/src/themes/spinner.theme.ts +30 -0
- package/src/themes/stickySurface.theme.ts +10 -0
- package/src/themes/switch.theme.ts +59 -0
- package/src/themes/table.theme.ts +36 -0
- package/src/themes/tabs.theme.ts +44 -0
- package/src/themes/textarea.theme.ts +26 -0
- package/src/themes/themeSelector.theme.ts +16 -0
- package/src/themes/toast.theme.ts +25 -0
- package/src/themes/toggle.theme.ts +42 -0
- package/src/themes/toggleGroup.theme.ts +21 -0
- package/src/themes/tooltip.theme.ts +27 -0
- package/src/types/components/alert-dialog.ts +35 -0
- package/src/types/components/alert.ts +31 -0
- package/src/types/components/aspect-ratio.ts +15 -0
- package/src/types/components/avatar.ts +47 -0
- package/src/types/components/badge.ts +27 -0
- package/src/types/components/breadcrumb.ts +24 -0
- package/src/types/components/card.ts +31 -0
- package/src/types/components/carousel.ts +25 -0
- package/src/types/components/chart.ts +38 -0
- package/src/types/components/checkbox.ts +35 -0
- package/src/types/components/collapsible.ts +31 -0
- package/src/types/components/combobox.ts +43 -0
- package/src/types/components/dialog.ts +39 -0
- package/src/types/components/drawer.ts +31 -0
- package/src/types/components/dropdown-menu.ts +42 -0
- package/src/types/components/dropzone.ts +39 -0
- package/src/types/components/field.ts +31 -0
- package/src/types/components/hover-card.ts +19 -0
- package/src/types/components/index.ts +47 -0
- package/src/types/components/input-group.ts +23 -0
- package/src/types/components/input.ts +59 -0
- package/src/types/components/kbd.ts +15 -0
- package/src/types/components/label.ts +19 -0
- package/src/types/components/localeSelector.ts +33 -0
- package/src/types/components/marquee.ts +23 -0
- package/src/types/components/native-select.ts +39 -0
- package/src/types/components/navigation-menu.ts +27 -0
- package/src/types/components/pagination.ts +23 -0
- package/src/types/components/popover.ts +23 -0
- package/src/types/components/progress.ts +27 -0
- package/src/types/components/resizable.ts +19 -0
- package/src/types/components/scroll-area.ts +23 -0
- package/src/types/components/select.ts +43 -0
- package/src/types/components/separator.ts +19 -0
- package/src/types/components/sheet.ts +31 -0
- package/src/types/components/sidebar.ts +36 -0
- package/src/types/components/skeleton.ts +15 -0
- package/src/types/components/slider.ts +35 -0
- package/src/types/components/spinner.ts +19 -0
- package/src/types/components/sticky-surface.ts +19 -0
- package/src/types/components/switch.ts +35 -0
- package/src/types/components/table.ts +31 -0
- package/src/types/components/tabs.ts +43 -0
- package/src/types/components/textarea.ts +51 -0
- package/src/types/components/themeSelector.ts +15 -0
- package/src/types/components/toast.ts +35 -0
- package/src/types/components/toggle-group.ts +23 -0
- package/src/types/components/toggle.ts +27 -0
- package/src/types/components/tooltip.ts +23 -0
- package/src/components/ThemeToggle.astro +0 -57
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { LocaleSelectorProps } from "../../types"
|
|
5
|
+
import localeSelectorTheme from "../../themes/localeSelector.theme"
|
|
6
|
+
import { getRelativeLocaleUrlList, getRelativeLocaleUrl } from "astro:i18n";
|
|
7
|
+
|
|
8
|
+
const localeSelector = scv(localeSelectorTheme)
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
locales,
|
|
12
|
+
variant = "default",
|
|
13
|
+
size = "md",
|
|
14
|
+
ui: uiProp,
|
|
15
|
+
class: className,
|
|
16
|
+
...rest
|
|
17
|
+
} = Astro.props as LocaleSelectorProps;
|
|
18
|
+
|
|
19
|
+
const classes = resolveClasses(localeSelector, {
|
|
20
|
+
variant,
|
|
21
|
+
size
|
|
22
|
+
}, useUi("localeSelector", uiProp), className);
|
|
23
|
+
|
|
24
|
+
const currentLocale = Astro.currentLocale ?? 'en';
|
|
25
|
+
const localeUrls = [...new Map(getRelativeLocaleUrlList().map(url => {
|
|
26
|
+
const locale = url.split('/')[1] || 'en';
|
|
27
|
+
return [locale, url];
|
|
28
|
+
})).values()];
|
|
29
|
+
|
|
30
|
+
const processedLocales = locales && locales.length > 0
|
|
31
|
+
? locales.map(item => typeof item === 'string' ? { code: item, label: item } : item)
|
|
32
|
+
: [...new Map(getRelativeLocaleUrlList().map(url => {
|
|
33
|
+
const locale = url.split('/')[1] || 'en';
|
|
34
|
+
return [locale, { code: locale, label: locale }];
|
|
35
|
+
})).values()];
|
|
36
|
+
|
|
37
|
+
function getLocaleDisplayName(localeCode: string): string {
|
|
38
|
+
try {
|
|
39
|
+
const formatter = new Intl.DisplayNames([localeCode], { type: 'language' });
|
|
40
|
+
const nativeName = formatter.of(localeCode);
|
|
41
|
+
|
|
42
|
+
return nativeName
|
|
43
|
+
? nativeName.charAt(0).toUpperCase() + nativeName.slice(1)
|
|
44
|
+
: localeCode.toUpperCase();
|
|
45
|
+
} catch (e) {
|
|
46
|
+
return localeCode.toUpperCase();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
<div class:list={[classes.root]} {...rest}>
|
|
52
|
+
{Astro.slots.has("label") && (
|
|
53
|
+
<label for="language-picker" class:list={[classes.label]}>
|
|
54
|
+
<slot name="label" />
|
|
55
|
+
</label>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
<div class:list={[classes.wrapper]}>
|
|
59
|
+
<select
|
|
60
|
+
id="language-picker"
|
|
61
|
+
class:list={[classes.select]}
|
|
62
|
+
aria-label="Select language"
|
|
63
|
+
data-variant={variant}
|
|
64
|
+
data-size={size}
|
|
65
|
+
>
|
|
66
|
+
{processedLocales.map((item) => {
|
|
67
|
+
const targetUrl = getRelativeLocaleUrl(item.code, Astro.url.pathname.replace(/^\/[^/]+/, ''));
|
|
68
|
+
return (
|
|
69
|
+
<option
|
|
70
|
+
value={targetUrl}
|
|
71
|
+
selected={currentLocale === item.code}
|
|
72
|
+
class:list={[classes.option]}
|
|
73
|
+
>
|
|
74
|
+
{item.label !== item.code ? item.label : getLocaleDisplayName(item.code)}
|
|
75
|
+
</option>
|
|
76
|
+
);
|
|
77
|
+
})}
|
|
78
|
+
</select>
|
|
79
|
+
|
|
80
|
+
<div class:list={[classes.iconWrapper]}>
|
|
81
|
+
<slot name="icon">
|
|
82
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-4 h-4 opacity-70">
|
|
83
|
+
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
|
|
84
|
+
</svg>
|
|
85
|
+
</slot>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<script>
|
|
91
|
+
const initializeLanguagePicker = () => {
|
|
92
|
+
const languagePickers = document.querySelectorAll('#language-picker');
|
|
93
|
+
|
|
94
|
+
languagePickers.forEach(picker => {
|
|
95
|
+
if (picker.hasAttribute('data-initialized')) return;
|
|
96
|
+
picker.setAttribute('data-initialized', 'true');
|
|
97
|
+
|
|
98
|
+
picker.addEventListener('change', (event) => {
|
|
99
|
+
const target = event.target as HTMLSelectElement;
|
|
100
|
+
const targetUrl = target.value;
|
|
101
|
+
if (targetUrl) {
|
|
102
|
+
window.location.href = targetUrl;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
document.addEventListener('DOMContentLoaded', initializeLanguagePicker);
|
|
109
|
+
document.addEventListener('astro:page-load', initializeLanguagePicker);
|
|
110
|
+
</script>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { MarqueeProps } from "../../types"
|
|
5
|
+
import marqueeTheme from "../../themes/marquee.theme"
|
|
6
|
+
|
|
7
|
+
const marquee = scv(marqueeTheme)
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
direction = "left",
|
|
11
|
+
speed = 40,
|
|
12
|
+
pauseOnHover = true,
|
|
13
|
+
ui: uiProp,
|
|
14
|
+
class: className,
|
|
15
|
+
...rest
|
|
16
|
+
} = Astro.props as MarqueeProps
|
|
17
|
+
|
|
18
|
+
const classes = resolveClasses(marquee, {
|
|
19
|
+
direction,
|
|
20
|
+
pauseOnHover
|
|
21
|
+
}, useUi("marquee", uiProp), className)
|
|
22
|
+
|
|
23
|
+
const duration = `${speed}s`
|
|
24
|
+
---
|
|
25
|
+
<div class={classes.root} data-slot="marquee-root" {...rest}>
|
|
26
|
+
<div class={classes.track} data-slot="track" style={{ animationDuration: duration }}>
|
|
27
|
+
<slot />
|
|
28
|
+
</div>
|
|
29
|
+
<div class={classes.track} data-slot="track" aria-hidden="true" style={{ animationDuration: duration }}>
|
|
30
|
+
<slot />
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { NativeSelectProps } from "../../types"
|
|
5
|
+
import nativeSelectThemeDefault, { createNativeSelectTheme } from "../../themes/nativeSelect.theme"
|
|
6
|
+
|
|
7
|
+
const nativeSelectTheme = nativeSelectThemeDefault
|
|
8
|
+
const nativeSelect = scv(nativeSelectTheme)
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
items = [],
|
|
12
|
+
value = "",
|
|
13
|
+
disabled = false,
|
|
14
|
+
name,
|
|
15
|
+
label,
|
|
16
|
+
size = "md",
|
|
17
|
+
color = "primary",
|
|
18
|
+
ui: uiProp,
|
|
19
|
+
class: className,
|
|
20
|
+
...rest
|
|
21
|
+
} = Astro.props as NativeSelectProps
|
|
22
|
+
|
|
23
|
+
const classes = resolveClasses(nativeSelect, {
|
|
24
|
+
size
|
|
25
|
+
}, useUi("nativeSelect", uiProp), className);
|
|
26
|
+
|
|
27
|
+
const normalizedItems = items.map(item => {
|
|
28
|
+
if (typeof item === "string") {
|
|
29
|
+
return { label: item, value: item }
|
|
30
|
+
}
|
|
31
|
+
return item
|
|
32
|
+
})
|
|
33
|
+
---
|
|
34
|
+
<div class={classes.root} data-slot="root">
|
|
35
|
+
{label && <label class={classes.label} data-slot="label">{label}</label>}
|
|
36
|
+
<div class="relative w-full">
|
|
37
|
+
<select
|
|
38
|
+
class={classes.select}
|
|
39
|
+
disabled={disabled}
|
|
40
|
+
name={name}
|
|
41
|
+
{...rest}
|
|
42
|
+
>
|
|
43
|
+
{normalizedItems.map(item => (
|
|
44
|
+
<option value={item.value} selected={item.value === value}>
|
|
45
|
+
{item.label}
|
|
46
|
+
</option>
|
|
47
|
+
))}
|
|
48
|
+
</select>
|
|
49
|
+
<span class={classes.icon + " i-lucide-chevron-down"} />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { NavigationMenuProps } from "../../types"
|
|
5
|
+
import navigationMenuThemeDefault, { createNavigationMenuTheme } from "../../themes/navigationMenu.theme"
|
|
6
|
+
|
|
7
|
+
const navigationMenuTheme = navigationMenuThemeDefault
|
|
8
|
+
const navigationMenu = scv(navigationMenuTheme)
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
items = [],
|
|
12
|
+
color = "primary",
|
|
13
|
+
ui: uiProp,
|
|
14
|
+
class: className,
|
|
15
|
+
...rest
|
|
16
|
+
} = Astro.props as NavigationMenuProps
|
|
17
|
+
|
|
18
|
+
const classes = resolveClasses(navigationMenu, {}, useUi("navigationMenu", uiProp), className)
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<nav class={classes.root} data-slot="root" data-nav-menu {...rest}>
|
|
22
|
+
<ul class={classes.list} data-slot="list" role="list">
|
|
23
|
+
{items.map((item, i) => {
|
|
24
|
+
const hasChildren = item.children && item.children.length > 0
|
|
25
|
+
return (
|
|
26
|
+
<li class={classes.item} data-slot="item" data-nav-item>
|
|
27
|
+
{hasChildren ? (
|
|
28
|
+
<button
|
|
29
|
+
type="button"
|
|
30
|
+
class={classes.trigger}
|
|
31
|
+
data-slot="trigger"
|
|
32
|
+
data-nav-trigger
|
|
33
|
+
aria-expanded="false"
|
|
34
|
+
aria-haspopup="true"
|
|
35
|
+
>
|
|
36
|
+
{item.icon && <span class={item.icon + " mr-1.5 size-4"} />}
|
|
37
|
+
{item.label}
|
|
38
|
+
<span class="i-lucide-chevron-down ml-1 size-3.5 transition-transform duration-200" aria-hidden="true" />
|
|
39
|
+
</button>
|
|
40
|
+
) : (
|
|
41
|
+
<a
|
|
42
|
+
href={item.href ?? "#"}
|
|
43
|
+
class={classes.link}
|
|
44
|
+
data-slot="link"
|
|
45
|
+
>
|
|
46
|
+
{item.icon && <span class={item.icon + " mr-1.5 size-4"} />}
|
|
47
|
+
{item.label}
|
|
48
|
+
</a>
|
|
49
|
+
)}
|
|
50
|
+
|
|
51
|
+
{hasChildren && (
|
|
52
|
+
<div class={classes.content + " hidden"} data-slot="content" data-nav-content role="region">
|
|
53
|
+
<ul role="list" class="py-1">
|
|
54
|
+
{item.children!.map(child => (
|
|
55
|
+
<li>
|
|
56
|
+
<a
|
|
57
|
+
href={child.href ?? "#"}
|
|
58
|
+
class="flex items-start gap-3 rounded-sm px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground transition-colors"
|
|
59
|
+
>
|
|
60
|
+
{child.icon && <span class={child.icon + " mt-0.5 size-4 shrink-0 text-muted-foreground"} />}
|
|
61
|
+
<div>
|
|
62
|
+
<div class="font-medium leading-none">{child.label}</div>
|
|
63
|
+
{child.description && (
|
|
64
|
+
<p class="mt-1 text-xs text-muted-foreground line-clamp-2">{child.description}</p>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
</a>
|
|
68
|
+
</li>
|
|
69
|
+
))}
|
|
70
|
+
</ul>
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
</li>
|
|
74
|
+
)
|
|
75
|
+
})}
|
|
76
|
+
</ul>
|
|
77
|
+
|
|
78
|
+
<slot />
|
|
79
|
+
|
|
80
|
+
<div class={classes.viewport} data-slot="viewport" />
|
|
81
|
+
</nav>
|
|
82
|
+
|
|
83
|
+
<script>
|
|
84
|
+
document.querySelectorAll("[data-nav-menu]").forEach(menu => {
|
|
85
|
+
const items = menu.querySelectorAll("[data-nav-item]");
|
|
86
|
+
|
|
87
|
+
items.forEach(item => {
|
|
88
|
+
const trigger = item.querySelector("[data-nav-trigger]");
|
|
89
|
+
const content = item.querySelector("[data-nav-content]");
|
|
90
|
+
|
|
91
|
+
if (!trigger || !content) return;
|
|
92
|
+
|
|
93
|
+
const open = () => {
|
|
94
|
+
content.classList.remove("hidden");
|
|
95
|
+
trigger.setAttribute("aria-expanded", "true");
|
|
96
|
+
const icon = trigger.querySelector(".i-lucide-chevron-down");
|
|
97
|
+
if (icon) (icon as HTMLElement).style.transform = "rotate(180deg)";
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const close = () => {
|
|
101
|
+
content.classList.add("hidden");
|
|
102
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
103
|
+
const icon = trigger.querySelector(".i-lucide-chevron-down");
|
|
104
|
+
if (icon) (icon as HTMLElement).style.transform = "";
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
trigger.addEventListener("click", (e) => {
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
const isOpen = !content.classList.contains("hidden");
|
|
110
|
+
// Close all others
|
|
111
|
+
items.forEach(other => {
|
|
112
|
+
const otherContent = other.querySelector("[data-nav-content]");
|
|
113
|
+
const otherTrigger = other.querySelector("[data-nav-trigger]");
|
|
114
|
+
if (otherContent && !otherContent.classList.contains("hidden")) {
|
|
115
|
+
otherContent.classList.add("hidden");
|
|
116
|
+
otherTrigger?.setAttribute("aria-expanded", "false");
|
|
117
|
+
const icon = otherTrigger?.querySelector(".i-lucide-chevron-down");
|
|
118
|
+
if (icon) (icon as HTMLElement).style.transform = "";
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
if (!isOpen) open();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
document.addEventListener("click", () => {
|
|
126
|
+
items.forEach(item => {
|
|
127
|
+
const content = item.querySelector("[data-nav-content]");
|
|
128
|
+
const trigger = item.querySelector("[data-nav-trigger]");
|
|
129
|
+
if (content) content.classList.add("hidden");
|
|
130
|
+
if (trigger) trigger.setAttribute("aria-expanded", "false");
|
|
131
|
+
const icon = trigger?.querySelector(".i-lucide-chevron-down");
|
|
132
|
+
if (icon) (icon as HTMLElement).style.transform = "";
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
</script>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { PaginationProps } from "../../types"
|
|
5
|
+
import paginationTheme from "../../themes/pagination.theme"
|
|
6
|
+
|
|
7
|
+
const pagination = scv(paginationTheme)
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
currentPage = 1,
|
|
11
|
+
totalPages = 1,
|
|
12
|
+
baseUrl = "?page=",
|
|
13
|
+
ui: uiProp,
|
|
14
|
+
class: className,
|
|
15
|
+
...rest
|
|
16
|
+
} = Astro.props as PaginationProps
|
|
17
|
+
|
|
18
|
+
const classes = resolveClasses(pagination, {}, useUi("pagination", uiProp), className)
|
|
19
|
+
|
|
20
|
+
const getPageNumbers = () => {
|
|
21
|
+
const pages: (number | "ellipsis")[] = []
|
|
22
|
+
const maxVisible = 5
|
|
23
|
+
if (totalPages <= maxVisible) {
|
|
24
|
+
for (let i = 1; i <= totalPages; i++) pages.push(i)
|
|
25
|
+
} else {
|
|
26
|
+
pages.push(1)
|
|
27
|
+
if (currentPage > 3) pages.push("ellipsis")
|
|
28
|
+
const start = Math.max(2, currentPage - 1)
|
|
29
|
+
const end = Math.min(totalPages - 1, currentPage + 1)
|
|
30
|
+
for (let i = start; i <= end; i++) {
|
|
31
|
+
if (!pages.includes(i)) pages.push(i)
|
|
32
|
+
}
|
|
33
|
+
if (currentPage < totalPages - 2) pages.push("ellipsis")
|
|
34
|
+
if (!pages.includes(totalPages)) pages.push(totalPages)
|
|
35
|
+
}
|
|
36
|
+
return pages
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const pages = getPageNumbers()
|
|
40
|
+
---
|
|
41
|
+
<nav class={classes.root} data-slot="pagination-root" aria-label="pagination" {...rest}>
|
|
42
|
+
<ul class={classes.list} data-slot="list">
|
|
43
|
+
<li class={classes.item}>
|
|
44
|
+
<a
|
|
45
|
+
href={currentPage > 1 ? `${baseUrl}${currentPage - 1}` : undefined}
|
|
46
|
+
class={classes.link}
|
|
47
|
+
aria-label="Go to previous page"
|
|
48
|
+
aria-disabled={currentPage === 1}
|
|
49
|
+
style={currentPage === 1 ? "pointer-events: none; opacity: 0.5;" : ""}
|
|
50
|
+
>
|
|
51
|
+
<span class="i-lucide-chevron-left size-4" aria-hidden="true" />
|
|
52
|
+
</a>
|
|
53
|
+
</li>
|
|
54
|
+
|
|
55
|
+
{pages.map((p) => {
|
|
56
|
+
if (p === "ellipsis") {
|
|
57
|
+
return (
|
|
58
|
+
<li class={classes.item}>
|
|
59
|
+
<span class={classes.ellipsis} aria-hidden="true">
|
|
60
|
+
…
|
|
61
|
+
</span>
|
|
62
|
+
</li>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const isActive = p === currentPage
|
|
67
|
+
const itemClasses = resolveClasses(pagination, { active: isActive }, useUi("pagination", uiProp))
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<li class={classes.item}>
|
|
71
|
+
<a
|
|
72
|
+
href={`${baseUrl}${p}`}
|
|
73
|
+
class={itemClasses.link}
|
|
74
|
+
aria-current={isActive ? "page" : undefined}
|
|
75
|
+
>
|
|
76
|
+
{p}
|
|
77
|
+
</a>
|
|
78
|
+
</li>
|
|
79
|
+
)
|
|
80
|
+
})}
|
|
81
|
+
|
|
82
|
+
<li class={classes.item}>
|
|
83
|
+
<a
|
|
84
|
+
href={currentPage < totalPages ? `${baseUrl}${currentPage + 1}` : undefined}
|
|
85
|
+
class={classes.link}
|
|
86
|
+
aria-label="Go to next page"
|
|
87
|
+
aria-disabled={currentPage === totalPages}
|
|
88
|
+
style={currentPage === totalPages ? "pointer-events: none; opacity: 0.5;" : ""}
|
|
89
|
+
>
|
|
90
|
+
<span class="i-lucide-chevron-right size-4" aria-hidden="true" />
|
|
91
|
+
</a>
|
|
92
|
+
</li>
|
|
93
|
+
</ul>
|
|
94
|
+
</nav>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { PopoverProps } from "../../types"
|
|
5
|
+
import popoverTheme from "../../themes/popover.theme"
|
|
6
|
+
|
|
7
|
+
const popover = scv(popoverTheme)
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
triggerLabel,
|
|
11
|
+
placement = "bottom",
|
|
12
|
+
ui: uiProp,
|
|
13
|
+
class: className,
|
|
14
|
+
...rest
|
|
15
|
+
} = Astro.props as PopoverProps
|
|
16
|
+
|
|
17
|
+
const classes = resolveClasses(popover, {
|
|
18
|
+
placement
|
|
19
|
+
}, useUi("popover", uiProp), className)
|
|
20
|
+
---
|
|
21
|
+
<div class={classes.root} data-slot="popover-container" {...rest}>
|
|
22
|
+
<div class={classes.trigger} data-slot="trigger" tabindex="0">
|
|
23
|
+
<slot name="trigger">
|
|
24
|
+
{triggerLabel && <button type="button" class="cursor-pointer">{triggerLabel}</button>}
|
|
25
|
+
</slot>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class={classes.content} data-slot="content">
|
|
29
|
+
<slot />
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { ProgressProps } from "../../types"
|
|
5
|
+
import progressThemeDefault, { createProgressTheme } from "../../themes/progress.theme"
|
|
6
|
+
import { getUIConfig } from "virtual:rimelight-ui"
|
|
7
|
+
|
|
8
|
+
const uiConfig = getUIConfig()
|
|
9
|
+
const progressTheme = uiConfig.colors ? createProgressTheme(uiConfig.colors) : progressThemeDefault
|
|
10
|
+
const progress = scv(progressTheme)
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
value = 0,
|
|
14
|
+
max = 100,
|
|
15
|
+
color = "primary",
|
|
16
|
+
size = "md",
|
|
17
|
+
ui: uiProp,
|
|
18
|
+
class: className,
|
|
19
|
+
...rest
|
|
20
|
+
} = Astro.props as ProgressProps
|
|
21
|
+
|
|
22
|
+
const classes = resolveClasses(progress, {
|
|
23
|
+
color,
|
|
24
|
+
size
|
|
25
|
+
}, useUi("progress", uiProp), className)
|
|
26
|
+
|
|
27
|
+
const percentage = Math.min(100, Math.max(0, (value / max) * 100))
|
|
28
|
+
---
|
|
29
|
+
<div
|
|
30
|
+
class={classes.root}
|
|
31
|
+
data-slot="progress-root"
|
|
32
|
+
role="progressbar"
|
|
33
|
+
aria-valuemin={0}
|
|
34
|
+
aria-valuemax={max}
|
|
35
|
+
aria-valuenow={value}
|
|
36
|
+
{...rest}
|
|
37
|
+
>
|
|
38
|
+
<div
|
|
39
|
+
class={classes.indicator}
|
|
40
|
+
data-slot="indicator"
|
|
41
|
+
style={{ transform: `translateX(-${100 - percentage}%)` }}
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { ResizableProps } from "../../types"
|
|
5
|
+
import resizableThemeDefault, { createResizableTheme } from "../../themes/resizable.theme"
|
|
6
|
+
|
|
7
|
+
const resizableTheme = resizableThemeDefault
|
|
8
|
+
const resizable = scv(resizableTheme)
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
direction = "horizontal",
|
|
12
|
+
defaultSizes = [50, 50],
|
|
13
|
+
ui: uiProp,
|
|
14
|
+
class: className,
|
|
15
|
+
...rest
|
|
16
|
+
} = Astro.props as ResizableProps
|
|
17
|
+
|
|
18
|
+
const classes = resolveClasses(resizable, {
|
|
19
|
+
direction
|
|
20
|
+
}, useUi("resizable", uiProp), className)
|
|
21
|
+
|
|
22
|
+
// Normalise sizes so they sum to 100
|
|
23
|
+
const total = defaultSizes.reduce((s, v) => s + v, 0)
|
|
24
|
+
const sizes = defaultSizes.map(s => (s / (total || 100)) * 100)
|
|
25
|
+
|
|
26
|
+
const panelCount = sizes.length
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<div
|
|
30
|
+
class={classes.root}
|
|
31
|
+
data-slot="root"
|
|
32
|
+
data-resizable
|
|
33
|
+
data-direction={direction}
|
|
34
|
+
{...rest}
|
|
35
|
+
>
|
|
36
|
+
{sizes.map((size, i) => (
|
|
37
|
+
<>
|
|
38
|
+
<div
|
|
39
|
+
class={classes.panel}
|
|
40
|
+
data-slot="panel"
|
|
41
|
+
data-resizable-panel={i}
|
|
42
|
+
style={direction === "horizontal" ? `width:${size}%` : `height:${size}%`}
|
|
43
|
+
>
|
|
44
|
+
<slot name={`panel-${i}`}>
|
|
45
|
+
{i === 0
|
|
46
|
+
? <slot />
|
|
47
|
+
: <slot name="panel" />
|
|
48
|
+
}
|
|
49
|
+
</slot>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{/* Render a handle between panels, not after the last one */}
|
|
53
|
+
{i < panelCount - 1 && (
|
|
54
|
+
<div
|
|
55
|
+
class={classes.handle}
|
|
56
|
+
data-slot="handle"
|
|
57
|
+
data-resizable-handle={i}
|
|
58
|
+
role="separator"
|
|
59
|
+
aria-orientation={direction === "horizontal" ? "vertical" : "horizontal"}
|
|
60
|
+
tabindex={0}
|
|
61
|
+
aria-label="Resize panels"
|
|
62
|
+
>
|
|
63
|
+
<span
|
|
64
|
+
class={
|
|
65
|
+
direction === "horizontal"
|
|
66
|
+
? "i-lucide-grip-vertical size-3.5 text-muted-foreground/50"
|
|
67
|
+
: "i-lucide-grip-horizontal size-3.5 text-muted-foreground/50"
|
|
68
|
+
}
|
|
69
|
+
aria-hidden="true"
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
</>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<script>
|
|
78
|
+
document.querySelectorAll("[data-resizable]").forEach(container => {
|
|
79
|
+
const direction = (container as HTMLElement).dataset.direction ?? "horizontal";
|
|
80
|
+
const isHorizontal = direction === "horizontal";
|
|
81
|
+
|
|
82
|
+
const panels = Array.from(
|
|
83
|
+
container.querySelectorAll("[data-resizable-panel]")
|
|
84
|
+
) as HTMLElement[];
|
|
85
|
+
|
|
86
|
+
const handles = Array.from(
|
|
87
|
+
container.querySelectorAll("[data-resizable-handle]")
|
|
88
|
+
) as HTMLElement[];
|
|
89
|
+
|
|
90
|
+
handles.forEach((handle, idx) => {
|
|
91
|
+
const panelA = panels[idx];
|
|
92
|
+
const panelB = panels[idx + 1];
|
|
93
|
+
if (!panelA || !panelB) return;
|
|
94
|
+
|
|
95
|
+
const containerSize = () =>
|
|
96
|
+
isHorizontal
|
|
97
|
+
? (container as HTMLElement).clientWidth
|
|
98
|
+
: (container as HTMLElement).clientHeight;
|
|
99
|
+
|
|
100
|
+
let isDragging = false;
|
|
101
|
+
let startPos = 0;
|
|
102
|
+
let startASize = 0;
|
|
103
|
+
let startBSize = 0;
|
|
104
|
+
|
|
105
|
+
const getSize = (el: HTMLElement) =>
|
|
106
|
+
isHorizontal ? el.offsetWidth : el.offsetHeight;
|
|
107
|
+
|
|
108
|
+
const setSize = (el: HTMLElement, px: number) => {
|
|
109
|
+
const pct = (px / containerSize()) * 100;
|
|
110
|
+
if (isHorizontal) {
|
|
111
|
+
el.style.width = `${pct}%`;
|
|
112
|
+
} else {
|
|
113
|
+
el.style.height = `${pct}%`;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const onMouseMove = (e: MouseEvent) => {
|
|
118
|
+
if (!isDragging) return;
|
|
119
|
+
const delta = isHorizontal
|
|
120
|
+
? e.clientX - startPos
|
|
121
|
+
: e.clientY - startPos;
|
|
122
|
+
|
|
123
|
+
const newASize = Math.max(48, startASize + delta);
|
|
124
|
+
const newBSize = Math.max(48, startBSize - delta);
|
|
125
|
+
|
|
126
|
+
// Only resize if both panels have enough space
|
|
127
|
+
if (startASize + startBSize - newASize < 48) return;
|
|
128
|
+
|
|
129
|
+
setSize(panelA, newASize);
|
|
130
|
+
setSize(panelB, newBSize);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const onMouseUp = () => {
|
|
134
|
+
isDragging = false;
|
|
135
|
+
document.body.style.cursor = "";
|
|
136
|
+
document.body.style.userSelect = "";
|
|
137
|
+
handle.classList.remove("bg-primary/30");
|
|
138
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
139
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
handle.addEventListener("mousedown", (e) => {
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
isDragging = true;
|
|
145
|
+
startPos = isHorizontal ? e.clientX : e.clientY;
|
|
146
|
+
startASize = getSize(panelA);
|
|
147
|
+
startBSize = getSize(panelB);
|
|
148
|
+
document.body.style.cursor = isHorizontal ? "col-resize" : "row-resize";
|
|
149
|
+
document.body.style.userSelect = "none";
|
|
150
|
+
handle.classList.add("bg-primary/30");
|
|
151
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
152
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Keyboard resize: arrow keys shift by 2%
|
|
156
|
+
handle.addEventListener("keydown", (e) => {
|
|
157
|
+
const step = containerSize() * 0.02;
|
|
158
|
+
const forward = isHorizontal
|
|
159
|
+
? e.key === "ArrowRight"
|
|
160
|
+
: e.key === "ArrowDown";
|
|
161
|
+
const backward = isHorizontal
|
|
162
|
+
? e.key === "ArrowLeft"
|
|
163
|
+
: e.key === "ArrowUp";
|
|
164
|
+
|
|
165
|
+
if (!forward && !backward) return;
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
|
|
168
|
+
const aSize = getSize(panelA);
|
|
169
|
+
const bSize = getSize(panelB);
|
|
170
|
+
const delta = forward ? step : -step;
|
|
171
|
+
|
|
172
|
+
setSize(panelA, Math.max(48, aSize + delta));
|
|
173
|
+
setSize(panelB, Math.max(48, bSize - delta));
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
</script>
|