@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.
@@ -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
+ };
@@ -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
  }