@rovula/ui 0.0.68 → 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.
Files changed (39) hide show
  1. package/dist/cjs/bundle.css +67 -0
  2. package/dist/cjs/bundle.js +3 -3
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
  5. package/dist/cjs/types/components/FocusedScrollView/FocusedScrollView.d.ts +12 -0
  6. package/dist/cjs/types/components/FocusedScrollView/FocusedScrollView.stories.d.ts +7 -0
  7. package/dist/cjs/types/components/InputFilter/InputFilter.stories.d.ts +2 -0
  8. package/dist/cjs/types/components/Search/Search.stories.d.ts +2 -0
  9. package/dist/cjs/types/components/TextInput/TextInput.d.ts +4 -0
  10. package/dist/cjs/types/components/TextInput/TextInput.stories.d.ts +11 -0
  11. package/dist/cjs/types/components/TextInput/TextInput.styles.d.ts +1 -0
  12. package/dist/cjs/types/index.d.ts +1 -0
  13. package/dist/components/FocusedScrollView/FocusedScrollView.js +67 -0
  14. package/dist/components/FocusedScrollView/FocusedScrollView.stories.js +33 -0
  15. package/dist/components/TextInput/TextInput.js +66 -14
  16. package/dist/components/TextInput/TextInput.stories.js +15 -0
  17. package/dist/components/TextInput/TextInput.styles.js +116 -7
  18. package/dist/esm/bundle.css +67 -0
  19. package/dist/esm/bundle.js +3 -3
  20. package/dist/esm/bundle.js.map +1 -1
  21. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
  22. package/dist/esm/types/components/FocusedScrollView/FocusedScrollView.d.ts +12 -0
  23. package/dist/esm/types/components/FocusedScrollView/FocusedScrollView.stories.d.ts +7 -0
  24. package/dist/esm/types/components/InputFilter/InputFilter.stories.d.ts +2 -0
  25. package/dist/esm/types/components/Search/Search.stories.d.ts +2 -0
  26. package/dist/esm/types/components/TextInput/TextInput.d.ts +4 -0
  27. package/dist/esm/types/components/TextInput/TextInput.stories.d.ts +11 -0
  28. package/dist/esm/types/components/TextInput/TextInput.styles.d.ts +1 -0
  29. package/dist/esm/types/index.d.ts +1 -0
  30. package/dist/index.d.ts +17 -2
  31. package/dist/index.js +1 -0
  32. package/dist/src/theme/global.css +86 -0
  33. package/package.json +1 -1
  34. package/src/components/FocusedScrollView/FocusedScrollView.stories.tsx +114 -0
  35. package/src/components/FocusedScrollView/FocusedScrollView.tsx +112 -0
  36. package/src/components/TextInput/TextInput.stories.tsx +83 -0
  37. package/src/components/TextInput/TextInput.styles.ts +117 -7
  38. package/src/components/TextInput/TextInput.tsx +115 -21
  39. package/src/index.ts +1 -0
@@ -359,10 +359,12 @@ declare const meta: {
359
359
  placeholder?: string | undefined | undefined;
360
360
  readOnly?: boolean | undefined | undefined;
361
361
  src?: string | undefined | undefined;
362
+ iconMode?: "flat" | "solid" | undefined;
362
363
  keepCloseIconOnValue?: boolean | undefined;
363
364
  labelClassName?: string | undefined;
364
365
  onClickStartIcon?: (() => void) | undefined;
365
366
  onClickEndIcon?: (() => void) | undefined;
367
+ renderStartIcon?: (() => React.ReactNode) | undefined;
366
368
  renderEndIcon?: (() => React.ReactNode) | undefined;
367
369
  ref?: React.LegacyRef<HTMLInputElement> | undefined;
368
370
  key?: React.Key | null | undefined;
@@ -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;
@@ -349,10 +349,12 @@ declare const meta: {
349
349
  placeholder?: string | undefined | undefined;
350
350
  readOnly?: boolean | undefined | undefined;
351
351
  src?: string | undefined | undefined;
352
+ iconMode?: "flat" | "solid" | undefined;
352
353
  keepCloseIconOnValue?: boolean | undefined;
353
354
  labelClassName?: string | undefined;
354
355
  onClickStartIcon?: (() => void) | undefined;
355
356
  onClickEndIcon?: (() => void) | undefined;
357
+ renderStartIcon?: (() => React.ReactNode) | undefined;
356
358
  renderEndIcon?: (() => React.ReactNode) | undefined;
357
359
  ref?: React.LegacyRef<HTMLInputElement> | undefined;
358
360
  key?: React.Key | null | undefined;
@@ -310,11 +310,13 @@ declare const meta: {
310
310
  required?: boolean | undefined;
311
311
  src?: string | undefined | undefined;
312
312
  label?: string | undefined;
313
+ iconMode?: "flat" | "solid" | undefined;
313
314
  helperText?: string | undefined;
314
315
  errorMessage?: string | undefined;
315
316
  labelClassName?: string | undefined;
316
317
  onClickStartIcon?: (() => void) | undefined;
317
318
  onClickEndIcon?: (() => void) | undefined;
319
+ renderStartIcon?: (() => React.ReactNode) | undefined;
318
320
  renderEndIcon?: (() => React.ReactNode) | undefined;
319
321
  modal?: boolean | undefined;
320
322
  optionContainerClassName?: string | undefined;
@@ -5,6 +5,7 @@ export type InputProps = {
5
5
  size?: "sm" | "md" | "lg";
6
6
  rounded?: "none" | "normal" | "full";
7
7
  variant?: "flat" | "outline" | "underline";
8
+ iconMode?: "flat" | "solid";
8
9
  type?: React.HTMLInputTypeAttribute;
9
10
  helperText?: string;
10
11
  errorMessage?: string;
@@ -22,6 +23,7 @@ export type InputProps = {
22
23
  labelClassName?: string;
23
24
  onClickStartIcon?: () => void;
24
25
  onClickEndIcon?: () => void;
26
+ renderStartIcon?: () => ReactNode;
25
27
  renderEndIcon?: () => ReactNode;
26
28
  } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
27
29
  export declare const TextInput: React.ForwardRefExoticComponent<{
@@ -30,6 +32,7 @@ export declare const TextInput: React.ForwardRefExoticComponent<{
30
32
  size?: "sm" | "md" | "lg";
31
33
  rounded?: "none" | "normal" | "full";
32
34
  variant?: "flat" | "outline" | "underline";
35
+ iconMode?: "flat" | "solid";
33
36
  type?: React.HTMLInputTypeAttribute;
34
37
  helperText?: string;
35
38
  errorMessage?: string;
@@ -47,6 +50,7 @@ export declare const TextInput: React.ForwardRefExoticComponent<{
47
50
  labelClassName?: string;
48
51
  onClickStartIcon?: () => void;
49
52
  onClickEndIcon?: () => void;
53
+ renderStartIcon?: () => ReactNode;
50
54
  renderEndIcon?: () => ReactNode;
51
55
  } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & React.RefAttributes<HTMLInputElement>>;
52
56
  export default TextInput;
@@ -7,6 +7,7 @@ declare const meta: {
7
7
  size?: "sm" | "md" | "lg";
8
8
  rounded?: "none" | "normal" | "full";
9
9
  variant?: "flat" | "outline" | "underline";
10
+ iconMode?: "flat" | "solid";
10
11
  type?: React.HTMLInputTypeAttribute;
11
12
  helperText?: string;
12
13
  errorMessage?: string;
@@ -24,6 +25,7 @@ declare const meta: {
24
25
  labelClassName?: string;
25
26
  onClickStartIcon?: () => void;
26
27
  onClickEndIcon?: () => void;
28
+ renderStartIcon?: () => React.ReactNode;
27
29
  renderEndIcon?: () => React.ReactNode;
28
30
  } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & React.RefAttributes<HTMLInputElement>>;
29
31
  tags: string[];
@@ -36,6 +38,7 @@ declare const meta: {
36
38
  size?: "sm" | "md" | "lg" | undefined;
37
39
  rounded?: "none" | "normal" | "full" | undefined;
38
40
  variant?: "flat" | "outline" | "underline" | undefined;
41
+ iconMode?: "flat" | "solid" | undefined;
39
42
  type?: React.HTMLInputTypeAttribute | undefined;
40
43
  helperText?: string | undefined;
41
44
  errorMessage?: string | undefined;
@@ -53,6 +56,7 @@ declare const meta: {
53
56
  labelClassName?: string | undefined;
54
57
  onClickStartIcon?: (() => void) | undefined;
55
58
  onClickEndIcon?: (() => void) | undefined;
59
+ renderStartIcon?: (() => React.ReactNode) | undefined;
56
60
  renderEndIcon?: (() => React.ReactNode) | undefined;
57
61
  suppressHydrationWarning?: boolean | undefined | undefined;
58
62
  color?: string | undefined | undefined;
@@ -371,3 +375,10 @@ export declare const FuctionInput: {
371
375
  };
372
376
  render: (args: {}) => import("react/jsx-runtime").JSX.Element;
373
377
  };
378
+ export declare const CustomIcon: {
379
+ args: {
380
+ label: string;
381
+ disabled: boolean;
382
+ };
383
+ render: (args: {}) => import("react/jsx-runtime").JSX.Element;
384
+ };
@@ -15,6 +15,7 @@ export declare const labelVariant: (props?: ({
15
15
  disabled?: boolean | null | undefined;
16
16
  error?: boolean | null | undefined;
17
17
  hasSearchIcon?: boolean | null | undefined;
18
+ hasLeftSectionIcon?: boolean | null | undefined;
18
19
  isFloatingLabel?: boolean | null | undefined;
19
20
  } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
20
21
  export declare const helperTextVariant: (props?: ({
@@ -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";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import React__default, { ReactElement, ReactNode, CSSProperties, FC, ComponentPropsWithoutRef } from 'react';
2
+ import React__default, { ReactElement, ReactNode, CSSProperties, FC, ComponentPropsWithoutRef, HTMLAttributes } from 'react';
3
3
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
4
4
  import * as class_variance_authority_dist_types from 'class-variance-authority/dist/types';
5
5
  import * as LabelPrimitive from '@radix-ui/react-label';
@@ -49,6 +49,7 @@ type InputProps = {
49
49
  size?: "sm" | "md" | "lg";
50
50
  rounded?: "none" | "normal" | "full";
51
51
  variant?: "flat" | "outline" | "underline";
52
+ iconMode?: "flat" | "solid";
52
53
  type?: React__default.HTMLInputTypeAttribute;
53
54
  helperText?: string;
54
55
  errorMessage?: string;
@@ -66,6 +67,7 @@ type InputProps = {
66
67
  labelClassName?: string;
67
68
  onClickStartIcon?: () => void;
68
69
  onClickEndIcon?: () => void;
70
+ renderStartIcon?: () => ReactNode;
69
71
  renderEndIcon?: () => ReactNode;
70
72
  } & Omit<React__default.InputHTMLAttributes<HTMLInputElement>, "size">;
71
73
  declare const TextInput: React__default.ForwardRefExoticComponent<{
@@ -74,6 +76,7 @@ declare const TextInput: React__default.ForwardRefExoticComponent<{
74
76
  size?: "sm" | "md" | "lg";
75
77
  rounded?: "none" | "normal" | "full";
76
78
  variant?: "flat" | "outline" | "underline";
79
+ iconMode?: "flat" | "solid";
77
80
  type?: React__default.HTMLInputTypeAttribute;
78
81
  helperText?: string;
79
82
  errorMessage?: string;
@@ -91,6 +94,7 @@ declare const TextInput: React__default.ForwardRefExoticComponent<{
91
94
  labelClassName?: string;
92
95
  onClickStartIcon?: () => void;
93
96
  onClickEndIcon?: () => void;
97
+ renderStartIcon?: () => ReactNode;
94
98
  renderEndIcon?: () => ReactNode;
95
99
  } & Omit<React__default.InputHTMLAttributes<HTMLInputElement>, "size"> & React__default.RefAttributes<HTMLInputElement>>;
96
100
 
@@ -732,6 +736,17 @@ declare const Tree: FC<TreeProps>;
732
736
 
733
737
  declare const TreeItem: FC<TreeItemProps>;
734
738
 
739
+ type ScrollAlign = "start" | "center" | "end";
740
+ type FocusedScrollViewProps = {
741
+ selectedKey?: string;
742
+ children: ReactNode;
743
+ direction?: "vertical" | "horizontal";
744
+ className?: string;
745
+ containerProps?: HTMLAttributes<HTMLDivElement>;
746
+ scrollAlign?: ScrollAlign;
747
+ };
748
+ declare const FocusedScrollView: React__default.FC<FocusedScrollViewProps>;
749
+
735
750
  declare const resloveTimestamp: (timestamp: number) => number;
736
751
  declare const getStartDateOfDay: (date: Date) => Date;
737
752
  declare const getEndDateOfDay: (date: Date) => Date;
@@ -745,4 +760,4 @@ declare function usePrevious<T>(value: T): T | undefined;
745
760
 
746
761
  declare function cn(...inputs: ClassValue[]): string;
747
762
 
748
- export { ActionButton, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Button, type ButtonProps, Calendar, Checkbox, Collapsible, DataTable, type DataTableProps, DatePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Dropdown, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, type DropdownProps, Icon, Input, InputFilter, type InputFilterProps, type InputProps, Label, Loading, Navbar, type NavbarProps, type Options$1 as Options, Popover, PopoverContent, PopoverTrigger, ProgressBar, Search, type SearchProps, Slider, type SliderProps, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, Text, TextInput, Toast$1 as Toast, ToastAction, type ToastActionElement, ToastClose, ToastDescription, type ToastProps, ToastProvider, ToastTitle, ToastViewport, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipSimple, TooltipTrigger, Tree, type TreeData, TreeItem, type TreeItemProps, type TreeProps, cn, getEndDateOfDay, getStartDateOfDay, getStartEndTimestampOfDay, getTimestampUTC, reducer, resloveTimestamp, toast, usePrevious, useToast };
763
+ export { ActionButton, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Button, type ButtonProps, Calendar, Checkbox, Collapsible, DataTable, type DataTableProps, DatePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Dropdown, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, type DropdownProps, FocusedScrollView, Icon, Input, InputFilter, type InputFilterProps, type InputProps, Label, Loading, Navbar, type NavbarProps, type Options$1 as Options, Popover, PopoverContent, PopoverTrigger, ProgressBar, Search, type SearchProps, Slider, type SliderProps, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, Text, TextInput, Toast$1 as Toast, ToastAction, type ToastActionElement, ToastClose, ToastDescription, type ToastProps, ToastProvider, ToastTitle, ToastViewport, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipSimple, TooltipTrigger, Tree, type TreeData, TreeItem, type TreeItemProps, type TreeProps, cn, getEndDateOfDay, getStartDateOfDay, getStartEndTimestampOfDay, getTimestampUTC, reducer, resloveTimestamp, toast, usePrevious, useToast };
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ export * from "./components/Toast/Toast";
35
35
  export * from "./components/Toast/Toaster";
36
36
  export * from "./components/Toast/useToast";
37
37
  export * from "./components/Tree";
38
+ export * from "./components/FocusedScrollView/FocusedScrollView";
38
39
  // UTILS
39
40
  export { resloveTimestamp, getStartDateOfDay, getEndDateOfDay, getStartEndTimestampOfDay, getTimestampUTC, } from "./utils/datetime";
40
41
  // Hooks
@@ -2284,10 +2284,22 @@ input[type=number] {
2284
2284
  left: 2.25rem;
2285
2285
  }
2286
2286
 
2287
+ .left-\[38px\] {
2288
+ left: 38px;
2289
+ }
2290
+
2291
+ .left-\[46px\] {
2292
+ left: 46px;
2293
+ }
2294
+
2287
2295
  .left-\[50\%\] {
2288
2296
  left: 50%;
2289
2297
  }
2290
2298
 
2299
+ .left-\[72px\] {
2300
+ left: 72px;
2301
+ }
2302
+
2291
2303
  .right-0 {
2292
2304
  right: 0px;
2293
2305
  }
@@ -2349,6 +2361,11 @@ input[type=number] {
2349
2361
  margin-right: -0.5rem;
2350
2362
  }
2351
2363
 
2364
+ .mx-2 {
2365
+ margin-left: 0.5rem;
2366
+ margin-right: 0.5rem;
2367
+ }
2368
+
2352
2369
  .mx-4 {
2353
2370
  margin-left: 1rem;
2354
2371
  margin-right: 1rem;
@@ -2440,6 +2457,10 @@ input[type=number] {
2440
2457
  display: block;
2441
2458
  }
2442
2459
 
2460
+ .inline-block {
2461
+ display: inline-block;
2462
+ }
2463
+
2443
2464
  .flex {
2444
2465
  display: flex;
2445
2466
  }
@@ -2651,6 +2672,10 @@ input[type=number] {
2651
2672
  width: 0.5rem;
2652
2673
  }
2653
2674
 
2675
+ .w-28 {
2676
+ width: 7rem;
2677
+ }
2678
+
2654
2679
  .w-3 {
2655
2680
  width: 0.75rem;
2656
2681
  }
@@ -2741,6 +2766,10 @@ input[type=number] {
2741
2766
  min-width: fit-content;
2742
2767
  }
2743
2768
 
2769
+ .max-w-full {
2770
+ max-width: 100%;
2771
+ }
2772
+
2744
2773
  .max-w-lg {
2745
2774
  max-width: 32rem;
2746
2775
  }
@@ -3015,6 +3044,10 @@ input[type=number] {
3015
3044
  text-overflow: ellipsis;
3016
3045
  }
3017
3046
 
3047
+ .whitespace-nowrap {
3048
+ white-space: nowrap;
3049
+ }
3050
+
3018
3051
  .rounded {
3019
3052
  border-radius: 0.25rem;
3020
3053
  }
@@ -3047,6 +3080,21 @@ input[type=number] {
3047
3080
  border-radius: var(--radius-radius-xs);
3048
3081
  }
3049
3082
 
3083
+ .rounded-l-full {
3084
+ border-top-left-radius: 9999px;
3085
+ border-bottom-left-radius: 9999px;
3086
+ }
3087
+
3088
+ .rounded-l-none {
3089
+ border-top-left-radius: 0px;
3090
+ border-bottom-left-radius: 0px;
3091
+ }
3092
+
3093
+ .rounded-l-xl {
3094
+ border-top-left-radius: var(--radius-radius-xl);
3095
+ border-bottom-left-radius: var(--radius-radius-xl);
3096
+ }
3097
+
3050
3098
  .rounded-r-full {
3051
3099
  border-top-right-radius: 9999px;
3052
3100
  border-bottom-right-radius: 9999px;
@@ -3094,6 +3142,10 @@ input[type=number] {
3094
3142
  border-left-width: 1px;
3095
3143
  }
3096
3144
 
3145
+ .border-r {
3146
+ border-right-width: 1px;
3147
+ }
3148
+
3097
3149
  .border-t {
3098
3150
  border-top-width: 1px;
3099
3151
  }
@@ -3359,6 +3411,16 @@ input[type=number] {
3359
3411
  border-left-color: color-mix(in srgb, var(--input-color-error) calc(100% * var(--tw-border-opacity, 1)), transparent);
3360
3412
  }
3361
3413
 
3414
+ .border-r-input-default-stroke {
3415
+ --tw-border-opacity: 1;
3416
+ border-right-color: color-mix(in srgb, var(--input-color-default-stroke) calc(100% * var(--tw-border-opacity, 1)), transparent);
3417
+ }
3418
+
3419
+ .border-r-input-error {
3420
+ --tw-border-opacity: 1;
3421
+ border-right-color: color-mix(in srgb, var(--input-color-error) calc(100% * var(--tw-border-opacity, 1)), transparent);
3422
+ }
3423
+
3362
3424
  .border-t-secondary {
3363
3425
  --tw-border-opacity: 1;
3364
3426
  border-top-color: color-mix(in srgb, var(--state-color-secondary-default) calc(100% * var(--tw-border-opacity, 1)), transparent);
@@ -4848,6 +4910,11 @@ input[type=number] {
4848
4910
  background-color: color-mix(in srgb, var(--other-transparency-warning-8) calc(100% * var(--tw-bg-opacity, 1)), transparent);
4849
4911
  }
4850
4912
 
4913
+ .bg-white {
4914
+ --tw-bg-opacity: 1;
4915
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
4916
+ }
4917
+
4851
4918
  .bg-white-transparent-12 {
4852
4919
  --tw-bg-opacity: 1;
4853
4920
  background-color: color-mix(in srgb, var(--other-transparency-white-12) calc(100% * var(--tw-bg-opacity, 1)), transparent);
@@ -5504,6 +5571,10 @@ input[type=number] {
5504
5571
  text-transform: capitalize;
5505
5572
  }
5506
5573
 
5574
+ .leading-\[3rem\] {
5575
+ line-height: 3rem;
5576
+ }
5577
+
5507
5578
  .leading-none {
5508
5579
  line-height: 1;
5509
5580
  }
@@ -7977,6 +8048,11 @@ input[type=number] {
7977
8048
  border-left-color: color-mix(in srgb, var(--input-color-active-stroke) calc(100% * var(--tw-border-opacity, 1)), transparent);
7978
8049
  }
7979
8050
 
8051
+ .peer:hover ~ .peer-hover\:border-r-input-active-stroke {
8052
+ --tw-border-opacity: 1;
8053
+ border-right-color: color-mix(in srgb, var(--input-color-active-stroke) calc(100% * var(--tw-border-opacity, 1)), transparent);
8054
+ }
8055
+
7980
8056
  .peer:hover ~ .peer-hover\:fill-input-filled-text {
7981
8057
  fill: color-mix(in srgb, var(--input-color-filled-text) calc(100% * 1), transparent);
7982
8058
  }
@@ -8006,6 +8082,11 @@ input[type=number] {
8006
8082
  border-left-color: color-mix(in srgb, var(--input-color-active-stroke) calc(100% * var(--tw-border-opacity, 1)), transparent);
8007
8083
  }
8008
8084
 
8085
+ .peer:focus ~ .peer-focus\:border-r-input-active-stroke {
8086
+ --tw-border-opacity: 1;
8087
+ border-right-color: color-mix(in srgb, var(--input-color-active-stroke) calc(100% * var(--tw-border-opacity, 1)), transparent);
8088
+ }
8089
+
8009
8090
  .peer:focus ~ .peer-focus\:bg-input-label-bg {
8010
8091
  --tw-bg-opacity: 1;
8011
8092
  background-color: color-mix(in srgb, var(--input-color-label-bg) calc(100% * var(--tw-bg-opacity, 1)), transparent);
@@ -8062,6 +8143,11 @@ input[type=number] {
8062
8143
  border-left-color: color-mix(in srgb, var(--input-color-disable-stroke) calc(100% * var(--tw-border-opacity, 1)), transparent);
8063
8144
  }
8064
8145
 
8146
+ .peer:disabled ~ .peer-disabled\:border-r-input-disable-stroke {
8147
+ --tw-border-opacity: 1;
8148
+ border-right-color: color-mix(in srgb, var(--input-color-disable-stroke) calc(100% * var(--tw-border-opacity, 1)), transparent);
8149
+ }
8150
+
8065
8151
  .peer:disabled ~ .peer-disabled\:fill-input-disable-stroke {
8066
8152
  fill: color-mix(in srgb, var(--input-color-disable-stroke) calc(100% * 1), transparent);
8067
8153
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rovula/ui",
3
- "version": "0.0.68",
3
+ "version": "0.0.70",
4
4
  "main": "dist/cjs/bundle.js",
5
5
  "module": "dist/esm/bundle.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,114 @@
1
+ import { Meta, StoryObj } from "@storybook/react";
2
+ import React, { useState } from "react";
3
+ import { FocusedScrollView } from "./FocusedScrollView";
4
+
5
+ const meta: Meta<typeof FocusedScrollView> = {
6
+ title: "Components/FocusedScrollView",
7
+ component: FocusedScrollView,
8
+ args: {
9
+ direction: "vertical",
10
+ scrollAlign: "center",
11
+ className: "border border-input-default-stroke rounded",
12
+ },
13
+ };
14
+
15
+ export default meta;
16
+
17
+ type Story = StoryObj<typeof FocusedScrollView>;
18
+
19
+ const items = Array.from({ length: 30 }, (_, i) => `Item ${i + 1}`);
20
+
21
+ export const VerticalScroll: Story = {
22
+ render: (args) => {
23
+ const [selected, setSelected] = useState("item-5");
24
+
25
+ return (
26
+ <div className="p-6 space-y-4">
27
+ <div className="flex gap-2">
28
+ <button
29
+ onClick={() => setSelected("item-5")}
30
+ className="px-3 py-1 bg-primary text-white rounded"
31
+ >
32
+ Scroll to Item 5
33
+ </button>
34
+ <button
35
+ onClick={() => setSelected("item-20")}
36
+ className="px-3 py-1 bg-primary text-white rounded"
37
+ >
38
+ Scroll to Item 20
39
+ </button>
40
+ <button
41
+ onClick={() => setSelected("item-29")}
42
+ className="px-3 py-1 bg-primary text-white rounded"
43
+ >
44
+ Scroll to Item 30
45
+ </button>
46
+ </div>
47
+
48
+ <FocusedScrollView {...args} selectedKey={selected}>
49
+ {items.map((item, index) => (
50
+ <div
51
+ key={`item-${index + 1}`}
52
+ className={`px-4 py-2 ${
53
+ selected === `item-${index + 1}`
54
+ ? "bg-secondary text-secondary-foreground"
55
+ : "bg-white"
56
+ }`}
57
+ >
58
+ {item}
59
+ </div>
60
+ ))}
61
+ </FocusedScrollView>
62
+ </div>
63
+ );
64
+ },
65
+ };
66
+
67
+ export const HorizontalScroll: Story = {
68
+ args: {
69
+ direction: "horizontal",
70
+ },
71
+ render: (args) => {
72
+ const [selected, setSelected] = useState("item-15");
73
+
74
+ return (
75
+ <div className="p-6 space-y-4">
76
+ <div className="flex gap-2">
77
+ <button
78
+ onClick={() => setSelected("item-5")}
79
+ className="px-3 py-1 bg-primary text-white rounded"
80
+ >
81
+ Scroll to Item 5
82
+ </button>
83
+ <button
84
+ onClick={() => setSelected("item-15")}
85
+ className="px-3 py-1 bg-primary text-white rounded"
86
+ >
87
+ Scroll to Item 15
88
+ </button>
89
+ <button
90
+ onClick={() => setSelected("item-25")}
91
+ className="px-3 py-1 bg-primary text-white rounded"
92
+ >
93
+ Scroll to Item 25
94
+ </button>
95
+ </div>
96
+
97
+ <FocusedScrollView {...args} selectedKey={selected}>
98
+ {items.map((item, index) => (
99
+ <div
100
+ key={`item-${index + 1}`}
101
+ className={`inline-block w-28 h-12 mx-2 text-center leading-[3rem] rounded ${
102
+ selected === `item-${index + 1}`
103
+ ? "bg-secondary text-secondary-foreground"
104
+ : "bg-gray-200"
105
+ }`}
106
+ >
107
+ {item}
108
+ </div>
109
+ ))}
110
+ </FocusedScrollView>
111
+ </div>
112
+ );
113
+ },
114
+ };
@@ -0,0 +1,112 @@
1
+ import React, {
2
+ useRef,
3
+ useEffect,
4
+ cloneElement,
5
+ ReactNode,
6
+ isValidElement,
7
+ HTMLAttributes,
8
+ } from "react";
9
+
10
+ type ScrollAlign = "start" | "center" | "end";
11
+
12
+ type FocusedScrollViewProps = {
13
+ selectedKey?: string;
14
+ children: ReactNode;
15
+ direction?: "vertical" | "horizontal";
16
+ className?: string;
17
+ containerProps?: HTMLAttributes<HTMLDivElement>;
18
+ scrollAlign?: ScrollAlign;
19
+ };
20
+
21
+ export const FocusedScrollView: React.FC<FocusedScrollViewProps> = ({
22
+ selectedKey,
23
+ children,
24
+ direction = "vertical",
25
+ className,
26
+ containerProps,
27
+ scrollAlign = "center",
28
+ }) => {
29
+ const containerRef = useRef<HTMLDivElement>(null);
30
+ const itemRefs = useRef<Record<string, HTMLDivElement | null>>({});
31
+
32
+ const scrollToItem = (key: string) => {
33
+ const container = containerRef.current;
34
+ const item = itemRefs.current[key];
35
+
36
+ if (container && item) {
37
+ if (direction === "vertical") {
38
+ const containerTop = container.getBoundingClientRect().top;
39
+ const itemTop = item.getBoundingClientRect().top;
40
+ const offset = itemTop - containerTop + container.scrollTop;
41
+
42
+ const containerHeight = container.clientHeight;
43
+ const itemHeight = item.offsetHeight;
44
+
45
+ let targetTop = offset;
46
+
47
+ if (scrollAlign === "center") {
48
+ targetTop = offset - (containerHeight / 2 - itemHeight / 2);
49
+ } else if (scrollAlign === "end") {
50
+ targetTop = offset + (itemHeight - containerHeight);
51
+ }
52
+
53
+ container.scrollTo({
54
+ top: targetTop,
55
+ behavior: "smooth",
56
+ });
57
+ } else if (direction === "horizontal") {
58
+ const containerLeft = container.getBoundingClientRect().left;
59
+ const itemLeft = item.getBoundingClientRect().left;
60
+ const offset = itemLeft - containerLeft + container.scrollLeft;
61
+
62
+ const containerWidth = container.clientWidth;
63
+ const itemWidth = item.offsetWidth;
64
+
65
+ let targetLeft = offset;
66
+
67
+ if (scrollAlign === "center") {
68
+ targetLeft = offset - (containerWidth / 2 - itemWidth / 2);
69
+ } else if (scrollAlign === "end") {
70
+ targetLeft = offset + (itemWidth - containerWidth);
71
+ }
72
+
73
+ container.scrollTo({
74
+ left: targetLeft,
75
+ behavior: "smooth",
76
+ });
77
+ }
78
+ }
79
+ };
80
+
81
+ useEffect(() => {
82
+ if (selectedKey) {
83
+ scrollToItem(selectedKey);
84
+ }
85
+ }, [selectedKey, direction, scrollAlign]);
86
+
87
+ const enhancedChildren = Array.isArray(children)
88
+ ? children.map((child) => {
89
+ if (isValidElement(child) && child.key) {
90
+ return cloneElement(child, {
91
+ // @ts-ignore
92
+ ref: (el: HTMLDivElement) => {
93
+ itemRefs.current[String(child.key)] = el;
94
+ },
95
+ });
96
+ }
97
+ return child;
98
+ })
99
+ : children;
100
+
101
+ return (
102
+ <div
103
+ ref={containerRef}
104
+ className={`overflow-auto ${
105
+ direction === "vertical" ? "max-h-60" : "max-w-full whitespace-nowrap"
106
+ } ${className ?? ""}`}
107
+ {...containerProps}
108
+ >
109
+ {enhancedChildren}
110
+ </div>
111
+ );
112
+ };