@rolder/kit 3.0.0-alpha.76 → 3.0.0-alpha.78

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.
@@ -6,8 +6,7 @@ import { getIsStreaming, useChatMessage, useChatMessagePart } from "./store/inde
6
6
  const Message = /*#__PURE__*/ memo(({ messageId })=>{
7
7
  const message = useChatMessage(messageId);
8
8
  const part = useChatMessagePart(messageId, 'text');
9
- console.log(messageId);
10
- return part ? /*#__PURE__*/ jsx(Paper, {
9
+ return part?.text ? /*#__PURE__*/ jsx(Paper, {
11
10
  radius: "md",
12
11
  px: "md",
13
12
  py: "sm",
@@ -0,0 +1,2 @@
1
+ import type { ChatRootProps } from './Root';
2
+ export declare const Messages: ({ children, height, radius, padding, scrollAreaProps, withScrollButton, stackProps, ...props }: Omit<ChatRootProps, "chatOptions">) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,27 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Paper, Stack } from "@mantine/core";
3
+ import { ScrollArea } from "../../../ui/index.js";
4
+ import { useChatMessageIds } from "./store/index.js";
5
+ const Messages = ({ children, height, radius = 'md', padding = 'md', scrollAreaProps, withScrollButton = true, stackProps, ...props })=>{
6
+ const messageIds = useChatMessageIds();
7
+ return /*#__PURE__*/ jsx(Paper, {
8
+ withBorder: true,
9
+ radius: radius,
10
+ ...props,
11
+ children: /*#__PURE__*/ jsxs(ScrollArea.Root, {
12
+ autoScroll: true,
13
+ height: height,
14
+ radius: radius,
15
+ ...scrollAreaProps,
16
+ children: [
17
+ /*#__PURE__*/ jsx(Stack, {
18
+ p: padding,
19
+ ...stackProps,
20
+ children: 'function' == typeof children ? children(messageIds) : null
21
+ }),
22
+ withScrollButton && /*#__PURE__*/ jsx(ScrollArea.ScrollButton, {})
23
+ ]
24
+ })
25
+ });
26
+ };
27
+ export { Messages };
@@ -1,14 +1,15 @@
1
1
  import type { UseChatOptions } from '@ai-sdk/react';
2
- import { type PaperProps, type StackProps } from '@mantine/core';
2
+ import type { MantineRadius, MantineSpacing, PaperProps, ScrollAreaProps, StackProps } from '@mantine/core';
3
3
  import type { ChatInit, UIMessage } from 'ai';
4
4
  import type { ReactNode } from 'react';
5
- import { type ScrollAreaProps } from '../../../ui';
6
- export interface ConversationRootProps extends PaperProps {
5
+ export interface ChatRootProps extends PaperProps {
7
6
  children: (messageIds: string[]) => ReactNode;
8
- h: string;
7
+ height: string;
8
+ radius?: MantineRadius;
9
+ padding?: MantineSpacing;
9
10
  scrollAreaProps?: Omit<ScrollAreaProps, 'children' | 'h'>;
10
11
  withScrollButton?: boolean;
11
- stackProps?: StackProps;
12
+ stackProps?: Omit<StackProps, 'p'>;
12
13
  chatOptions?: UseChatOptions<UIMessage> & ChatInit<UIMessage>;
13
14
  }
14
15
  /**
@@ -26,6 +27,7 @@ export interface ConversationRootProps extends PaperProps {
26
27
  * {messageIds.map((messageId) => (
27
28
  * <Chat.Message key={messageId} messageId={messageId} />
28
29
  * ))}
30
+ *
29
31
  * <Chat.Empty />
30
32
  * <Chat.Loader />
31
33
  * </>
@@ -36,10 +38,12 @@ export interface ConversationRootProps extends PaperProps {
36
38
  * ```
37
39
  *
38
40
  * @param children - Render-функция, принимающая массив ID сообщений
39
- * @param h - Высота компонента (обязательный параметр)
41
+ * @param height - Высота компонента (обязательный параметр)
42
+ * @param radius - Радиус скругления углов компонента
43
+ * @param padding - Отступы внутри компонента
40
44
  * @param chatOptions - Опции для инициализации чата (API endpoint, начальные сообщения и т.д.)
41
45
  * @param scrollAreaProps - Пропсы для ScrollArea
42
46
  * @param withScrollButton - Показывать кнопку прокрутки (по умолчанию true)
43
47
  * @param stackProps - Пропсы для Stack контейнера
44
48
  */
45
- export declare const Root: ({ children, h, scrollAreaProps, withScrollButton, stackProps, chatOptions, ...props }: ConversationRootProps) => import("react/jsx-runtime").JSX.Element;
49
+ export declare const Root: ({ chatOptions, ...props }: ChatRootProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,38 +1,18 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { Paper, Stack } from "@mantine/core";
3
- import { ScrollArea } from "../../../ui/index.js";
4
- import { useChatMessageIds, useInitChat } from "./store/index.js";
2
+ import { Messages } from "./Messages.js";
3
+ import { useInitChat } from "./store/index.js";
5
4
  const ChatInitializer = ({ chatOptions })=>{
6
5
  useInitChat(chatOptions);
7
6
  return null;
8
7
  };
9
- const Root = ({ children, h, scrollAreaProps, withScrollButton = true, stackProps, chatOptions, ...props })=>{
10
- const messageIds = useChatMessageIds();
11
- return /*#__PURE__*/ jsxs(Fragment, {
8
+ const Root = ({ chatOptions, ...props })=>/*#__PURE__*/ jsxs(Fragment, {
12
9
  children: [
13
10
  /*#__PURE__*/ jsx(ChatInitializer, {
14
11
  chatOptions: chatOptions
15
12
  }),
16
- /*#__PURE__*/ jsx(Paper, {
17
- withBorder: true,
18
- radius: "md",
19
- ...props,
20
- children: /*#__PURE__*/ jsxs(ScrollArea, {
21
- autoScroll: true,
22
- scrollToBottomOnInit: true,
23
- p: "md",
24
- h: h,
25
- ...scrollAreaProps,
26
- children: [
27
- /*#__PURE__*/ jsx(Stack, {
28
- ...stackProps,
29
- children: 'function' == typeof children ? children(messageIds) : null
30
- }),
31
- withScrollButton && /*#__PURE__*/ jsx(ScrollArea.ScrollButton, {})
32
- ]
33
- })
13
+ /*#__PURE__*/ jsx(Messages, {
14
+ ...props
34
15
  })
35
16
  ]
36
17
  });
37
- };
38
18
  export { Root };
@@ -1,5 +1,5 @@
1
1
  export declare const Chat: {
2
- Root: ({ children, h, scrollAreaProps, withScrollButton, stackProps, chatOptions, ...props }: import("./Root").ConversationRootProps) => import("react/jsx-runtime").JSX.Element;
2
+ Root: ({ chatOptions, ...props }: import("./Root").ChatRootProps) => import("react/jsx-runtime").JSX.Element;
3
3
  Message: import("react").MemoExoticComponent<({ messageId }: {
4
4
  messageId: string;
5
5
  }) => import("react/jsx-runtime").JSX.Element | null>;
package/dist/styles.css CHANGED
@@ -62,11 +62,6 @@
62
62
  border-radius: var(--radius);
63
63
  }
64
64
 
65
- .rolder-scroll-area-content {
66
- width: 100%;
67
- height: 100%;
68
- }
69
-
70
65
  .rolder-scroll-area-scrollbar {
71
66
  border-top-right-radius: var(--radius);
72
67
  border-bottom-right-radius: var(--radius);
@@ -1,8 +1,8 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { RichTextEditor } from "@mantine/tiptap";
3
3
  import { ScrollArea } from "../scrollArea/index.js";
4
- const Content = ({ height })=>/*#__PURE__*/ jsxs(ScrollArea, {
5
- h: height,
4
+ const Content = ({ height })=>/*#__PURE__*/ jsxs(ScrollArea.Root, {
5
+ height: height,
6
6
  autoScroll: true,
7
7
  radius: "md",
8
8
  children: [
@@ -0,0 +1,7 @@
1
+ import { type MantineRadius, type ScrollAreaProps as MantineScrollAreaProps } from '@mantine/core';
2
+ export interface ScrollAreaProps extends Omit<MantineScrollAreaProps, 'h'> {
3
+ height: string;
4
+ radius?: MantineRadius;
5
+ autoScroll?: boolean;
6
+ }
7
+ export declare const Root: ({ children, height, radius, autoScroll, ...props }: ScrollAreaProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Box, ScrollArea, getRadius } from "@mantine/core";
3
+ import { useElementSize } from "@mantine/hooks";
4
+ import { useEffect, useRef } from "react";
5
+ import { isAboveCenter } from "./methods.js";
6
+ import { getHasScrollableContent, getHeight, getIsAboveCenter, getScrollAreaRef, setAutoScroll, setHeight, setIsAboveCenter, setScrollAreaRef } from "./store.js";
7
+ const Root = ({ children, height, radius, autoScroll = false, ...props })=>{
8
+ const scrollAreaRef = useRef(null);
9
+ const { ref: boxRef, height: contentHeight } = useElementSize();
10
+ useEffect(()=>{
11
+ setAutoScroll(autoScroll);
12
+ });
13
+ useEffect(()=>{
14
+ if (getHeight() !== contentHeight) setHeight(contentHeight);
15
+ if (scrollAreaRef.current && !getScrollAreaRef()) setScrollAreaRef(scrollAreaRef.current);
16
+ }, [
17
+ contentHeight
18
+ ]);
19
+ return /*#__PURE__*/ jsx(ScrollArea, {
20
+ viewportRef: scrollAreaRef,
21
+ h: height,
22
+ classNames: {
23
+ viewport: 'rolder-scroll-area-viewport',
24
+ scrollbar: 'rolder-scroll-area-scrollbar'
25
+ },
26
+ style: {
27
+ '--radius': radius ? getRadius(radius) : void 0
28
+ },
29
+ onScrollPositionChange: ()=>{
30
+ if (getHasScrollableContent()) {
31
+ const current = isAboveCenter();
32
+ if (getIsAboveCenter() !== current) setIsAboveCenter(current);
33
+ }
34
+ },
35
+ ...props,
36
+ children: /*#__PURE__*/ jsx(Box, {
37
+ ref: boxRef,
38
+ children: children
39
+ })
40
+ });
41
+ };
42
+ export { Root };
@@ -0,0 +1,10 @@
1
+ import { type ReactNode } from '@tabler/icons-react';
2
+ export interface ScrollButtonProps {
3
+ /** CSS классы для стилизации кнопки */
4
+ className?: string;
5
+ /** Иконка для прокрутки вверх (по умолчанию IconChevronUp) */
6
+ upIcon?: ReactNode;
7
+ /** Иконка для прокрутки вниз (по умолчанию IconChevronDown) */
8
+ downIcon?: ReactNode;
9
+ }
10
+ export declare const ScrollButton: ({ className, upIcon, downIcon, }: ScrollButtonProps) => import("react/jsx-runtime").JSX.Element | null;
@@ -1,11 +1,12 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { ActionIcon } from "@mantine/core";
3
3
  import { IconChevronDown, IconChevronUp } from "@tabler/icons-react";
4
- import { useScrollArea } from "./context.js";
5
- const ScrollAreaButton = ({ className, upIcon, downIcon })=>{
6
- const { hasScrollableContent, isAboveCenter, scrollToTop, scrollToBottom } = useScrollArea();
4
+ import { scrollToBottom, scrollToTop } from "./methods.js";
5
+ import { useHasScrollableContent, useIsAboveCenter } from "./store.js";
6
+ const ScrollButton = ({ className, upIcon, downIcon })=>{
7
+ const hasScrollableContent = useHasScrollableContent();
8
+ const isScrollingDown = useIsAboveCenter();
7
9
  if (!hasScrollableContent) return null;
8
- const isScrollingDown = isAboveCenter;
9
10
  const icon = isScrollingDown ? downIcon ?? /*#__PURE__*/ jsx(IconChevronDown, {
10
11
  strokeWidth: 1.5
11
12
  }) : upIcon ?? /*#__PURE__*/ jsx(IconChevronUp, {
@@ -26,5 +27,4 @@ const ScrollAreaButton = ({ className, upIcon, downIcon })=>{
26
27
  children: icon
27
28
  });
28
29
  };
29
- ScrollAreaButton.displayName = 'ScrollArea.ScrollButton';
30
- export { ScrollAreaButton };
30
+ export { ScrollButton };
@@ -1,3 +1,6 @@
1
- export { useScrollArea } from './context';
2
- export { ScrollArea } from './ScrollArea';
3
- export type { ScrollAreaContextValue, ScrollAreaHook, ScrollAreaProps, ScrollAreaState, ScrollButtonProps, ScrollPosition, } from './types';
1
+ export declare const ScrollArea: {
2
+ Root: ({ children, height, radius, autoScroll, ...props }: import("./Root").ScrollAreaProps) => import("react/jsx-runtime").JSX.Element;
3
+ ScrollButton: ({ className, upIcon, downIcon, }: import("./ScrollButton").ScrollButtonProps) => import("react/jsx-runtime").JSX.Element | null;
4
+ };
5
+ export type * from './Root';
6
+ export type * from './ScrollButton';
@@ -1,3 +1,7 @@
1
- import { useScrollArea } from "./context.js";
2
- import { ScrollArea } from "./ScrollArea.js";
3
- export { ScrollArea, useScrollArea };
1
+ import { Root } from "./Root.js";
2
+ import { ScrollButton } from "./ScrollButton.js";
3
+ const ScrollArea = {
4
+ Root: Root,
5
+ ScrollButton: ScrollButton
6
+ };
7
+ export { ScrollArea };
@@ -0,0 +1,4 @@
1
+ export declare const scrollToBottom: () => void;
2
+ export declare const scrollToTop: () => void;
3
+ export declare const isAboveCenter: () => boolean;
4
+ export declare const hasScrollableContent: () => boolean;
@@ -0,0 +1,32 @@
1
+ import { getScrollAreaRef } from "./store.js";
2
+ const scrollToBottom = ()=>{
3
+ const ref = getScrollAreaRef();
4
+ if (!ref) return;
5
+ ref.scrollTo({
6
+ top: ref.scrollHeight,
7
+ behavior: 'smooth'
8
+ });
9
+ };
10
+ const scrollToTop = ()=>{
11
+ const ref = getScrollAreaRef();
12
+ if (!ref) return;
13
+ ref.scrollTo({
14
+ top: 0,
15
+ behavior: 'smooth'
16
+ });
17
+ };
18
+ const isAboveCenter = ()=>{
19
+ const ref = getScrollAreaRef();
20
+ if (!ref) return false;
21
+ const { scrollTop, scrollHeight, clientHeight } = ref;
22
+ const viewportCenter = scrollTop + clientHeight / 2;
23
+ const contentCenter = scrollHeight / 2;
24
+ return viewportCenter < contentCenter;
25
+ };
26
+ const hasScrollableContent = ()=>{
27
+ const ref = getScrollAreaRef();
28
+ if (!ref) return false;
29
+ const { scrollHeight, clientHeight } = ref;
30
+ return scrollHeight > clientHeight;
31
+ };
32
+ export { hasScrollableContent, isAboveCenter, scrollToBottom, scrollToTop };
@@ -0,0 +1,12 @@
1
+ export declare const getScrollAreaRef: () => HTMLElement | null;
2
+ export declare const setScrollAreaRef: (scrollAreaRef: HTMLElement | null) => void;
3
+ export declare const getHeight: () => number;
4
+ export declare const setHeight: (height: number) => void;
5
+ export declare const getAutoScroll: () => boolean;
6
+ export declare const setAutoScroll: (autoScroll: boolean) => void;
7
+ export declare const getHasScrollableContent: () => boolean;
8
+ export declare const useHasScrollableContent: () => boolean;
9
+ export declare const setHasScrollableContent: (hasScrollableContent: boolean) => void;
10
+ export declare const getIsAboveCenter: () => boolean;
11
+ export declare const useIsAboveCenter: () => boolean;
12
+ export declare const setIsAboveCenter: (isAboveCenter: boolean) => void;
@@ -0,0 +1,25 @@
1
+ import { useStore } from "@nanostores/react";
2
+ import { atom } from "nanostores";
3
+ import { hasScrollableContent as external_methods_js_hasScrollableContent, scrollToBottom } from "./methods.js";
4
+ const $scrollAreaRef = atom(null);
5
+ const getScrollAreaRef = ()=>$scrollAreaRef.get();
6
+ const setScrollAreaRef = (scrollAreaRef)=>$scrollAreaRef.set(scrollAreaRef);
7
+ const $height = atom(0);
8
+ const getHeight = ()=>$height.get();
9
+ const setHeight = (height)=>$height.set(height);
10
+ const $autoScroll = atom(false);
11
+ const getAutoScroll = ()=>$autoScroll.get();
12
+ const setAutoScroll = (autoScroll)=>$autoScroll.set(autoScroll);
13
+ const $hasScrollableContent = atom(false);
14
+ const getHasScrollableContent = ()=>$hasScrollableContent.get();
15
+ const useHasScrollableContent = ()=>useStore($hasScrollableContent);
16
+ const setHasScrollableContent = (hasScrollableContent)=>$hasScrollableContent.set(hasScrollableContent);
17
+ const $isAboveCenter = atom(false);
18
+ const getIsAboveCenter = ()=>$isAboveCenter.get();
19
+ const useIsAboveCenter = ()=>useStore($isAboveCenter);
20
+ const setIsAboveCenter = (isAboveCenter)=>$isAboveCenter.set(isAboveCenter);
21
+ $height.listen((height)=>{
22
+ if (getAutoScroll() && height) scrollToBottom();
23
+ if (external_methods_js_hasScrollableContent() && !getHasScrollableContent()) setHasScrollableContent(true);
24
+ });
25
+ export { getAutoScroll, getHasScrollableContent, getHeight, getIsAboveCenter, getScrollAreaRef, setAutoScroll, setHasScrollableContent, setHeight, setIsAboveCenter, setScrollAreaRef, useHasScrollableContent, useIsAboveCenter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rolder/kit",
3
- "version": "3.0.0-alpha.76",
3
+ "version": "3.0.0-alpha.78",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -41,10 +41,10 @@
41
41
  "@mantine/tiptap": "^8.3.12",
42
42
  "@nanostores/react": "^1.0.0",
43
43
  "@tanstack/react-form": "^1.27.7",
44
- "@tanstack/react-query": "^5.90.18",
45
- "@tanstack/react-router": "^1.151.0",
46
- "@tanstack/react-router-ssr-query": "^1.151.0",
47
- "@tanstack/react-start": "^1.151.0",
44
+ "@tanstack/react-query": "^5.90.19",
45
+ "@tanstack/react-router": "^1.151.1",
46
+ "@tanstack/react-router-ssr-query": "^1.151.1",
47
+ "@tanstack/react-start": "^1.151.1",
48
48
  "@tiptap/extension-highlight": "^3.15.3",
49
49
  "@tiptap/extension-placeholder": "^3.15.3",
50
50
  "@tiptap/extension-table": "^3.15.3",
@@ -1,62 +0,0 @@
1
- import type { ScrollAreaContextValue, ScrollAreaProps } from './types';
2
- /**
3
- * ScrollArea Component - Расширенная область прокрутки с автоскроллом
4
- *
5
- * @example
6
- * // Базовое использование
7
- * <ScrollArea h={300}>
8
- * <div>Контент для прокрутки</div>
9
- * </ScrollArea>
10
- *
11
- * @example
12
- * // С автоскроллом и кнопкой
13
- * <ScrollArea autoScroll scrollToBottomOnInit h={400}>
14
- * <div>Чат сообщения...</div>
15
- * <ScrollArea.ScrollButton className="absolute bottom-4 right-4" />
16
- * </ScrollArea>
17
- *
18
- * @example
19
- * // С кастомными настройками
20
- * <ScrollArea
21
- * autoScroll={true}
22
- * animated={true}
23
- * nearThreshold={50}
24
- * h={300}
25
- * scrollbarSize={6}
26
- * type="hover"
27
- * >
28
- * {messages.map(msg => <div key={msg.id}>{msg.text}</div>)}
29
- * <ScrollArea.ScrollButton
30
- * className="absolute bottom-4 right-4"
31
- * upIcon={<CustomUpIcon />}
32
- * downIcon={<CustomDownIcon />}
33
- * />
34
- * </ScrollArea>
35
- *
36
- * @example
37
- * // Использование хука для доступа к viewport
38
- * function CustomControl() {
39
- * const { isAtBottom, scrollToBottom, viewportRef } = useScrollArea();
40
- *
41
- * const scrollToElement = () => {
42
- * const target = viewportRef.current?.querySelector('[data-target]');
43
- * target?.scrollIntoView();
44
- * };
45
- *
46
- * return (
47
- * <button onClick={scrollToBottom} disabled={isAtBottom}>
48
- * Прокрутить вниз
49
- * </button>
50
- * );
51
- * }
52
- */
53
- export declare const ScrollArea: {
54
- ({ children, autoScroll, scrollToBottomOnInit, animated, nearThreshold, radius, ...mantineProps }: ScrollAreaProps): import("react/jsx-runtime").JSX.Element;
55
- displayName: string;
56
- } & {
57
- ScrollButton: {
58
- ({ className, upIcon, downIcon, }: import("./types").ScrollButtonProps): import("react/jsx-runtime").JSX.Element | null;
59
- displayName: string;
60
- };
61
- Provider: import("react").Provider<ScrollAreaContextValue | null>;
62
- };
@@ -1,30 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { ScrollAreaProvider } from "./context.js";
3
- import { ScrollAreaButton } from "./ScrollAreaButton.js";
4
- import { ScrollAreaContent } from "./ScrollAreaContent.js";
5
- import { useScrollAreaState } from "./useScrollArea.js";
6
- const ScrollAreaRoot = ({ children, autoScroll = false, scrollToBottomOnInit = false, animated = true, nearThreshold = 100, radius, ...mantineProps })=>{
7
- const scrollAreaValue = useScrollAreaState({
8
- autoScroll,
9
- scrollToBottomOnInit,
10
- animated,
11
- nearThreshold
12
- });
13
- const contextValue = {
14
- ...scrollAreaValue,
15
- radius,
16
- mantineProps
17
- };
18
- return /*#__PURE__*/ jsx(ScrollAreaProvider, {
19
- value: contextValue,
20
- children: /*#__PURE__*/ jsx(ScrollAreaContent, {
21
- children: children
22
- })
23
- });
24
- };
25
- ScrollAreaRoot.displayName = 'ScrollArea';
26
- const ScrollArea = Object.assign(ScrollAreaRoot, {
27
- ScrollButton: ScrollAreaButton,
28
- Provider: ScrollAreaProvider
29
- });
30
- export { ScrollArea };
@@ -1,5 +0,0 @@
1
- import type { ScrollButtonProps } from './types';
2
- export declare const ScrollAreaButton: {
3
- ({ className, upIcon, downIcon, }: ScrollButtonProps): import("react/jsx-runtime").JSX.Element | null;
4
- displayName: string;
5
- };
@@ -1,6 +0,0 @@
1
- export declare const ScrollAreaContent: {
2
- ({ children, }: {
3
- children: React.ReactNode;
4
- }): import("react/jsx-runtime").JSX.Element;
5
- displayName: string;
6
- };
@@ -1,29 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { Box, ScrollArea, getRadius } from "@mantine/core";
3
- import { useContext } from "react";
4
- import { ScrollAreaContext } from "./context.js";
5
- const ScrollAreaContent = ({ children })=>{
6
- const fullContext = useContext(ScrollAreaContext);
7
- if (!fullContext) throw new Error('ScrollAreaContent must be used within ScrollArea');
8
- const { _callbackRef, mantineProps, radius, _contentResizeRef } = fullContext;
9
- return /*#__PURE__*/ jsx(ScrollArea, {
10
- classNames: {
11
- viewport: 'rolder-scroll-area-viewport',
12
- content: 'rolder-scroll-area-content',
13
- scrollbar: 'rolder-scroll-area-scrollbar'
14
- },
15
- style: {
16
- '--radius': radius ? getRadius(radius) : void 0
17
- },
18
- ...mantineProps,
19
- viewportRef: _callbackRef,
20
- children: /*#__PURE__*/ jsx(Box, {
21
- h: "100%",
22
- w: "100%",
23
- ref: _contentResizeRef,
24
- children: children
25
- })
26
- });
27
- };
28
- ScrollAreaContent.displayName = 'ScrollArea.Content';
29
- export { ScrollAreaContent };
@@ -1,28 +0,0 @@
1
- import type { ScrollAreaContextValue, ScrollAreaHook } from './types';
2
- /** Внутренний контекст для передачи состояния ScrollArea */
3
- export declare const ScrollAreaContext: import("react").Context<ScrollAreaContextValue | null>;
4
- export declare const ScrollAreaProvider: import("react").Provider<ScrollAreaContextValue | null>;
5
- /**
6
- * Хук для доступа к состоянию и методам ScrollArea
7
- *
8
- * @returns Объект с индикаторами позиции, методами управления и ref на viewport
9
- *
10
- * @example
11
- * ```tsx
12
- * function MyComponent() {
13
- * const { isAtBottom, scrollToTop, viewportRef } = useScrollArea();
14
- *
15
- * const scrollToElement = () => {
16
- * const target = viewportRef.current?.querySelector('[data-target]');
17
- * target?.scrollIntoView();
18
- * };
19
- *
20
- * return (
21
- * <button onClick={scrollToTop} disabled={isAtTop}>
22
- * В начало
23
- * </button>
24
- * );
25
- * }
26
- * ```
27
- */
28
- export declare const useScrollArea: () => ScrollAreaHook;
@@ -1,10 +0,0 @@
1
- import { createContext, useContext } from "react";
2
- const ScrollAreaContext = /*#__PURE__*/ createContext(null);
3
- const ScrollAreaProvider = ScrollAreaContext.Provider;
4
- const useScrollArea = ()=>{
5
- const context = useContext(ScrollAreaContext);
6
- if (!context) throw new Error('useScrollArea must be used within ScrollArea');
7
- const { _callbackRef, mantineProps: _, radius: __, ...publicAPI } = context;
8
- return publicAPI;
9
- };
10
- export { ScrollAreaContext, ScrollAreaProvider, useScrollArea };
@@ -1,65 +0,0 @@
1
- import type { MantineRadius, ScrollAreaProps as MantineScrollAreaProps } from '@mantine/core';
2
- import type { ReactNode } from 'react';
3
- export interface ScrollAreaProps extends Omit<MantineScrollAreaProps, 'children'> {
4
- /** Включение автоскролла при добавлении контента (по умолчанию false) */
5
- autoScroll?: boolean;
6
- /** Прокрутить к концу при инициализации компонента (по умолчанию false) */
7
- scrollToBottomOnInit?: boolean;
8
- /** Анимированная прокрутка (по умолчанию true) */
9
- animated?: boolean;
10
- /** Отступ для near-зон в пикселях (по умолчанию 100) */
11
- nearThreshold?: number;
12
- /** Радиус для viewport и scrollbar на тот случай, когда они "торчат" углами*/
13
- radius?: MantineRadius;
14
- /** Дочерние элементы - автоматически оборачиваются в ScrollArea.Content */
15
- children: ReactNode;
16
- }
17
- export interface ScrollAreaHook {
18
- /** Точно в начале (scrollTop === 0) */
19
- isAtTop: boolean;
20
- /** В пределах nearThreshold от начала */
21
- isNearTop: boolean;
22
- /** Точно в конце (scrollTop + clientHeight >= scrollHeight) */
23
- isAtBottom: boolean;
24
- /** В пределах nearThreshold от конца */
25
- isNearBottom: boolean;
26
- /** Выше центральной точки области прокрутки */
27
- isAboveCenter: boolean;
28
- /** Есть контент требующий прокрутки */
29
- hasScrollableContent: boolean;
30
- /** Прокрутить к началу области */
31
- scrollToTop: (animated?: boolean) => void;
32
- /** Прокрутить к концу области */
33
- scrollToBottom: (animated?: boolean) => void;
34
- /** React ref на viewport элемент для прямого доступа к DOM */
35
- viewportRef: React.RefObject<HTMLDivElement | null>;
36
- }
37
- export interface ScrollAreaState extends ScrollAreaHook {
38
- /** Внутренний callback ref для подключения к Mantine ScrollArea */
39
- _callbackRef: (node: HTMLDivElement | null) => void;
40
- /** Внутренний ref для наблюдения за изменениями контента */
41
- _contentResizeRef: (node: HTMLDivElement | null) => void;
42
- }
43
- export interface ScrollAreaContextValue extends ScrollAreaState {
44
- /** Радиус для viewport и scrollbar на тот случай, когда они "торчат" углами*/
45
- radius?: MantineRadius;
46
- /** Пропсы Mantine ScrollArea, переданные в корневой компонент */
47
- mantineProps: Omit<MantineScrollAreaProps, 'children'>;
48
- }
49
- export interface ScrollButtonProps {
50
- /** CSS классы для стилизации кнопки */
51
- className?: string;
52
- /** Иконка для прокрутки вверх (по умолчанию IconChevronUp) */
53
- upIcon?: ReactNode;
54
- /** Иконка для прокрутки вниз (по умолчанию IconChevronDown) */
55
- downIcon?: ReactNode;
56
- }
57
- /** Внутренний тип для отслеживания позиции скролла */
58
- export interface ScrollPosition {
59
- /** Текущая позиция скролла сверху */
60
- scrollTop: number;
61
- /** Высота видимой области */
62
- clientHeight: number;
63
- /** Общая высота прокручиваемого контента */
64
- scrollHeight: number;
65
- }
File without changes
@@ -1,9 +0,0 @@
1
- import type { ScrollAreaState } from './types';
2
- interface UseScrollAreaOptions {
3
- autoScroll?: boolean;
4
- scrollToBottomOnInit?: boolean;
5
- animated?: boolean;
6
- nearThreshold?: number;
7
- }
8
- export declare const useScrollAreaState: (options?: UseScrollAreaOptions) => ScrollAreaState;
9
- export {};
@@ -1,146 +0,0 @@
1
- import { useDebouncedCallback, useMergedRef, useResizeObserver } from "@mantine/hooks";
2
- import { useCallback, useEffect, useRef, useState } from "react";
3
- const useScrollAreaState = (options = {})=>{
4
- const { autoScroll = false, scrollToBottomOnInit = false, animated = true, nearThreshold = 32 } = options;
5
- const scrollAreaRef = useRef(null);
6
- const isUserInteractingRef = useRef(false);
7
- const userInteractionTimeoutRef = useRef(void 0);
8
- const isInitializedRef = useRef(false);
9
- const [contentResizeRef, contentRect] = useResizeObserver();
10
- const [scrollPosition, setScrollPosition] = useState({
11
- scrollTop: 0,
12
- clientHeight: 0,
13
- scrollHeight: 0
14
- });
15
- const isAtTop = 0 === scrollPosition.scrollTop;
16
- const isAtBottom = scrollPosition.scrollTop + scrollPosition.clientHeight >= scrollPosition.scrollHeight;
17
- const isNearTop = scrollPosition.scrollTop <= nearThreshold;
18
- const isNearBottom = scrollPosition.scrollTop + scrollPosition.clientHeight >= scrollPosition.scrollHeight - nearThreshold;
19
- const isAboveCenter = scrollPosition.scrollTop < (scrollPosition.scrollHeight - scrollPosition.clientHeight) / 2;
20
- const hasScrollableContent = scrollPosition.scrollHeight > scrollPosition.clientHeight;
21
- const debouncedUpdatePosition = useDebouncedCallback((element)=>{
22
- const newPosition = {
23
- scrollTop: element.scrollTop,
24
- clientHeight: element.clientHeight,
25
- scrollHeight: element.scrollHeight
26
- };
27
- setScrollPosition(newPosition);
28
- }, {
29
- delay: 16
30
- });
31
- const scrollToTop = useCallback((isAnimated)=>{
32
- const element = scrollAreaRef.current;
33
- if (!element) return;
34
- const shouldAnimate = isAnimated ?? animated;
35
- if (shouldAnimate) element.scrollTo({
36
- top: 0,
37
- behavior: 'smooth'
38
- });
39
- else element.scrollTop = 0;
40
- }, [
41
- animated
42
- ]);
43
- const scrollToBottom = useCallback((isAnimated)=>{
44
- const element = scrollAreaRef.current;
45
- if (!element) return;
46
- const shouldAnimate = isAnimated ?? animated;
47
- if (shouldAnimate) element.scrollTo({
48
- top: element.scrollHeight,
49
- behavior: 'smooth'
50
- });
51
- else element.scrollTop = element.scrollHeight;
52
- const scrollHeightBefore = element.scrollHeight;
53
- setTimeout(()=>{
54
- const scrollHeightAfter = element.scrollHeight;
55
- const diff = element.scrollHeight - (element.scrollTop + element.clientHeight);
56
- if (diff > 1 && scrollHeightAfter !== scrollHeightBefore) element.scrollTop = element.scrollHeight;
57
- }, 50);
58
- }, [
59
- animated
60
- ]);
61
- const performAutoScroll = useCallback(()=>{
62
- if (!autoScroll || isUserInteractingRef.current || !scrollAreaRef.current) return;
63
- if (isNearBottom) scrollToBottom(false);
64
- }, [
65
- autoScroll,
66
- isNearBottom,
67
- scrollToBottom
68
- ]);
69
- const handleUserInteraction = useCallback(()=>{
70
- isUserInteractingRef.current = true;
71
- if (userInteractionTimeoutRef.current) clearTimeout(userInteractionTimeoutRef.current);
72
- userInteractionTimeoutRef.current = setTimeout(()=>{
73
- isUserInteractingRef.current = false;
74
- }, 150);
75
- }, []);
76
- const handleScroll = useCallback((event)=>{
77
- const element = event.target;
78
- debouncedUpdatePosition(element);
79
- }, [
80
- debouncedUpdatePosition
81
- ]);
82
- useEffect(()=>{
83
- if (!scrollToBottomOnInit || isInitializedRef.current) return;
84
- if (hasScrollableContent) {
85
- scrollToBottom(animated);
86
- isInitializedRef.current = true;
87
- }
88
- }, [
89
- scrollToBottomOnInit,
90
- hasScrollableContent,
91
- scrollToBottom,
92
- animated
93
- ]);
94
- useEffect(()=>{
95
- const element = scrollAreaRef.current;
96
- if (!element || 0 === contentRect.height) return;
97
- debouncedUpdatePosition(element);
98
- const timeoutId = setTimeout(performAutoScroll, 10);
99
- return ()=>clearTimeout(timeoutId);
100
- }, [
101
- contentRect.height,
102
- debouncedUpdatePosition,
103
- performAutoScroll
104
- ]);
105
- useEffect(()=>{
106
- const element = scrollAreaRef.current;
107
- if (!element) return;
108
- const events = [
109
- 'wheel',
110
- 'touchstart',
111
- 'touchmove',
112
- 'mousedown'
113
- ];
114
- element.addEventListener('scroll', handleScroll);
115
- events.forEach((event)=>{
116
- element.addEventListener(event, handleUserInteraction);
117
- });
118
- debouncedUpdatePosition(element);
119
- return ()=>{
120
- element.removeEventListener('scroll', handleScroll);
121
- events.forEach((event)=>{
122
- element.removeEventListener(event, handleUserInteraction);
123
- });
124
- if (userInteractionTimeoutRef.current) clearTimeout(userInteractionTimeoutRef.current);
125
- };
126
- }, [
127
- handleScroll,
128
- handleUserInteraction,
129
- debouncedUpdatePosition
130
- ]);
131
- const callbackRef = useMergedRef(scrollAreaRef);
132
- return {
133
- isAtTop,
134
- isNearTop,
135
- isAtBottom,
136
- isNearBottom,
137
- isAboveCenter,
138
- hasScrollableContent,
139
- scrollToTop,
140
- scrollToBottom,
141
- viewportRef: scrollAreaRef,
142
- _callbackRef: callbackRef,
143
- _contentResizeRef: contentResizeRef
144
- };
145
- };
146
- export { useScrollAreaState };