@rolder/kit 3.0.0-alpha.6 → 3.0.0-alpha.8
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 +3 -3
- package/rslib.config.ts +21 -0
- package/src/ai/ui/conversation/ConversationContext.ts +21 -0
- package/src/ai/ui/conversation/ConversationProvider.tsx +21 -0
- package/src/ai/ui/conversation/Empty.tsx +15 -0
- package/src/ai/ui/conversation/File.tsx +40 -0
- package/src/ai/ui/conversation/FileIcon.tsx +143 -0
- package/src/ai/ui/conversation/Loader.tsx +8 -0
- package/src/ai/ui/conversation/Message.tsx +34 -0
- package/src/ai/ui/conversation/Root.tsx +24 -0
- package/src/ai/ui/conversation/index.ts +16 -0
- package/{dist/ai/ui/conversation/types.d.ts → src/ai/ui/conversation/types.ts} +5 -4
- package/src/ai/ui/conversation/useChatMessage.ts +13 -0
- package/src/ai/ui/promptInput/File.tsx +98 -0
- package/src/ai/ui/promptInput/FileIcon.tsx +149 -0
- package/src/ai/ui/promptInput/Footer.tsx +5 -0
- package/src/ai/ui/promptInput/PromptInputContext.ts +24 -0
- package/src/ai/ui/promptInput/PromptInputProvider.tsx +54 -0
- package/src/ai/ui/promptInput/Root.tsx +29 -0
- package/src/ai/ui/promptInput/Submit.tsx +39 -0
- package/src/ai/ui/promptInput/Textarea.tsx +39 -0
- package/src/ai/ui/promptInput/index.ts +15 -0
- package/src/ai/ui/promptInput/types.ts +9 -0
- package/src/ai/utils/convertFileUIPartBlobToDataURL.ts +29 -0
- package/src/ai/utils/parseAiMessagePart.ts +19 -0
- package/src/app/AppDefaults.tsx +21 -0
- package/src/app/DefaultApp.tsx +50 -0
- package/src/app/cookieColorSchemeManager.ts +70 -0
- package/src/app/defaultRequestMiddlewares.ts +22 -0
- package/src/app/defaultTheme.ts +22 -0
- package/src/functions/getCookie.ts +36 -0
- package/src/functions/setCookie.ts +29 -0
- package/src/functions/setCookies.ts +24 -0
- package/src/hooks/useMutation.ts +14 -0
- package/src/hooks/useMutationWithInvalidate.ts +23 -0
- package/{dist/index.d.ts → src/index.ts} +45 -5
- package/{dist → src}/styles.css +21 -11
- package/src/surreal/connection.ts +72 -0
- package/src/surreal/deafaultCrud.ts +25 -0
- package/src/surreal/deserialize.ts +144 -0
- package/src/surreal/encryption.ts +51 -0
- package/src/ui/AnimatedChevron.tsx +32 -0
- package/src/ui/JsonInput.tsx +52 -0
- package/src/ui/RouterLink.tsx +78 -0
- package/src/ui/editor/Content.tsx +11 -0
- package/src/ui/editor/Provider.tsx +96 -0
- package/src/ui/editor/Root.tsx +25 -0
- package/src/ui/editor/Toolbar.tsx +92 -0
- package/src/ui/editor/index.ts +13 -0
- package/src/ui/editor/types.ts +7 -0
- package/src/ui/error/DefaultError.tsx +60 -0
- package/src/ui/error/DefaultNotFound.tsx +19 -0
- package/src/ui/error/Forbidden.tsx +18 -0
- package/src/ui/error/defaultErrorNotification.ts +9 -0
- package/src/ui/form/blurOnError.ts +21 -0
- package/src/ui/form/buttons/CancelButton.tsx +42 -0
- package/src/ui/form/buttons/SubmitButton.tsx +43 -0
- package/src/ui/form/buttons/SubscribeActionIcon.tsx +18 -0
- package/src/ui/form/buttons/SubscribeButton.tsx +17 -0
- package/src/ui/form/context.ts +45 -0
- package/src/ui/form/fields/JsonField.tsx +16 -0
- package/src/ui/form/fields/MultiSelectField.tsx +17 -0
- package/src/ui/form/fields/NumberField.tsx +17 -0
- package/src/ui/form/fields/PassowrdField.tsx +20 -0
- package/src/ui/form/fields/SelectField.tsx +17 -0
- package/src/ui/form/fields/SwitchField.tsx +17 -0
- package/src/ui/form/fields/TextField.tsx +17 -0
- package/src/ui/form/fields/TextPassowrdField.tsx +51 -0
- package/src/ui/form/fields/TextareaField.tsx +17 -0
- package/src/ui/form/fieldsSchema.ts +24 -0
- package/src/ui/hoverPaper/HoverPaper.tsx +17 -0
- package/src/ui/hoverPaper/usePaperHover.ts +9 -0
- package/src/ui/saveInput/JsonInput.tsx +40 -0
- package/src/ui/saveInput/NumberInput.tsx +40 -0
- package/src/ui/saveInput/SaveInput.tsx +15 -0
- package/src/ui/saveInput/Select.tsx +41 -0
- package/src/ui/saveInput/Switch.tsx +46 -0
- package/src/ui/saveInput/TextInput.tsx +40 -0
- package/src/ui/saveInput/Textarea.tsx +40 -0
- package/src/ui/scrollArea/ARCH.md +204 -0
- package/src/ui/scrollArea/README.md +369 -0
- package/{dist/ui/scrollArea/ScrollArea.d.ts → src/ui/scrollArea/ScrollArea.tsx} +41 -10
- package/src/ui/scrollArea/ScrollAreaButton.tsx +56 -0
- package/src/ui/scrollArea/ScrollAreaContent.tsx +36 -0
- package/{dist/ui/scrollArea/context.d.ts → src/ui/scrollArea/context.tsx} +18 -3
- package/src/ui/scrollArea/index.ts +10 -0
- package/src/ui/scrollArea/types.ts +77 -0
- package/src/ui/scrollArea/useScrollArea.ts +227 -0
- package/tsconfig.json +14 -0
- package/dist/ai/ui/conversation/ConversationContext.d.ts +0 -7
- package/dist/ai/ui/conversation/ConversationContext.js +0 -8
- package/dist/ai/ui/conversation/ConversationProvider.d.ts +0 -2
- package/dist/ai/ui/conversation/ConversationProvider.js +0 -14
- package/dist/ai/ui/conversation/Empty.d.ts +0 -1
- package/dist/ai/ui/conversation/Empty.js +0 -21
- package/dist/ai/ui/conversation/File.d.ts +0 -4
- package/dist/ai/ui/conversation/File.js +0 -42
- package/dist/ai/ui/conversation/FileIcon.d.ts +0 -3
- package/dist/ai/ui/conversation/FileIcon.js +0 -225
- package/dist/ai/ui/conversation/Loader.d.ts +0 -2
- package/dist/ai/ui/conversation/Loader.js +0 -12
- package/dist/ai/ui/conversation/Message.d.ts +0 -4
- package/dist/ai/ui/conversation/Message.js +0 -25
- package/dist/ai/ui/conversation/Root.d.ts +0 -2
- package/dist/ai/ui/conversation/Root.js +0 -26
- package/dist/ai/ui/conversation/index.d.ts +0 -13
- package/dist/ai/ui/conversation/index.js +0 -14
- package/dist/ai/ui/conversation/types.js +0 -0
- package/dist/ai/ui/conversation/useChatMessage.d.ts +0 -2
- package/dist/ai/ui/conversation/useChatMessage.js +0 -12
- package/dist/ai/ui/promptInput/File.d.ts +0 -2
- package/dist/ai/ui/promptInput/File.js +0 -117
- package/dist/ai/ui/promptInput/FileIcon.d.ts +0 -3
- package/dist/ai/ui/promptInput/FileIcon.js +0 -225
- package/dist/ai/ui/promptInput/Footer.d.ts +0 -2
- package/dist/ai/ui/promptInput/Footer.js +0 -8
- package/dist/ai/ui/promptInput/PromptInputContext.d.ts +0 -12
- package/dist/ai/ui/promptInput/PromptInputContext.js +0 -8
- package/dist/ai/ui/promptInput/PromptInputProvider.d.ts +0 -2
- package/dist/ai/ui/promptInput/PromptInputProvider.js +0 -50
- package/dist/ai/ui/promptInput/Root.d.ts +0 -3
- package/dist/ai/ui/promptInput/Root.js +0 -17
- package/dist/ai/ui/promptInput/Submit.d.ts +0 -2
- package/dist/ai/ui/promptInput/Submit.js +0 -40
- package/dist/ai/ui/promptInput/Textarea.d.ts +0 -2
- package/dist/ai/ui/promptInput/Textarea.js +0 -33
- package/dist/ai/ui/promptInput/index.d.ts +0 -8
- package/dist/ai/ui/promptInput/index.js +0 -13
- package/dist/ai/ui/promptInput/types.d.ts +0 -11
- package/dist/ai/ui/promptInput/types.js +0 -0
- package/dist/ai/utils/convertFileUIPartBlobToDataURL.d.ts +0 -5
- package/dist/ai/utils/convertFileUIPartBlobToDataURL.js +0 -21
- package/dist/ai/utils/parseAiMessagePart.d.ts +0 -2
- package/dist/ai/utils/parseAiMessagePart.js +0 -12
- package/dist/app/AppDefaults.d.ts +0 -3
- package/dist/app/AppDefaults.js +0 -27
- package/dist/app/DefaultApp.d.ts +0 -6
- package/dist/app/DefaultApp.js +0 -43
- package/dist/app/cookieColorSchemeManager.d.ts +0 -6
- package/dist/app/cookieColorSchemeManager.js +0 -46
- package/dist/app/defaultRequestMiddlewares.d.ts +0 -4
- package/dist/app/defaultRequestMiddlewares.js +0 -24
- package/dist/app/defaultTheme.d.ts +0 -141
- package/dist/app/defaultTheme.js +0 -24
- package/dist/functions/getCookie.d.ts +0 -3
- package/dist/functions/getCookie.js +0 -8
- package/dist/functions/setCookie.d.ts +0 -10
- package/dist/functions/setCookie.js +0 -19
- package/dist/functions/setCookies.d.ts +0 -14
- package/dist/functions/setCookies.js +0 -13
- package/dist/hooks/useMutation.d.ts +0 -4
- package/dist/hooks/useMutation.js +0 -8
- package/dist/hooks/useMutationWithInvalidate.d.ts +0 -4
- package/dist/hooks/useMutationWithInvalidate.js +0 -16
- package/dist/index.js +0 -26
- package/dist/surreal/connection.d.ts +0 -9
- package/dist/surreal/connection.js +0 -49
- package/dist/surreal/deafaultCrud.d.ts +0 -2
- package/dist/surreal/deafaultCrud.js +0 -18
- package/dist/surreal/deserialize.d.ts +0 -17
- package/dist/surreal/deserialize.js +0 -46
- package/dist/surreal/encryption.d.ts +0 -6
- package/dist/surreal/encryption.js +0 -30
- package/dist/ui/AnimatedChevron.d.ts +0 -6
- package/dist/ui/AnimatedChevron.js +0 -31
- package/dist/ui/JsonInput.d.ts +0 -2
- package/dist/ui/JsonInput.js +0 -45
- package/dist/ui/RouterLink.d.ts +0 -16
- package/dist/ui/RouterLink.js +0 -36
- package/dist/ui/editor/Content.d.ts +0 -3
- package/dist/ui/editor/Content.js +0 -13
- package/dist/ui/editor/Provider.d.ts +0 -17
- package/dist/ui/editor/Provider.js +0 -80
- package/dist/ui/editor/Root.d.ts +0 -2
- package/dist/ui/editor/Root.js +0 -18
- package/dist/ui/editor/Toolbar.d.ts +0 -5
- package/dist/ui/editor/Toolbar.js +0 -156
- package/dist/ui/editor/index.d.ts +0 -12
- package/dist/ui/editor/index.js +0 -11
- package/dist/ui/editor/types.d.ts +0 -7
- package/dist/ui/editor/types.js +0 -0
- package/dist/ui/error/DefaultError.d.ts +0 -2
- package/dist/ui/error/DefaultError.js +0 -62
- package/dist/ui/error/DefaultNotFound.d.ts +0 -1
- package/dist/ui/error/DefaultNotFound.js +0 -37
- package/dist/ui/error/Forbidden.d.ts +0 -1
- package/dist/ui/error/Forbidden.js +0 -32
- package/dist/ui/error/defaultErrorNotification.d.ts +0 -1
- package/dist/ui/error/defaultErrorNotification.js +0 -8
- package/dist/ui/error/index.js +0 -5
- package/dist/ui/form/blurOnError.d.ts +0 -4
- package/dist/ui/form/blurOnError.js +0 -11
- package/dist/ui/form/buttons/CancelButton.d.ts +0 -5
- package/dist/ui/form/buttons/CancelButton.js +0 -44
- package/dist/ui/form/buttons/SubmitButton.d.ts +0 -5
- package/dist/ui/form/buttons/SubmitButton.js +0 -47
- package/dist/ui/form/buttons/SubscribeActionIcon.d.ts +0 -4
- package/dist/ui/form/buttons/SubscribeActionIcon.js +0 -15
- package/dist/ui/form/buttons/SubscribeButton.d.ts +0 -5
- package/dist/ui/form/buttons/SubscribeButton.js +0 -16
- package/dist/ui/form/buttons/index.js +0 -4
- package/dist/ui/form/context.d.ts +0 -83
- package/dist/ui/form/context.js +0 -26
- package/dist/ui/form/fields/JsonField.d.ts +0 -2
- package/dist/ui/form/fields/JsonField.js +0 -13
- package/dist/ui/form/fields/MultiSelectField.d.ts +0 -2
- package/dist/ui/form/fields/MultiSelectField.js +0 -15
- package/dist/ui/form/fields/NumberField.d.ts +0 -2
- package/dist/ui/form/fields/NumberField.js +0 -15
- package/dist/ui/form/fields/PassowrdField.d.ts +0 -2
- package/dist/ui/form/fields/PassowrdField.js +0 -18
- package/dist/ui/form/fields/SelectField.d.ts +0 -2
- package/dist/ui/form/fields/SelectField.js +0 -15
- package/dist/ui/form/fields/SwitchField.d.ts +0 -2
- package/dist/ui/form/fields/SwitchField.js +0 -15
- package/dist/ui/form/fields/TextField.d.ts +0 -2
- package/dist/ui/form/fields/TextField.js +0 -15
- package/dist/ui/form/fields/TextPassowrdField.d.ts +0 -2
- package/dist/ui/form/fields/TextPassowrdField.js +0 -51
- package/dist/ui/form/fields/TextareaField.d.ts +0 -2
- package/dist/ui/form/fields/TextareaField.js +0 -15
- package/dist/ui/form/fields/index.js +0 -9
- package/dist/ui/form/fieldsSchema.d.ts +0 -12
- package/dist/ui/form/fieldsSchema.js +0 -13
- package/dist/ui/form/index.js +0 -4
- package/dist/ui/hoverPaper/HoverPaper.d.ts +0 -6
- package/dist/ui/hoverPaper/HoverPaper.js +0 -15
- package/dist/ui/hoverPaper/index.js +0 -3
- package/dist/ui/hoverPaper/usePaperHover.d.ts +0 -4
- package/dist/ui/hoverPaper/usePaperHover.js +0 -9
- package/dist/ui/saveInput/JsonInput.d.ts +0 -6
- package/dist/ui/saveInput/JsonInput.js +0 -34
- package/dist/ui/saveInput/NumberInput.d.ts +0 -6
- package/dist/ui/saveInput/NumberInput.js +0 -27
- package/dist/ui/saveInput/SaveInput.d.ts +0 -36
- package/dist/ui/saveInput/SaveInput.js +0 -15
- package/dist/ui/saveInput/Select.d.ts +0 -6
- package/dist/ui/saveInput/Select.js +0 -27
- package/dist/ui/saveInput/Switch.d.ts +0 -6
- package/dist/ui/saveInput/Switch.js +0 -30
- package/dist/ui/saveInput/TextInput.d.ts +0 -6
- package/dist/ui/saveInput/TextInput.js +0 -26
- package/dist/ui/saveInput/Textarea.d.ts +0 -6
- package/dist/ui/saveInput/Textarea.js +0 -26
- package/dist/ui/saveInput/index.js +0 -2
- package/dist/ui/scrollArea/ScrollArea.js +0 -30
- package/dist/ui/scrollArea/ScrollAreaButton.d.ts +0 -5
- package/dist/ui/scrollArea/ScrollAreaButton.js +0 -51
- package/dist/ui/scrollArea/ScrollAreaContent.d.ts +0 -6
- package/dist/ui/scrollArea/ScrollAreaContent.js +0 -29
- package/dist/ui/scrollArea/context.js +0 -10
- package/dist/ui/scrollArea/index.d.ts +0 -3
- package/dist/ui/scrollArea/index.js +0 -3
- package/dist/ui/scrollArea/types.d.ts +0 -65
- package/dist/ui/scrollArea/types.js +0 -0
- package/dist/ui/scrollArea/useScrollArea.d.ts +0 -9
- package/dist/ui/scrollArea/useScrollArea.js +0 -146
- /package/{dist/ui/error/index.d.ts → src/ui/error/index.ts} +0 -0
- /package/{dist/ui/form/buttons/index.d.ts → src/ui/form/buttons/index.ts} +0 -0
- /package/{dist/ui/form/fields/index.d.ts → src/ui/form/fields/index.ts} +0 -0
- /package/{dist/ui/form/index.d.ts → src/ui/form/index.ts} +0 -0
- /package/{dist/ui/hoverPaper/index.d.ts → src/ui/hoverPaper/index.ts} +0 -0
- /package/{dist/ui/saveInput/index.d.ts → src/ui/saveInput/index.ts} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Box, Loader } from '@mantine/core';
|
|
2
|
+
import { useDebouncedCallback } from '@mantine/hooks';
|
|
3
|
+
import type { ReactCodeMirrorProps } from '@uiw/react-codemirror';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { JsonInput as JsonInputComponent } from '../JsonInput';
|
|
6
|
+
|
|
7
|
+
export const JsonInput = ({
|
|
8
|
+
initialValue,
|
|
9
|
+
onChange,
|
|
10
|
+
debounce = 300,
|
|
11
|
+
...props
|
|
12
|
+
}: {
|
|
13
|
+
initialValue: string;
|
|
14
|
+
onChange: (value: string) => Promise<void>;
|
|
15
|
+
debounce?: number;
|
|
16
|
+
} & Omit<ReactCodeMirrorProps, 'value' | 'onChange'>) => {
|
|
17
|
+
const [value, setValue] = useState(initialValue);
|
|
18
|
+
const [loading, setLoading] = useState(false);
|
|
19
|
+
|
|
20
|
+
const handleSave = useDebouncedCallback(async (content: string) => {
|
|
21
|
+
setLoading(true);
|
|
22
|
+
await onChange(content);
|
|
23
|
+
setLoading(false);
|
|
24
|
+
}, debounce);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Box pos="relative">
|
|
28
|
+
<JsonInputComponent
|
|
29
|
+
value={value}
|
|
30
|
+
onChange={(e) => {
|
|
31
|
+
setValue(e);
|
|
32
|
+
handleSave(e);
|
|
33
|
+
}}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
{loading && <Loader size="sm" pos="absolute" top={8} left={8} />}
|
|
38
|
+
</Box>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Loader,
|
|
3
|
+
NumberInput as MantineNumberInput,
|
|
4
|
+
type NumberInputProps,
|
|
5
|
+
} from '@mantine/core';
|
|
6
|
+
import { useDebouncedCallback } from '@mantine/hooks';
|
|
7
|
+
import { useState } from 'react';
|
|
8
|
+
|
|
9
|
+
export const NumberInput = ({
|
|
10
|
+
initialValue,
|
|
11
|
+
onChange,
|
|
12
|
+
debounce = 300,
|
|
13
|
+
...props
|
|
14
|
+
}: {
|
|
15
|
+
initialValue: string | number;
|
|
16
|
+
onChange: (value: string | number) => Promise<void>;
|
|
17
|
+
debounce?: number;
|
|
18
|
+
} & Omit<NumberInputProps, 'value' | 'onChange'>) => {
|
|
19
|
+
const [value, setValue] = useState(initialValue);
|
|
20
|
+
const [loading, setLoading] = useState(false);
|
|
21
|
+
|
|
22
|
+
const handleSave = useDebouncedCallback(async (content: string | number) => {
|
|
23
|
+
setLoading(true);
|
|
24
|
+
await onChange(content);
|
|
25
|
+
setLoading(false);
|
|
26
|
+
}, debounce);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<MantineNumberInput
|
|
30
|
+
value={value}
|
|
31
|
+
onChange={(v) => {
|
|
32
|
+
setValue(v);
|
|
33
|
+
handleSave(v);
|
|
34
|
+
}}
|
|
35
|
+
flex={props.w ? undefined : 1}
|
|
36
|
+
rightSection={loading ? <Loader size="sm" mr={8} /> : undefined}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { JsonInput } from './JsonInput';
|
|
2
|
+
import { NumberInput } from './NumberInput';
|
|
3
|
+
import { Select } from './Select';
|
|
4
|
+
import { Switch } from './Switch';
|
|
5
|
+
import { Textarea } from './Textarea';
|
|
6
|
+
import { TextInput } from './TextInput';
|
|
7
|
+
|
|
8
|
+
export const SaveInput = Object.assign(TextInput, {
|
|
9
|
+
TextInput,
|
|
10
|
+
NumberInput,
|
|
11
|
+
Textarea,
|
|
12
|
+
Switch,
|
|
13
|
+
JsonInput,
|
|
14
|
+
Select,
|
|
15
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Loader,
|
|
3
|
+
Select as MantineSelect,
|
|
4
|
+
type SelectProps,
|
|
5
|
+
} from '@mantine/core';
|
|
6
|
+
import { useDebouncedCallback } from '@mantine/hooks';
|
|
7
|
+
import { useState } from 'react';
|
|
8
|
+
|
|
9
|
+
export const Select = ({
|
|
10
|
+
initialValue,
|
|
11
|
+
onChange,
|
|
12
|
+
debounce = 300,
|
|
13
|
+
...props
|
|
14
|
+
}: {
|
|
15
|
+
initialValue: string | null;
|
|
16
|
+
onChange: (value: string | null) => Promise<void>;
|
|
17
|
+
debounce?: number;
|
|
18
|
+
} & Omit<SelectProps, 'value' | 'onChange'>) => {
|
|
19
|
+
const [value, setValue] = useState(initialValue);
|
|
20
|
+
const [loading, setLoading] = useState(false);
|
|
21
|
+
|
|
22
|
+
const handleSave = useDebouncedCallback(async (content: string | null) => {
|
|
23
|
+
setLoading(true);
|
|
24
|
+
await onChange(content);
|
|
25
|
+
setLoading(false);
|
|
26
|
+
}, debounce);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<MantineSelect
|
|
30
|
+
value={value}
|
|
31
|
+
onChange={(v) => {
|
|
32
|
+
setValue(v);
|
|
33
|
+
handleSave(v);
|
|
34
|
+
}}
|
|
35
|
+
flex={props.w ? undefined : 1}
|
|
36
|
+
disabled={loading}
|
|
37
|
+
rightSection={loading ? <Loader size="sm" /> : undefined}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Box,
|
|
3
|
+
Loader,
|
|
4
|
+
Switch as MantineSwitch,
|
|
5
|
+
type SwitchProps,
|
|
6
|
+
} from '@mantine/core';
|
|
7
|
+
import { useDebouncedCallback } from '@mantine/hooks';
|
|
8
|
+
import { useState } from 'react';
|
|
9
|
+
|
|
10
|
+
export const Switch = ({
|
|
11
|
+
initialValue,
|
|
12
|
+
onChange,
|
|
13
|
+
debounce = 300,
|
|
14
|
+
w,
|
|
15
|
+
...props
|
|
16
|
+
}: {
|
|
17
|
+
initialValue: boolean;
|
|
18
|
+
onChange: (value: boolean) => Promise<void>;
|
|
19
|
+
debounce?: number;
|
|
20
|
+
} & Omit<SwitchProps, 'checked' | 'onChange'>) => {
|
|
21
|
+
const [value, setValue] = useState(initialValue);
|
|
22
|
+
const [loading, setLoading] = useState(false);
|
|
23
|
+
|
|
24
|
+
const handleSave = useDebouncedCallback(async (content: boolean) => {
|
|
25
|
+
setLoading(true);
|
|
26
|
+
await onChange(content);
|
|
27
|
+
setLoading(false);
|
|
28
|
+
}, debounce);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Box flex={1} w={w}>
|
|
32
|
+
{loading ? (
|
|
33
|
+
<Loader size="sm" mt={6.5} ml={8} />
|
|
34
|
+
) : (
|
|
35
|
+
<MantineSwitch
|
|
36
|
+
checked={value}
|
|
37
|
+
onChange={(e) => {
|
|
38
|
+
setValue(e.currentTarget.checked);
|
|
39
|
+
handleSave(e.currentTarget.checked);
|
|
40
|
+
}}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
44
|
+
</Box>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Loader,
|
|
3
|
+
TextInput as MantineTextInput,
|
|
4
|
+
type TextInputProps,
|
|
5
|
+
} from '@mantine/core';
|
|
6
|
+
import { useDebouncedCallback } from '@mantine/hooks';
|
|
7
|
+
import { useState } from 'react';
|
|
8
|
+
|
|
9
|
+
export const TextInput = ({
|
|
10
|
+
initialValue,
|
|
11
|
+
onChange,
|
|
12
|
+
debounce = 300,
|
|
13
|
+
...props
|
|
14
|
+
}: {
|
|
15
|
+
initialValue?: string;
|
|
16
|
+
onChange: (value: string) => Promise<void>;
|
|
17
|
+
debounce?: number;
|
|
18
|
+
} & Omit<TextInputProps, 'value' | 'onChange'>) => {
|
|
19
|
+
const [value, setValue] = useState(initialValue || '');
|
|
20
|
+
const [loading, setLoading] = useState(false);
|
|
21
|
+
|
|
22
|
+
const handleSave = useDebouncedCallback(async (content: string) => {
|
|
23
|
+
setLoading(true);
|
|
24
|
+
await onChange(content);
|
|
25
|
+
setLoading(false);
|
|
26
|
+
}, debounce);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<MantineTextInput
|
|
30
|
+
value={value}
|
|
31
|
+
onChange={(e) => {
|
|
32
|
+
setValue(e.target.value);
|
|
33
|
+
handleSave(e.target.value);
|
|
34
|
+
}}
|
|
35
|
+
flex={props.w ? undefined : 1}
|
|
36
|
+
rightSection={loading ? <Loader size="sm" /> : undefined}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Loader,
|
|
3
|
+
Textarea as MantineTextarea,
|
|
4
|
+
type TextareaProps,
|
|
5
|
+
} from '@mantine/core';
|
|
6
|
+
import { useDebouncedCallback } from '@mantine/hooks';
|
|
7
|
+
import { useState } from 'react';
|
|
8
|
+
|
|
9
|
+
export const Textarea = ({
|
|
10
|
+
initialValue,
|
|
11
|
+
onChange,
|
|
12
|
+
debounce = 300,
|
|
13
|
+
...props
|
|
14
|
+
}: {
|
|
15
|
+
initialValue: string;
|
|
16
|
+
onChange: (value: string) => Promise<void>;
|
|
17
|
+
debounce?: number;
|
|
18
|
+
} & Omit<TextareaProps, 'value' | 'onChange'>) => {
|
|
19
|
+
const [value, setValue] = useState(initialValue);
|
|
20
|
+
const [loading, setLoading] = useState(false);
|
|
21
|
+
|
|
22
|
+
const handleSave = useDebouncedCallback(async (content: string) => {
|
|
23
|
+
setLoading(true);
|
|
24
|
+
await onChange(content);
|
|
25
|
+
setLoading(false);
|
|
26
|
+
}, debounce);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<MantineTextarea
|
|
30
|
+
value={value}
|
|
31
|
+
onChange={(e) => {
|
|
32
|
+
setValue(e.target.value);
|
|
33
|
+
handleSave(e.target.value);
|
|
34
|
+
}}
|
|
35
|
+
flex={props.w ? undefined : 1}
|
|
36
|
+
rightSection={loading ? <Loader size="sm" /> : undefined}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# ScrollArea Component - Техническое задание
|
|
2
|
+
|
|
3
|
+
Компонент расширенной области прокрутки с автоматическим скроллом и интеллектуальным управлением позиционированием на базе Mantine Scrollarea.
|
|
4
|
+
|
|
5
|
+
## Архитектура компонента
|
|
6
|
+
|
|
7
|
+
### Составная структура
|
|
8
|
+
|
|
9
|
+
- `ScrollArea` - корневой контейнер с провайдером контекста
|
|
10
|
+
- `ScrollArea.ScrollButton` - опциональная адаптивная кнопка управления прокруткой
|
|
11
|
+
- `ScrollArea.Provider` - контекст для передачи состояния между компонентами
|
|
12
|
+
- `ScrollArea.Content` - внутренний приватный компонент (не экспортируется)
|
|
13
|
+
|
|
14
|
+
## Интерфейсы и типы
|
|
15
|
+
|
|
16
|
+
### Основные пропсы ScrollArea
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
interface ScrollAreaProps extends Omit<MantineScrollAreaProps, "children"> {
|
|
20
|
+
autoScroll?: boolean; // Включение автоскролла (по умолчанию false)
|
|
21
|
+
scrollToBottomOnInit?: boolean; // Прокрутить к концу при инициализации (по умолчанию false)
|
|
22
|
+
animated?: boolean; // Анимированная прокрутка (по умолчанию true)
|
|
23
|
+
nearThreshold?: number; // Отступ для near-зон в пикселях (по умолчанию 100)
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Публичный API (useScrollArea)
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
interface ScrollAreaHook {
|
|
32
|
+
// Индикаторы позиции
|
|
33
|
+
isAtTop: boolean; // Точно в начале (scrollTop === 0)
|
|
34
|
+
isNearTop: boolean; // В пределах nearThreshold от начала
|
|
35
|
+
isAtBottom: boolean; // Точно в конце (scrollTop + clientHeight >= scrollHeight)
|
|
36
|
+
isNearBottom: boolean; // В пределах nearThreshold от конца
|
|
37
|
+
isAboveCenter: boolean; // Выше центральной точки области прокрутки
|
|
38
|
+
hasScrollableContent: boolean; // Есть контент требующий прокрутки
|
|
39
|
+
|
|
40
|
+
// Методы управления
|
|
41
|
+
scrollToTop: (animated?: boolean) => void;
|
|
42
|
+
scrollToBottom: (animated?: boolean) => void;
|
|
43
|
+
|
|
44
|
+
// Доступ к viewport элементу
|
|
45
|
+
viewportRef: React.RefObject<HTMLDivElement | null>;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Внутреннее состояние (useScrollAreaState)
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
interface ScrollAreaState extends ScrollAreaHook {
|
|
53
|
+
_callbackRef: (node: HTMLDivElement | null) => void; // для подключения к Mantine
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface ScrollAreaContextValue extends ScrollAreaState {
|
|
57
|
+
mantineProps: Omit<MantineScrollAreaProps, "children">; // пропсы Mantine
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Логика отслеживания позиции
|
|
62
|
+
|
|
63
|
+
### Расчет состояний позиции
|
|
64
|
+
|
|
65
|
+
- Отслеживание через событие `scroll` на viewport элементе
|
|
66
|
+
- Дебаунсинг через `useDebouncedCallback` (16ms для 60fps)
|
|
67
|
+
- Расчет всех индикаторов позиции на основе `scrollTop`, `clientHeight`, `scrollHeight`
|
|
68
|
+
- Использование `nearThreshold` для определения `isNearTop` и `isNearBottom`
|
|
69
|
+
|
|
70
|
+
### Отслеживание изменений контента
|
|
71
|
+
|
|
72
|
+
- `useResizeObserver` от Mantine для отслеживания изменений размера
|
|
73
|
+
- `MutationObserver` для детекции изменений DOM
|
|
74
|
+
- `useMergedRef` для объединения refs (scrollAreaRef + resizeRef)
|
|
75
|
+
- Перерасчет состояний при изменении размеров
|
|
76
|
+
|
|
77
|
+
## Автоскролл логика
|
|
78
|
+
|
|
79
|
+
### Прокрутка при инициализации (scrollToBottomOnInit)
|
|
80
|
+
|
|
81
|
+
**Условия активации:**
|
|
82
|
+
|
|
83
|
+
- `scrollToBottomOnInit === true` (независимо от autoScroll)
|
|
84
|
+
- `hasScrollableContent === true`
|
|
85
|
+
|
|
86
|
+
**Поведение:**
|
|
87
|
+
|
|
88
|
+
- Выполняется при первом рендере с прокручиваемым контентом
|
|
89
|
+
- Использует анимацию (если `animated !== false`)
|
|
90
|
+
- Прокручивает к концу области
|
|
91
|
+
|
|
92
|
+
### Автоскролл при добавлении контента
|
|
93
|
+
|
|
94
|
+
**Условия активации:**
|
|
95
|
+
|
|
96
|
+
1. `autoScroll === true`
|
|
97
|
+
2. `isAtBottom === true` (точное позиционирование, НЕ near!)
|
|
98
|
+
3. `!isUserInteracting` (нет активного взаимодействия)
|
|
99
|
+
|
|
100
|
+
**Поведение:**
|
|
101
|
+
|
|
102
|
+
- Выполняется мгновенно (instant, без анимации)
|
|
103
|
+
- Сохраняет позицию в конце при добавлении нового контента
|
|
104
|
+
|
|
105
|
+
### Блокировка автоскролла
|
|
106
|
+
|
|
107
|
+
**Детекция пользовательского взаимодействия:**
|
|
108
|
+
|
|
109
|
+
- События: `wheel`, `touchstart`, `touchmove`, `mousedown` на области прокрутки
|
|
110
|
+
- Установка флага `isUserInteracting = true`
|
|
111
|
+
- Автоматический сброс через таймаут (например, 150ms после последнего события)
|
|
112
|
+
|
|
113
|
+
## Методы управления прокруткой
|
|
114
|
+
|
|
115
|
+
### scrollToTop(animated?: boolean)
|
|
116
|
+
|
|
117
|
+
- Прокрутка к началу области
|
|
118
|
+
- Использует параметр `animated` или дефолтное значение из пропсов
|
|
119
|
+
- Реализация через `element.scrollTo()` с `behavior: 'smooth'` или прямое управление `scrollTop`
|
|
120
|
+
|
|
121
|
+
### scrollToBottom(animated?: boolean)
|
|
122
|
+
|
|
123
|
+
- Прокрутка к концу области
|
|
124
|
+
- Использует параметр `animated` или дефолтное значение из пропсов
|
|
125
|
+
- Реализация через `element.scrollTo()` с `behavior: 'smooth'` или прямое управление `scrollTop`
|
|
126
|
+
|
|
127
|
+
## ScrollArea.ScrollButton
|
|
128
|
+
|
|
129
|
+
### Адаптивное поведение
|
|
130
|
+
|
|
131
|
+
**Логика отображения:**
|
|
132
|
+
|
|
133
|
+
- Показывается только при `hasScrollableContent === true`
|
|
134
|
+
- Скрывается при отсутствии прокручиваемого контента
|
|
135
|
+
|
|
136
|
+
**Логика направления:**
|
|
137
|
+
|
|
138
|
+
- `isAboveCenter === true` → стрелка вниз, действие `scrollToBottom()`
|
|
139
|
+
- `isAboveCenter === false` → стрелка вверх, действие `scrollToTop()`
|
|
140
|
+
|
|
141
|
+
### Пропсы ScrollButton
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
interface ScrollButtonProps {
|
|
145
|
+
className?: string;
|
|
146
|
+
upIcon?: ReactNode; // Иконка для прокрутки вверх
|
|
147
|
+
downIcon?: ReactNode; // Иконка для прокрутки вниз
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Производительность и оптимизация
|
|
152
|
+
|
|
153
|
+
### Дебаунсинг событий
|
|
154
|
+
|
|
155
|
+
- `useDebouncedCallback` от Mantine для обработчика `scroll`
|
|
156
|
+
- Задержка: 16ms (60fps)
|
|
157
|
+
|
|
158
|
+
### Объединение refs
|
|
159
|
+
|
|
160
|
+
- `useMergedRef` от Mantine для объединения scrollAreaRef и resizeRef
|
|
161
|
+
- Один callback ref для подключения к Mantine ScrollArea
|
|
162
|
+
|
|
163
|
+
### Управление подписками
|
|
164
|
+
|
|
165
|
+
- Правильная очистка обработчиков событий в useEffect cleanup
|
|
166
|
+
- Корректное управление ResizeObserver и MutationObserver через Mantine хуки
|
|
167
|
+
|
|
168
|
+
## Интеграция с Mantine
|
|
169
|
+
|
|
170
|
+
### Стилизация
|
|
171
|
+
|
|
172
|
+
- Полная совместимость с системой стилизации Mantine
|
|
173
|
+
- Передача всех Mantine ScrollArea пропсов через корневой ScrollArea компонент
|
|
174
|
+
- Поддержка кастомизации через className и стили
|
|
175
|
+
- Прямой доступ к viewport через `viewportRef` из `useScrollArea()`
|
|
176
|
+
|
|
177
|
+
### Пропсы Content (приватный компонент)
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
interface ScrollAreaContentProps {
|
|
181
|
+
children: ReactNode;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Примечание:** ScrollArea.Content не экспортируется и используется только внутри компонента.
|
|
186
|
+
|
|
187
|
+
## Требования к реализации
|
|
188
|
+
|
|
189
|
+
### Обязательные зависимости
|
|
190
|
+
|
|
191
|
+
- React 18+
|
|
192
|
+
- Mantine Scrollarea компонент
|
|
193
|
+
- React Context для состояния
|
|
194
|
+
|
|
195
|
+
### Хуки
|
|
196
|
+
|
|
197
|
+
- `useScrollArea` - публичный хук для разработчиков (фильтрует внутренние детали)
|
|
198
|
+
- `useScrollAreaState` - внутренний хук с полным состоянием
|
|
199
|
+
- Разделение ответственности: публичный API vs внутренняя логика
|
|
200
|
+
|
|
201
|
+
### Типизация
|
|
202
|
+
|
|
203
|
+
- Полная TypeScript типизация всех интерфейсов
|
|
204
|
+
- Экспорт типов для использования потребителями
|