@rovula/ui 0.0.69 → 0.0.70
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/dist/cjs/bundle.css +23 -0
- package/dist/cjs/bundle.js +1 -1
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/components/FocusedScrollView/FocusedScrollView.d.ts +12 -0
- package/dist/cjs/types/components/FocusedScrollView/FocusedScrollView.stories.d.ts +7 -0
- package/dist/cjs/types/index.d.ts +1 -0
- package/dist/components/FocusedScrollView/FocusedScrollView.js +67 -0
- package/dist/components/FocusedScrollView/FocusedScrollView.stories.js +33 -0
- package/dist/esm/bundle.css +23 -0
- package/dist/esm/bundle.js +3 -3
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/components/FocusedScrollView/FocusedScrollView.d.ts +12 -0
- package/dist/esm/types/components/FocusedScrollView/FocusedScrollView.stories.d.ts +7 -0
- package/dist/esm/types/index.d.ts +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.js +1 -0
- package/dist/src/theme/global.css +30 -0
- package/package.json +1 -1
- package/src/components/FocusedScrollView/FocusedScrollView.stories.tsx +114 -0
- package/src/components/FocusedScrollView/FocusedScrollView.tsx +112 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React, { ReactNode, HTMLAttributes } from "react";
|
|
2
|
+
type ScrollAlign = "start" | "center" | "end";
|
|
3
|
+
type FocusedScrollViewProps = {
|
|
4
|
+
selectedKey?: string;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
direction?: "vertical" | "horizontal";
|
|
7
|
+
className?: string;
|
|
8
|
+
containerProps?: HTMLAttributes<HTMLDivElement>;
|
|
9
|
+
scrollAlign?: ScrollAlign;
|
|
10
|
+
};
|
|
11
|
+
export declare const FocusedScrollView: React.FC<FocusedScrollViewProps>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { FocusedScrollView } from "./FocusedScrollView";
|
|
3
|
+
declare const meta: Meta<typeof FocusedScrollView>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof FocusedScrollView>;
|
|
6
|
+
export declare const VerticalScroll: Story;
|
|
7
|
+
export declare const HorizontalScroll: Story;
|
|
@@ -33,6 +33,7 @@ export * from "./components/Toast/Toast";
|
|
|
33
33
|
export * from "./components/Toast/Toaster";
|
|
34
34
|
export * from "./components/Toast/useToast";
|
|
35
35
|
export * from "./components/Tree";
|
|
36
|
+
export * from "./components/FocusedScrollView/FocusedScrollView";
|
|
36
37
|
export type { ButtonProps } from "./components/Button/Button";
|
|
37
38
|
export type { InputProps } from "./components/TextInput/TextInput";
|
|
38
39
|
export type { DropdownProps, Options } from "./components/Dropdown/Dropdown";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useEffect, cloneElement, isValidElement, } from "react";
|
|
3
|
+
export const FocusedScrollView = ({ selectedKey, children, direction = "vertical", className, containerProps, scrollAlign = "center", }) => {
|
|
4
|
+
const containerRef = useRef(null);
|
|
5
|
+
const itemRefs = useRef({});
|
|
6
|
+
const scrollToItem = (key) => {
|
|
7
|
+
const container = containerRef.current;
|
|
8
|
+
const item = itemRefs.current[key];
|
|
9
|
+
if (container && item) {
|
|
10
|
+
if (direction === "vertical") {
|
|
11
|
+
const containerTop = container.getBoundingClientRect().top;
|
|
12
|
+
const itemTop = item.getBoundingClientRect().top;
|
|
13
|
+
const offset = itemTop - containerTop + container.scrollTop;
|
|
14
|
+
const containerHeight = container.clientHeight;
|
|
15
|
+
const itemHeight = item.offsetHeight;
|
|
16
|
+
let targetTop = offset;
|
|
17
|
+
if (scrollAlign === "center") {
|
|
18
|
+
targetTop = offset - (containerHeight / 2 - itemHeight / 2);
|
|
19
|
+
}
|
|
20
|
+
else if (scrollAlign === "end") {
|
|
21
|
+
targetTop = offset + (itemHeight - containerHeight);
|
|
22
|
+
}
|
|
23
|
+
container.scrollTo({
|
|
24
|
+
top: targetTop,
|
|
25
|
+
behavior: "smooth",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else if (direction === "horizontal") {
|
|
29
|
+
const containerLeft = container.getBoundingClientRect().left;
|
|
30
|
+
const itemLeft = item.getBoundingClientRect().left;
|
|
31
|
+
const offset = itemLeft - containerLeft + container.scrollLeft;
|
|
32
|
+
const containerWidth = container.clientWidth;
|
|
33
|
+
const itemWidth = item.offsetWidth;
|
|
34
|
+
let targetLeft = offset;
|
|
35
|
+
if (scrollAlign === "center") {
|
|
36
|
+
targetLeft = offset - (containerWidth / 2 - itemWidth / 2);
|
|
37
|
+
}
|
|
38
|
+
else if (scrollAlign === "end") {
|
|
39
|
+
targetLeft = offset + (itemWidth - containerWidth);
|
|
40
|
+
}
|
|
41
|
+
container.scrollTo({
|
|
42
|
+
left: targetLeft,
|
|
43
|
+
behavior: "smooth",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (selectedKey) {
|
|
50
|
+
scrollToItem(selectedKey);
|
|
51
|
+
}
|
|
52
|
+
}, [selectedKey, direction, scrollAlign]);
|
|
53
|
+
const enhancedChildren = Array.isArray(children)
|
|
54
|
+
? children.map((child) => {
|
|
55
|
+
if (isValidElement(child) && child.key) {
|
|
56
|
+
return cloneElement(child, {
|
|
57
|
+
// @ts-ignore
|
|
58
|
+
ref: (el) => {
|
|
59
|
+
itemRefs.current[String(child.key)] = el;
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return child;
|
|
64
|
+
})
|
|
65
|
+
: children;
|
|
66
|
+
return (_jsx("div", Object.assign({ ref: containerRef, className: `overflow-auto ${direction === "vertical" ? "max-h-60" : "max-w-full whitespace-nowrap"} ${className !== null && className !== void 0 ? className : ""}` }, containerProps, { children: enhancedChildren })));
|
|
67
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { FocusedScrollView } from "./FocusedScrollView";
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/FocusedScrollView",
|
|
6
|
+
component: FocusedScrollView,
|
|
7
|
+
args: {
|
|
8
|
+
direction: "vertical",
|
|
9
|
+
scrollAlign: "center",
|
|
10
|
+
className: "border border-input-default-stroke rounded",
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
export default meta;
|
|
14
|
+
const items = Array.from({ length: 30 }, (_, i) => `Item ${i + 1}`);
|
|
15
|
+
export const VerticalScroll = {
|
|
16
|
+
render: (args) => {
|
|
17
|
+
const [selected, setSelected] = useState("item-5");
|
|
18
|
+
return (_jsxs("div", { className: "p-6 space-y-4", children: [_jsxs("div", { className: "flex gap-2", children: [_jsx("button", { onClick: () => setSelected("item-5"), className: "px-3 py-1 bg-primary text-white rounded", children: "Scroll to Item 5" }), _jsx("button", { onClick: () => setSelected("item-20"), className: "px-3 py-1 bg-primary text-white rounded", children: "Scroll to Item 20" }), _jsx("button", { onClick: () => setSelected("item-29"), className: "px-3 py-1 bg-primary text-white rounded", children: "Scroll to Item 30" })] }), _jsx(FocusedScrollView, Object.assign({}, args, { selectedKey: selected, children: items.map((item, index) => (_jsx("div", { className: `px-4 py-2 ${selected === `item-${index + 1}`
|
|
19
|
+
? "bg-secondary text-secondary-foreground"
|
|
20
|
+
: "bg-white"}`, children: item }, `item-${index + 1}`))) }))] }));
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
export const HorizontalScroll = {
|
|
24
|
+
args: {
|
|
25
|
+
direction: "horizontal",
|
|
26
|
+
},
|
|
27
|
+
render: (args) => {
|
|
28
|
+
const [selected, setSelected] = useState("item-15");
|
|
29
|
+
return (_jsxs("div", { className: "p-6 space-y-4", children: [_jsxs("div", { className: "flex gap-2", children: [_jsx("button", { onClick: () => setSelected("item-5"), className: "px-3 py-1 bg-primary text-white rounded", children: "Scroll to Item 5" }), _jsx("button", { onClick: () => setSelected("item-15"), className: "px-3 py-1 bg-primary text-white rounded", children: "Scroll to Item 15" }), _jsx("button", { onClick: () => setSelected("item-25"), className: "px-3 py-1 bg-primary text-white rounded", children: "Scroll to Item 25" })] }), _jsx(FocusedScrollView, Object.assign({}, args, { selectedKey: selected, children: items.map((item, index) => (_jsx("div", { className: `inline-block w-28 h-12 mx-2 text-center leading-[3rem] rounded ${selected === `item-${index + 1}`
|
|
30
|
+
? "bg-secondary text-secondary-foreground"
|
|
31
|
+
: "bg-gray-200"}`, children: item }, `item-${index + 1}`))) }))] }));
|
|
32
|
+
},
|
|
33
|
+
};
|
package/dist/esm/bundle.css
CHANGED
|
@@ -675,6 +675,10 @@ input[type=number] {
|
|
|
675
675
|
margin-left: -0.5rem;
|
|
676
676
|
margin-right: -0.5rem;
|
|
677
677
|
}
|
|
678
|
+
.mx-2{
|
|
679
|
+
margin-left: 0.5rem;
|
|
680
|
+
margin-right: 0.5rem;
|
|
681
|
+
}
|
|
678
682
|
.mx-4{
|
|
679
683
|
margin-left: 1rem;
|
|
680
684
|
margin-right: 1rem;
|
|
@@ -744,6 +748,9 @@ input[type=number] {
|
|
|
744
748
|
.block{
|
|
745
749
|
display: block;
|
|
746
750
|
}
|
|
751
|
+
.inline-block{
|
|
752
|
+
display: inline-block;
|
|
753
|
+
}
|
|
747
754
|
.flex{
|
|
748
755
|
display: flex;
|
|
749
756
|
}
|
|
@@ -906,6 +913,9 @@ input[type=number] {
|
|
|
906
913
|
.w-2{
|
|
907
914
|
width: 0.5rem;
|
|
908
915
|
}
|
|
916
|
+
.w-28{
|
|
917
|
+
width: 7rem;
|
|
918
|
+
}
|
|
909
919
|
.w-3{
|
|
910
920
|
width: 0.75rem;
|
|
911
921
|
}
|
|
@@ -974,6 +984,9 @@ input[type=number] {
|
|
|
974
984
|
min-width: -moz-fit-content;
|
|
975
985
|
min-width: fit-content;
|
|
976
986
|
}
|
|
987
|
+
.max-w-full{
|
|
988
|
+
max-width: 100%;
|
|
989
|
+
}
|
|
977
990
|
.max-w-lg{
|
|
978
991
|
max-width: 32rem;
|
|
979
992
|
}
|
|
@@ -1185,6 +1198,9 @@ input[type=number] {
|
|
|
1185
1198
|
.text-ellipsis{
|
|
1186
1199
|
text-overflow: ellipsis;
|
|
1187
1200
|
}
|
|
1201
|
+
.whitespace-nowrap{
|
|
1202
|
+
white-space: nowrap;
|
|
1203
|
+
}
|
|
1188
1204
|
.rounded{
|
|
1189
1205
|
border-radius: 0.25rem;
|
|
1190
1206
|
}
|
|
@@ -2669,6 +2685,10 @@ input[type=number] {
|
|
|
2669
2685
|
--tw-bg-opacity: 1;
|
|
2670
2686
|
background-color: color-mix(in srgb, var(--other-transparency-warning-8) calc(100% * var(--tw-bg-opacity, 1)), transparent);
|
|
2671
2687
|
}
|
|
2688
|
+
.bg-white{
|
|
2689
|
+
--tw-bg-opacity: 1;
|
|
2690
|
+
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
|
|
2691
|
+
}
|
|
2672
2692
|
.bg-white-transparent-12{
|
|
2673
2693
|
--tw-bg-opacity: 1;
|
|
2674
2694
|
background-color: color-mix(in srgb, var(--other-transparency-white-12) calc(100% * var(--tw-bg-opacity, 1)), transparent);
|
|
@@ -3184,6 +3204,9 @@ input[type=number] {
|
|
|
3184
3204
|
.capitalize{
|
|
3185
3205
|
text-transform: capitalize;
|
|
3186
3206
|
}
|
|
3207
|
+
.leading-\[3rem\]{
|
|
3208
|
+
line-height: 3rem;
|
|
3209
|
+
}
|
|
3187
3210
|
.leading-none{
|
|
3188
3211
|
line-height: 1;
|
|
3189
3212
|
}
|