@rimelight/ui 0.0.29 → 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/RLAMain.astro +28 -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/RLASection.astro +0 -1
- 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/main.theme.ts +12 -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 +51 -3
- 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/main.ts +17 -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,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>
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { ScrollAreaProps } from "../../types"
|
|
5
|
+
import scrollAreaThemeDefault, { createScrollAreaTheme } from "../../themes/scrollArea.theme"
|
|
6
|
+
|
|
7
|
+
const scrollAreaTheme = scrollAreaThemeDefault
|
|
8
|
+
const scrollArea = scv(scrollAreaTheme)
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
orientation = "vertical",
|
|
12
|
+
scrollHideDelay = 600,
|
|
13
|
+
height,
|
|
14
|
+
ui: uiProp,
|
|
15
|
+
class: className,
|
|
16
|
+
...rest
|
|
17
|
+
} = Astro.props as ScrollAreaProps
|
|
18
|
+
|
|
19
|
+
const classes = resolveClasses(scrollArea, {
|
|
20
|
+
orientation
|
|
21
|
+
}, useUi("scrollArea", uiProp), className)
|
|
22
|
+
|
|
23
|
+
const heightStyle = height
|
|
24
|
+
? `height:${typeof height === "number" ? `${height}px` : height}`
|
|
25
|
+
: undefined
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<div
|
|
29
|
+
class={classes.root}
|
|
30
|
+
data-slot="root"
|
|
31
|
+
data-scroll-area
|
|
32
|
+
data-orientation={orientation}
|
|
33
|
+
data-hide-delay={scrollHideDelay}
|
|
34
|
+
style={heightStyle}
|
|
35
|
+
{...rest}
|
|
36
|
+
>
|
|
37
|
+
{/* Scrollable viewport */}
|
|
38
|
+
<div
|
|
39
|
+
class={classes.viewport}
|
|
40
|
+
data-slot="viewport"
|
|
41
|
+
data-scroll-viewport
|
|
42
|
+
>
|
|
43
|
+
<slot />
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{/* Vertical scrollbar */}
|
|
47
|
+
{(orientation === "vertical" || orientation === "both") && (
|
|
48
|
+
<div
|
|
49
|
+
class={classes.scrollbar + " opacity-0 transition-opacity"}
|
|
50
|
+
data-slot="scrollbar"
|
|
51
|
+
data-scroll-bar="vertical"
|
|
52
|
+
aria-hidden="true"
|
|
53
|
+
>
|
|
54
|
+
<div class={classes.thumb} data-scroll-thumb="vertical" />
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
{/* Horizontal scrollbar */}
|
|
59
|
+
{(orientation === "horizontal" || orientation === "both") && (
|
|
60
|
+
<div
|
|
61
|
+
class={"absolute bottom-0 left-0 right-0 h-2.5 flex-row flex p-0.5 bg-transparent transition-colors duration-150 ease-out hover:bg-muted/10 opacity-0 transition-opacity"}
|
|
62
|
+
data-slot="scrollbar"
|
|
63
|
+
data-scroll-bar="horizontal"
|
|
64
|
+
aria-hidden="true"
|
|
65
|
+
>
|
|
66
|
+
<div class={classes.thumb} data-scroll-thumb="horizontal" />
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<script>
|
|
72
|
+
document.querySelectorAll("[data-scroll-area]").forEach(area => {
|
|
73
|
+
const viewport = area.querySelector("[data-scroll-viewport]") as HTMLElement | null;
|
|
74
|
+
if (!viewport) return;
|
|
75
|
+
|
|
76
|
+
const hideDelay = parseInt((area as HTMLElement).dataset.hideDelay ?? "600", 10);
|
|
77
|
+
|
|
78
|
+
const vBar = area.querySelector("[data-scroll-bar='vertical']") as HTMLElement | null;
|
|
79
|
+
const hBar = area.querySelector("[data-scroll-bar='horizontal']") as HTMLElement | null;
|
|
80
|
+
const vThumb = area.querySelector("[data-scroll-thumb='vertical']") as HTMLElement | null;
|
|
81
|
+
const hThumb = area.querySelector("[data-scroll-thumb='horizontal']") as HTMLElement | null;
|
|
82
|
+
|
|
83
|
+
let hideTimer: ReturnType<typeof setTimeout>;
|
|
84
|
+
|
|
85
|
+
const showBars = () => {
|
|
86
|
+
vBar?.classList.replace("opacity-0", "opacity-100");
|
|
87
|
+
hBar?.classList.replace("opacity-0", "opacity-100");
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const hideBars = () => {
|
|
91
|
+
hideTimer = setTimeout(() => {
|
|
92
|
+
vBar?.classList.replace("opacity-100", "opacity-0");
|
|
93
|
+
hBar?.classList.replace("opacity-100", "opacity-0");
|
|
94
|
+
}, hideDelay);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const updateThumbs = () => {
|
|
98
|
+
if (vThumb) {
|
|
99
|
+
const ratio = viewport.clientHeight / viewport.scrollHeight;
|
|
100
|
+
const thumbH = Math.max(ratio * (vBar?.clientHeight ?? 0), 20);
|
|
101
|
+
const thumbTop = (viewport.scrollTop / viewport.scrollHeight) * (vBar?.clientHeight ?? 0);
|
|
102
|
+
vThumb.style.height = `${thumbH}px`;
|
|
103
|
+
vThumb.style.transform = `translateY(${thumbTop}px)`;
|
|
104
|
+
}
|
|
105
|
+
if (hThumb) {
|
|
106
|
+
const ratio = viewport.clientWidth / viewport.scrollWidth;
|
|
107
|
+
const thumbW = Math.max(ratio * (hBar?.clientWidth ?? 0), 20);
|
|
108
|
+
const thumbLeft = (viewport.scrollLeft / viewport.scrollWidth) * (hBar?.clientWidth ?? 0);
|
|
109
|
+
hThumb.style.width = `${thumbW}px`;
|
|
110
|
+
hThumb.style.transform = `translateX(${thumbLeft}px)`;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
viewport.addEventListener("scroll", () => {
|
|
115
|
+
clearTimeout(hideTimer);
|
|
116
|
+
showBars();
|
|
117
|
+
updateThumbs();
|
|
118
|
+
hideBars();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Drag-to-scroll for custom thumbs
|
|
122
|
+
const makeDraggable = (
|
|
123
|
+
thumb: HTMLElement | null,
|
|
124
|
+
scrollProp: "scrollTop" | "scrollLeft",
|
|
125
|
+
clientProp: "clientY" | "clientX",
|
|
126
|
+
sizeProp: "scrollHeight" | "scrollWidth",
|
|
127
|
+
clientSizeProp: "clientHeight" | "clientWidth"
|
|
128
|
+
) => {
|
|
129
|
+
if (!thumb) return;
|
|
130
|
+
let startPos = 0;
|
|
131
|
+
let startScroll = 0;
|
|
132
|
+
|
|
133
|
+
thumb.addEventListener("mousedown", (e) => {
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
startPos = (e as MouseEvent)[clientProp];
|
|
136
|
+
startScroll = viewport[scrollProp];
|
|
137
|
+
|
|
138
|
+
const onMove = (ev: MouseEvent) => {
|
|
139
|
+
const delta = ev[clientProp] - startPos;
|
|
140
|
+
const ratio = viewport[sizeProp] / viewport[clientSizeProp];
|
|
141
|
+
viewport[scrollProp] = startScroll + delta * ratio;
|
|
142
|
+
};
|
|
143
|
+
const onUp = () => {
|
|
144
|
+
document.removeEventListener("mousemove", onMove);
|
|
145
|
+
document.removeEventListener("mouseup", onUp);
|
|
146
|
+
};
|
|
147
|
+
document.addEventListener("mousemove", onMove);
|
|
148
|
+
document.addEventListener("mouseup", onUp);
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
makeDraggable(vThumb, "scrollTop", "clientY", "scrollHeight", "clientHeight");
|
|
153
|
+
makeDraggable(hThumb, "scrollLeft", "clientX", "scrollWidth", "clientWidth");
|
|
154
|
+
|
|
155
|
+
updateThumbs();
|
|
156
|
+
});
|
|
157
|
+
</script>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { SelectProps } from "../../types"
|
|
5
|
+
import selectThemeDefault, { createSelectTheme } from "../../themes/select.theme"
|
|
6
|
+
|
|
7
|
+
const selectTheme = selectThemeDefault
|
|
8
|
+
const select = scv(selectTheme)
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
items = [],
|
|
12
|
+
value = "",
|
|
13
|
+
placeholder = "Select item...",
|
|
14
|
+
disabled = false,
|
|
15
|
+
name,
|
|
16
|
+
label,
|
|
17
|
+
size = "md",
|
|
18
|
+
color = "primary",
|
|
19
|
+
ui: uiProp,
|
|
20
|
+
class: className,
|
|
21
|
+
...rest
|
|
22
|
+
} = Astro.props as SelectProps
|
|
23
|
+
|
|
24
|
+
const classes = resolveClasses(select, {
|
|
25
|
+
size
|
|
26
|
+
}, useUi("select", uiProp), className);
|
|
27
|
+
|
|
28
|
+
const normalizedItems = items.map(item => {
|
|
29
|
+
if (typeof item === "string") {
|
|
30
|
+
return { label: item, value: item }
|
|
31
|
+
}
|
|
32
|
+
return item
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const selectedItem = normalizedItems.find(item => item.value === value)
|
|
36
|
+
const initialLabel = selectedItem ? selectedItem.label : ""
|
|
37
|
+
---
|
|
38
|
+
<div class={classes.root} data-slot="root" data-disabled={disabled ? "" : undefined}>
|
|
39
|
+
{label && <label class={classes.label} data-slot="label">{label}</label>}
|
|
40
|
+
|
|
41
|
+
<div class="relative w-full" data-select-wrapper>
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
class={classes.trigger}
|
|
45
|
+
data-slot="trigger"
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
data-select-trigger
|
|
48
|
+
{...rest}
|
|
49
|
+
>
|
|
50
|
+
<span data-select-value>{initialLabel || placeholder}</span>
|
|
51
|
+
<span class="i-lucide-chevron-down opacity-50" />
|
|
52
|
+
</button>
|
|
53
|
+
|
|
54
|
+
<div class={classes.content + " hidden"} data-select-dropdown data-slot="content">
|
|
55
|
+
<div class="py-1" data-select-items>
|
|
56
|
+
{normalizedItems.map(item => (
|
|
57
|
+
<div
|
|
58
|
+
class={classes.item}
|
|
59
|
+
data-slot="item"
|
|
60
|
+
data-value={item.value}
|
|
61
|
+
data-label={item.label}
|
|
62
|
+
data-select-item
|
|
63
|
+
>
|
|
64
|
+
<span class={classes.itemText}>{item.label}</span>
|
|
65
|
+
<span class={classes.itemIcon + " hidden i-lucide-check"} data-check-icon />
|
|
66
|
+
</div>
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<input type="hidden" name={name} value={value} data-select-hidden-input />
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<script>
|
|
75
|
+
document.querySelectorAll("[data-select-wrapper]").forEach(wrapper => {
|
|
76
|
+
const trigger = wrapper.querySelector("[data-select-trigger]") as HTMLButtonElement;
|
|
77
|
+
const dropdown = wrapper.querySelector("[data-select-dropdown]") as HTMLDivElement;
|
|
78
|
+
const items = wrapper.querySelectorAll("[data-select-item]");
|
|
79
|
+
const valueSpan = wrapper.querySelector("[data-select-value]");
|
|
80
|
+
const hiddenInput = wrapper.closest("[data-slot=root]")?.querySelector("[data-select-hidden-input]") as HTMLInputElement;
|
|
81
|
+
|
|
82
|
+
if (!trigger || !dropdown) return;
|
|
83
|
+
|
|
84
|
+
trigger.addEventListener("click", (e) => {
|
|
85
|
+
e.stopPropagation();
|
|
86
|
+
dropdown.classList.toggle("hidden");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
items.forEach(item => {
|
|
90
|
+
item.addEventListener("click", (e) => {
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
const val = (item as HTMLElement).dataset.value || "";
|
|
93
|
+
const label = (item as HTMLElement).dataset.label || "";
|
|
94
|
+
|
|
95
|
+
if (hiddenInput) hiddenInput.value = val;
|
|
96
|
+
if (valueSpan) valueSpan.textContent = label;
|
|
97
|
+
|
|
98
|
+
items.forEach(i => i.querySelector("[data-check-icon]")?.classList.add("hidden"));
|
|
99
|
+
item.querySelector("[data-check-icon]")?.classList.remove("hidden");
|
|
100
|
+
|
|
101
|
+
dropdown.classList.add("hidden");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
document.addEventListener("click", () => {
|
|
106
|
+
dropdown.classList.add("hidden");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
</script>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { SeparatorProps } from "../../types"
|
|
5
|
+
import separatorTheme from "../../themes/separator.theme"
|
|
6
|
+
|
|
7
|
+
const separator = scv(separatorTheme)
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
orientation = "horizontal",
|
|
11
|
+
decorative = true,
|
|
12
|
+
ui: uiProp,
|
|
13
|
+
class: className,
|
|
14
|
+
...rest
|
|
15
|
+
} = Astro.props as SeparatorProps
|
|
16
|
+
|
|
17
|
+
const classes = resolveClasses(separator, {
|
|
18
|
+
orientation
|
|
19
|
+
}, useUi("separator", uiProp), className)
|
|
20
|
+
---
|
|
21
|
+
<div
|
|
22
|
+
class={classes.root}
|
|
23
|
+
data-slot="root"
|
|
24
|
+
role={decorative ? "none" : "separator"}
|
|
25
|
+
aria-orientation={decorative ? undefined : orientation}
|
|
26
|
+
{...rest}
|
|
27
|
+
/>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { SheetProps } from "../../types"
|
|
5
|
+
import sheetTheme from "../../themes/sheet.theme"
|
|
6
|
+
|
|
7
|
+
const sheet = scv(sheetTheme)
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
side = "right",
|
|
11
|
+
title,
|
|
12
|
+
description,
|
|
13
|
+
triggerLabel,
|
|
14
|
+
ui: uiProp,
|
|
15
|
+
class: className,
|
|
16
|
+
...rest
|
|
17
|
+
} = Astro.props as SheetProps
|
|
18
|
+
|
|
19
|
+
const classes = resolveClasses(sheet, {
|
|
20
|
+
side
|
|
21
|
+
}, useUi("sheet", uiProp), className)
|
|
22
|
+
|
|
23
|
+
const dialogId = `rla-sheet-${Math.random().toString(36).substring(2, 9)}`
|
|
24
|
+
---
|
|
25
|
+
<div class={classes.root} data-slot="sheet-container">
|
|
26
|
+
{triggerLabel ? (
|
|
27
|
+
<button
|
|
28
|
+
class={classes.trigger}
|
|
29
|
+
data-slot="trigger"
|
|
30
|
+
type="button"
|
|
31
|
+
onclick={`document.getElementById('${dialogId}').showModal()`}
|
|
32
|
+
>
|
|
33
|
+
{triggerLabel}
|
|
34
|
+
</button>
|
|
35
|
+
) : (
|
|
36
|
+
<span onclick={`document.getElementById('${dialogId}').showModal()`}>
|
|
37
|
+
<slot name="trigger" />
|
|
38
|
+
</span>
|
|
39
|
+
)}
|
|
40
|
+
|
|
41
|
+
<dialog
|
|
42
|
+
id={dialogId}
|
|
43
|
+
class={classes.content}
|
|
44
|
+
data-slot="content"
|
|
45
|
+
data-close-on-click-outside="true"
|
|
46
|
+
{...rest}
|
|
47
|
+
>
|
|
48
|
+
<button
|
|
49
|
+
class={classes.closeButton}
|
|
50
|
+
data-slot="close-button"
|
|
51
|
+
type="button"
|
|
52
|
+
onclick={`document.getElementById('${dialogId}').close()`}
|
|
53
|
+
aria-label="Close sheet"
|
|
54
|
+
>
|
|
55
|
+
<span class="i-lucide-x size-4" aria-hidden="true" />
|
|
56
|
+
</button>
|
|
57
|
+
|
|
58
|
+
<div class={classes.header} data-slot="header">
|
|
59
|
+
{title && <h3 class={classes.title} data-slot="title">{title}</h3>}
|
|
60
|
+
<slot name="title" />
|
|
61
|
+
|
|
62
|
+
{description && <p class={classes.description} data-slot="description">{description}</p>}
|
|
63
|
+
<slot name="description" />
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<slot />
|
|
67
|
+
|
|
68
|
+
<div class={classes.footer} data-slot="footer">
|
|
69
|
+
<slot name="footer" />
|
|
70
|
+
</div>
|
|
71
|
+
</dialog>
|
|
72
|
+
</div>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { scv } from "css-variants"
|
|
3
|
+
import { useUi, resolveClasses } from "../../utils"
|
|
4
|
+
import type { SidebarProps, SidebarItem } from "../../types"
|
|
5
|
+
import sidebarThemeDefault, { createSidebarTheme } from "../../themes/sidebar.theme"
|
|
6
|
+
|
|
7
|
+
const sidebarTheme = sidebarThemeDefault
|
|
8
|
+
const sidebar = scv(sidebarTheme)
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
items = [],
|
|
12
|
+
side = "left",
|
|
13
|
+
collapsible = "none",
|
|
14
|
+
logo,
|
|
15
|
+
ui: uiProp,
|
|
16
|
+
class: className,
|
|
17
|
+
...rest
|
|
18
|
+
} = Astro.props as SidebarProps
|
|
19
|
+
|
|
20
|
+
const classes = resolveClasses(sidebar, {
|
|
21
|
+
side,
|
|
22
|
+
collapsible
|
|
23
|
+
}, useUi("sidebar", uiProp), className)
|
|
24
|
+
|
|
25
|
+
// Recursive helper to render sidebar items with children
|
|
26
|
+
const hasActiveChild = (item: SidebarItem): boolean => {
|
|
27
|
+
if (item.active) return true
|
|
28
|
+
return (item.children ?? []).some(c => hasActiveChild(c))
|
|
29
|
+
}
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<aside class={classes.root} data-slot="root" data-sidebar {...rest}>
|
|
33
|
+
{/* Header */}
|
|
34
|
+
<div class={classes.header} data-slot="header">
|
|
35
|
+
<slot name="header">
|
|
36
|
+
{logo && (
|
|
37
|
+
<span data-slot="sidebar-logo" class="flex items-center gap-2">
|
|
38
|
+
<span class="i-lucide-layers size-5 text-primary" />
|
|
39
|
+
<span class="truncate">{logo}</span>
|
|
40
|
+
</span>
|
|
41
|
+
)}
|
|
42
|
+
</slot>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
{/* Body */}
|
|
46
|
+
<div class={classes.content} data-slot="content">
|
|
47
|
+
<slot>
|
|
48
|
+
{items.map((item) => {
|
|
49
|
+
const hasChildren = item.children && item.children.length > 0
|
|
50
|
+
const isActive = item.active || hasActiveChild(item)
|
|
51
|
+
return (
|
|
52
|
+
<div class={classes.item} data-slot="item">
|
|
53
|
+
{hasChildren ? (
|
|
54
|
+
<details open={isActive} class="group/details">
|
|
55
|
+
<summary
|
|
56
|
+
class={classes.itemLink + (isActive ? " text-foreground bg-muted" : "") + " list-none cursor-pointer"}
|
|
57
|
+
data-slot="item-summary"
|
|
58
|
+
>
|
|
59
|
+
{item.icon && (
|
|
60
|
+
<span
|
|
61
|
+
class={classes.itemIcon + " " + item.icon}
|
|
62
|
+
aria-hidden="true"
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
<span data-slot="item-text" class="truncate flex-1">{item.label}</span>
|
|
66
|
+
{item.badge !== undefined && (
|
|
67
|
+
<span class={classes.itemBadge} data-slot="item-badge">{item.badge}</span>
|
|
68
|
+
)}
|
|
69
|
+
<span class="i-lucide-chevron-right size-3.5 ml-auto transition-transform group-open/details:rotate-90" />
|
|
70
|
+
</summary>
|
|
71
|
+
<div class="mt-0.5 ml-6 flex flex-col gap-0.5">
|
|
72
|
+
{item.children!.map(child => (
|
|
73
|
+
<a
|
|
74
|
+
href={child.href ?? "#"}
|
|
75
|
+
class={classes.itemLink + (child.active ? " text-foreground bg-muted" : "") + " text-xs pl-2"}
|
|
76
|
+
>
|
|
77
|
+
{child.icon && <span class={classes.itemIcon + " " + child.icon} aria-hidden="true" />}
|
|
78
|
+
<span data-slot="item-text" class="truncate flex-1">{child.label}</span>
|
|
79
|
+
{child.badge !== undefined && (
|
|
80
|
+
<span class={classes.itemBadge} data-slot="item-badge">{child.badge}</span>
|
|
81
|
+
)}
|
|
82
|
+
</a>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
</details>
|
|
86
|
+
) : (
|
|
87
|
+
<a
|
|
88
|
+
href={item.href ?? "#"}
|
|
89
|
+
class={classes.itemLink + (isActive ? " text-foreground bg-muted" : "")}
|
|
90
|
+
aria-current={isActive ? "page" : undefined}
|
|
91
|
+
>
|
|
92
|
+
{item.icon && (
|
|
93
|
+
<span
|
|
94
|
+
class={classes.itemIcon + " " + item.icon}
|
|
95
|
+
aria-hidden="true"
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
98
|
+
<span data-slot="item-text" class="truncate flex-1">{item.label}</span>
|
|
99
|
+
{item.badge !== undefined && (
|
|
100
|
+
<span class={classes.itemBadge} data-slot="item-badge">{item.badge}</span>
|
|
101
|
+
)}
|
|
102
|
+
</a>
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
})}
|
|
107
|
+
</slot>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{/* Footer */}
|
|
111
|
+
<div class={classes.footer} data-slot="footer">
|
|
112
|
+
<slot name="footer" />
|
|
113
|
+
</div>
|
|
114
|
+
</aside>
|
|
115
|
+
|
|
116
|
+
{collapsible === "offcanvas" && (
|
|
117
|
+
<button
|
|
118
|
+
type="button"
|
|
119
|
+
data-sidebar-toggle
|
|
120
|
+
class="fixed top-4 left-4 z-50 flex size-9 items-center justify-center rounded-md bg-background border border-border shadow-sm hover:bg-muted transition-colors"
|
|
121
|
+
aria-label="Toggle sidebar"
|
|
122
|
+
>
|
|
123
|
+
<span class="i-lucide-panel-left size-4" />
|
|
124
|
+
</button>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
<script>
|
|
128
|
+
document.querySelectorAll("[data-sidebar-toggle]").forEach(toggle => {
|
|
129
|
+
toggle.addEventListener("click", () => {
|
|
130
|
+
const sidebar = document.querySelector("[data-sidebar]");
|
|
131
|
+
if (!sidebar) return;
|
|
132
|
+
sidebar.classList.toggle("-translate-x-full");
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
</script>
|