@rolder/kit 3.0.0-alpha.7 → 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 +1 -4
- 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
package/{dist → src}/styles.css
RENAMED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
.rolder-prompt-input-root {
|
|
2
|
-
background-color: light-dark(
|
|
2
|
+
background-color: light-dark(
|
|
3
|
+
var(--mantine-color-white),
|
|
4
|
+
var(--mantine-color-dark-6)
|
|
5
|
+
);
|
|
3
6
|
|
|
4
7
|
&:focus-within {
|
|
8
|
+
outline: none;
|
|
5
9
|
border-color: var(--mantine-primary-color-filled);
|
|
6
|
-
|
|
10
|
+
}
|
|
11
|
+
&:focus-within {
|
|
7
12
|
outline: none;
|
|
13
|
+
border-color: var(--mantine-primary-color-filled);
|
|
8
14
|
}
|
|
9
15
|
}
|
|
10
16
|
|
|
11
17
|
.rolder-prompt-input-textarea {
|
|
12
18
|
border: 0;
|
|
13
|
-
border-radius: 8px;
|
|
14
19
|
overflow: hidden;
|
|
20
|
+
border-radius: 8px;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
.rolder-prompt-input-file-action-action {
|
|
@@ -23,7 +29,11 @@
|
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
.rolder-editor-root {
|
|
26
|
-
--editor-border-color: light-dark(
|
|
32
|
+
--editor-border-color: light-dark(
|
|
33
|
+
var(--mantine-color-gray-3),
|
|
34
|
+
var(--mantine-color-dark-4)
|
|
35
|
+
);
|
|
36
|
+
|
|
27
37
|
border: rem(1px) solid var(--editor-border-color);
|
|
28
38
|
border-radius: var(--mantine-radius-md);
|
|
29
39
|
}
|
|
@@ -39,14 +49,15 @@
|
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
.rolder-hover-paper-root {
|
|
42
|
-
transition: background-color .2s ease-in-out;
|
|
43
|
-
|
|
52
|
+
transition: background-color 0.2s ease-in-out;
|
|
44
53
|
&:hover {
|
|
45
|
-
background-color: light-dark(
|
|
54
|
+
background-color: light-dark(
|
|
55
|
+
var(--mantine-color-gray-0),
|
|
56
|
+
var(--mantine-color-dark-6)
|
|
57
|
+
);
|
|
58
|
+
transition: background-color 0.2s ease-in-out;
|
|
46
59
|
cursor: pointer;
|
|
47
|
-
transition: background-color .2s ease-in-out;
|
|
48
60
|
}
|
|
49
|
-
|
|
50
61
|
&[data-disabled] {
|
|
51
62
|
background-color: unset;
|
|
52
63
|
cursor: default;
|
|
@@ -54,8 +65,8 @@
|
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
.rolder-router-link-root {
|
|
57
|
-
color: inherit;
|
|
58
68
|
text-decoration: none;
|
|
69
|
+
color: inherit;
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
.rolder-scroll-area-viewport {
|
|
@@ -71,4 +82,3 @@
|
|
|
71
82
|
border-top-right-radius: var(--radius);
|
|
72
83
|
border-bottom-right-radius: var(--radius);
|
|
73
84
|
}
|
|
74
|
-
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createServerOnlyFn } from '@tanstack/react-start';
|
|
2
|
+
import { getCookie } from '@tanstack/react-start/server';
|
|
3
|
+
import { type CodecOptions, DateTime, Surreal } from 'surrealdb';
|
|
4
|
+
|
|
5
|
+
let db: Surreal | null = null;
|
|
6
|
+
|
|
7
|
+
export const getDB = createServerOnlyFn(
|
|
8
|
+
async (
|
|
9
|
+
params:
|
|
10
|
+
| {
|
|
11
|
+
url?: string;
|
|
12
|
+
namespace?: string;
|
|
13
|
+
database?: string;
|
|
14
|
+
username?: string;
|
|
15
|
+
password?: string;
|
|
16
|
+
codecOptions?: CodecOptions;
|
|
17
|
+
}
|
|
18
|
+
| undefined = {},
|
|
19
|
+
): Promise<Surreal> => {
|
|
20
|
+
if (db?.isConnected) return db;
|
|
21
|
+
|
|
22
|
+
// Нужно чтобы был в корне для получения контекста запроса серверной функции
|
|
23
|
+
const locale = getCookie('locale') || 'ru-RU';
|
|
24
|
+
const timeZone = getCookie('tz') || 'UTC';
|
|
25
|
+
|
|
26
|
+
const instance = new Surreal({
|
|
27
|
+
codecOptions: params.codecOptions || {
|
|
28
|
+
valueDecodeVisitor(value) {
|
|
29
|
+
if (value instanceof DateTime) {
|
|
30
|
+
return new Date(value.toDate()).toLocaleDateString(locale, {
|
|
31
|
+
hour: 'numeric',
|
|
32
|
+
minute: 'numeric',
|
|
33
|
+
timeZone,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const url = params.url || process.env.SURREALDB_URL;
|
|
43
|
+
if (!url) throw new Error('Missing required SurrealDB URL');
|
|
44
|
+
|
|
45
|
+
const namespace = params.namespace || process.env.SURREALDB_NAMESPACE;
|
|
46
|
+
if (!namespace) throw new Error('Missing required SurrealDB namespace');
|
|
47
|
+
|
|
48
|
+
const database = params.database || process.env.SURREALDB_DATABASE;
|
|
49
|
+
if (!database) throw new Error('Missing required SurrealDB database');
|
|
50
|
+
|
|
51
|
+
const username = params.username || process.env.SURREALDB_USERNAME;
|
|
52
|
+
const password = params.password || process.env.SURREALDB_PASSWORD;
|
|
53
|
+
|
|
54
|
+
if (username && password)
|
|
55
|
+
await instance.connect(url, { authentication: { username, password } });
|
|
56
|
+
else await instance.connect(url);
|
|
57
|
+
|
|
58
|
+
await instance.use({
|
|
59
|
+
namespace,
|
|
60
|
+
database,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
db = instance;
|
|
64
|
+
|
|
65
|
+
return instance;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('Failed to connect to SurrealDB:', error);
|
|
68
|
+
db = null;
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createServerFn } from '@tanstack/react-start';
|
|
2
|
+
import type { Uuid } from 'surrealdb';
|
|
3
|
+
import { getDB } from './connection';
|
|
4
|
+
import { deserialize } from './deserialize';
|
|
5
|
+
|
|
6
|
+
export const surrealDeleteFn = createServerFn({ method: 'POST' })
|
|
7
|
+
.inputValidator((data: string) => data)
|
|
8
|
+
.handler(async ({ data }) => {
|
|
9
|
+
const db = await getDB();
|
|
10
|
+
|
|
11
|
+
const id = deserialize(data);
|
|
12
|
+
await db.delete(id);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const surrealUnsubscribeFn = createServerFn({
|
|
16
|
+
method: 'POST',
|
|
17
|
+
})
|
|
18
|
+
.inputValidator((data: string) => data)
|
|
19
|
+
.handler(async ({ data }) => {
|
|
20
|
+
const db = await getDB();
|
|
21
|
+
|
|
22
|
+
const live = await db.liveOf(data as unknown as Uuid);
|
|
23
|
+
|
|
24
|
+
await live.kill();
|
|
25
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { RecordId } from 'surrealdb';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type that converts specified string paths to RecordId, others stay as their original types
|
|
5
|
+
*/
|
|
6
|
+
type DeserializeResult<T, IdPaths extends string = never> = T extends string
|
|
7
|
+
? RecordId<string> // Plain strings always become RecordId when no paths or when it's root level
|
|
8
|
+
: T extends (infer U)[]
|
|
9
|
+
? U extends object
|
|
10
|
+
? {
|
|
11
|
+
[K in keyof U]: K extends IdPaths ? RecordId<string> : U[K];
|
|
12
|
+
}[]
|
|
13
|
+
: DeserializeResult<U, IdPaths>[]
|
|
14
|
+
: T extends object
|
|
15
|
+
? T extends Date
|
|
16
|
+
? T
|
|
17
|
+
: {
|
|
18
|
+
[K in keyof T]: K extends IdPaths ? RecordId<string> : T[K]; // Keep original types for non-ID fields
|
|
19
|
+
}
|
|
20
|
+
: T;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Extract table name from "table:id" format string
|
|
24
|
+
*/
|
|
25
|
+
const extractTableName = (str: string): string | null => {
|
|
26
|
+
const match = str.match(/^([^:]+):/);
|
|
27
|
+
return match ? match[1] : null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if string is in "table:id" format
|
|
32
|
+
*/
|
|
33
|
+
const isRecordIdFormat = (str: string): boolean => {
|
|
34
|
+
return /^[^:]+:.+$/.test(str);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Deserializes DTO back to SurrealDB Record recursively based on specified ID paths
|
|
39
|
+
*/
|
|
40
|
+
// Overload for arrays with ID paths
|
|
41
|
+
export function deserialize<T, K extends keyof T & string>(
|
|
42
|
+
dto: T[],
|
|
43
|
+
idPaths: K[],
|
|
44
|
+
): DeserializeResult<T, K>[];
|
|
45
|
+
// Overload for single objects with ID paths
|
|
46
|
+
export function deserialize<T, K extends keyof T & string>(
|
|
47
|
+
dto: T,
|
|
48
|
+
idPaths: K[],
|
|
49
|
+
): DeserializeResult<T, K>;
|
|
50
|
+
// Overload for arrays without ID paths (treats all strings as potential IDs)
|
|
51
|
+
export function deserialize<T>(dto: T[]): DeserializeResult<T>[];
|
|
52
|
+
// Overload for single objects without ID paths (treats all strings as potential IDs)
|
|
53
|
+
export function deserialize<T>(dto: T): DeserializeResult<T>;
|
|
54
|
+
|
|
55
|
+
// Implementation
|
|
56
|
+
export function deserialize<T, K extends keyof T & string = never>(
|
|
57
|
+
dto: T | T[],
|
|
58
|
+
idPaths?: K[],
|
|
59
|
+
): DeserializeResult<T, K> | DeserializeResult<T, K>[] {
|
|
60
|
+
if (Array.isArray(dto)) {
|
|
61
|
+
return dto.map((item) =>
|
|
62
|
+
convertStringsToRecordIds(item, idPaths || []),
|
|
63
|
+
) as DeserializeResult<T, K>[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return convertStringsToRecordIds(dto, idPaths || []) as DeserializeResult<
|
|
67
|
+
T,
|
|
68
|
+
K
|
|
69
|
+
>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Recursively converts string IDs back to RecordId instances based on specified paths
|
|
74
|
+
*/
|
|
75
|
+
const convertStringsToRecordIds = (
|
|
76
|
+
obj: unknown,
|
|
77
|
+
idPaths: string[],
|
|
78
|
+
currentPath = '',
|
|
79
|
+
): unknown => {
|
|
80
|
+
if (obj === null || obj === undefined) {
|
|
81
|
+
return obj;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// If it's an array, check if the current path matches any idPath
|
|
85
|
+
if (Array.isArray(obj)) {
|
|
86
|
+
// If this array's path is in idPaths, convert all string elements to RecordIds
|
|
87
|
+
if (idPaths.includes(currentPath)) {
|
|
88
|
+
return obj.map((item) => {
|
|
89
|
+
if (typeof item === 'string' && isRecordIdFormat(item)) {
|
|
90
|
+
const tableName = extractTableName(item);
|
|
91
|
+
if (tableName) {
|
|
92
|
+
return new RecordId(tableName, item.split(':')[1]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return item;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// Otherwise, recursively process each element with indexed path
|
|
99
|
+
return obj.map((item, index) =>
|
|
100
|
+
convertStringsToRecordIds(item, idPaths, `${currentPath}[${index}]`),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// If it's an object, recursively process each property
|
|
105
|
+
if (typeof obj === 'object' && !(obj instanceof Date)) {
|
|
106
|
+
const result: Record<string, unknown> = {};
|
|
107
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
108
|
+
const fieldPath = currentPath ? `${currentPath}.${key}` : key;
|
|
109
|
+
|
|
110
|
+
if (typeof value === 'string') {
|
|
111
|
+
// Only convert if this field path is explicitly in idPaths
|
|
112
|
+
const shouldConvert =
|
|
113
|
+
idPaths.includes(fieldPath) && isRecordIdFormat(value);
|
|
114
|
+
|
|
115
|
+
if (shouldConvert) {
|
|
116
|
+
const tableName = extractTableName(value);
|
|
117
|
+
if (tableName) {
|
|
118
|
+
result[key] = new RecordId(tableName, value.split(':')[1]);
|
|
119
|
+
} else {
|
|
120
|
+
result[key] = value;
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
result[key] = value;
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
result[key] = convertStringsToRecordIds(value, idPaths, fieldPath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// For strings at root level (not in objects) - only convert if no idPaths specified
|
|
133
|
+
if (typeof obj === 'string') {
|
|
134
|
+
if (idPaths.length === 0 && isRecordIdFormat(obj)) {
|
|
135
|
+
const tableName = extractTableName(obj);
|
|
136
|
+
if (tableName) {
|
|
137
|
+
return new RecordId(tableName, obj.split(':')[1]);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// For primitive types, return as is
|
|
143
|
+
return obj;
|
|
144
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
|
|
2
|
+
import { createServerOnlyFn } from '@tanstack/react-start';
|
|
3
|
+
|
|
4
|
+
type EncryptionInstance = {
|
|
5
|
+
encrypt(text: string): string;
|
|
6
|
+
decrypt(encryptedText: string): string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const encryptionFn = createServerOnlyFn(
|
|
10
|
+
(secretHash: string): EncryptionInstance => {
|
|
11
|
+
class Encryption {
|
|
12
|
+
private key: Buffer;
|
|
13
|
+
|
|
14
|
+
constructor(secretHash: string) {
|
|
15
|
+
// Создаем 32-байтный ключ из 64-символьного hex хеша
|
|
16
|
+
this.key = Buffer.from(secretHash, 'hex');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Функция для шифрования
|
|
20
|
+
encrypt(text: string): string {
|
|
21
|
+
const algorithm = 'aes-256-cbc';
|
|
22
|
+
const iv = randomBytes(16);
|
|
23
|
+
|
|
24
|
+
const cipher = createCipheriv(algorithm, this.key, iv);
|
|
25
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
26
|
+
encrypted += cipher.final('hex');
|
|
27
|
+
|
|
28
|
+
// Возвращаем IV + зашифрованные данные
|
|
29
|
+
return `${iv.toString('hex')}:${encrypted}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Функция для дешифрования
|
|
33
|
+
decrypt(encryptedText: string): string {
|
|
34
|
+
const algorithm = 'aes-256-cbc';
|
|
35
|
+
|
|
36
|
+
// Разделяем IV и зашифрованные данные
|
|
37
|
+
const parts = encryptedText.split(':');
|
|
38
|
+
const iv = Buffer.from(parts[0], 'hex');
|
|
39
|
+
const encrypted = parts[1];
|
|
40
|
+
|
|
41
|
+
const decipher = createDecipheriv(algorithm, this.key, iv);
|
|
42
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
43
|
+
decrypted += decipher.final('utf8');
|
|
44
|
+
|
|
45
|
+
return decrypted;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return new Encryption(secretHash);
|
|
50
|
+
},
|
|
51
|
+
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SVGProps } from 'react';
|
|
2
|
+
|
|
3
|
+
interface Props extends SVGProps<SVGSVGElement> {
|
|
4
|
+
expanded?: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const AnimatedChevron = ({ expanded, style, ...props }: Props) => {
|
|
8
|
+
return (
|
|
9
|
+
<svg
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
width="24"
|
|
12
|
+
height="24"
|
|
13
|
+
viewBox="0 0 24 24"
|
|
14
|
+
fill="none"
|
|
15
|
+
stroke="currentColor"
|
|
16
|
+
strokeWidth="1.5"
|
|
17
|
+
strokeLinecap="round"
|
|
18
|
+
strokeLinejoin="round"
|
|
19
|
+
role="img"
|
|
20
|
+
aria-label="Chevron"
|
|
21
|
+
style={{
|
|
22
|
+
transition: 'transform 0.2s ease-in-out',
|
|
23
|
+
transform: expanded ? 'rotate(-180deg)' : 'rotate(0deg)',
|
|
24
|
+
...style,
|
|
25
|
+
}}
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
29
|
+
<path d="M6 9l6 6l6 -6" />
|
|
30
|
+
</svg>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { indentSelection } from '@codemirror/commands';
|
|
2
|
+
import { json, jsonParseLinter } from '@codemirror/lang-json';
|
|
3
|
+
import { linter } from '@codemirror/lint';
|
|
4
|
+
import { type EditorView, keymap } from '@codemirror/view';
|
|
5
|
+
import { vscodeDark, vscodeLight } from '@uiw/codemirror-theme-vscode';
|
|
6
|
+
import CodeMirror, { type ReactCodeMirrorProps } from '@uiw/react-codemirror';
|
|
7
|
+
import { getCookie } from '../functions/getCookie';
|
|
8
|
+
|
|
9
|
+
const formatJson = (view: EditorView) => {
|
|
10
|
+
try {
|
|
11
|
+
const text = view.state.doc.toString();
|
|
12
|
+
const parsed = JSON.parse(text);
|
|
13
|
+
const formatted = JSON.stringify(parsed, null, 2);
|
|
14
|
+
|
|
15
|
+
view.dispatch({
|
|
16
|
+
changes: {
|
|
17
|
+
from: 0,
|
|
18
|
+
to: view.state.doc.length,
|
|
19
|
+
insert: formatted,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return true;
|
|
24
|
+
} catch (_) {
|
|
25
|
+
// Если JSON невалидный, пробуем просто отступы
|
|
26
|
+
return indentSelection(view);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const extensions = [
|
|
31
|
+
json(),
|
|
32
|
+
linter(jsonParseLinter()),
|
|
33
|
+
keymap.of([
|
|
34
|
+
{
|
|
35
|
+
key: 'Mod-Shift-f',
|
|
36
|
+
run: formatJson,
|
|
37
|
+
},
|
|
38
|
+
]),
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
export const JsonInput = (props: ReactCodeMirrorProps) => {
|
|
42
|
+
const colorScheme = getCookie('colorScheme');
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<CodeMirror
|
|
46
|
+
height="280px"
|
|
47
|
+
theme={colorScheme === 'light' ? vscodeLight : vscodeDark}
|
|
48
|
+
extensions={extensions}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActionIcon,
|
|
3
|
+
type ActionIconProps,
|
|
4
|
+
Anchor,
|
|
5
|
+
type AnchorProps,
|
|
6
|
+
Button,
|
|
7
|
+
type ButtonProps,
|
|
8
|
+
UnstyledButton,
|
|
9
|
+
type UnstyledButtonProps,
|
|
10
|
+
} from '@mantine/core';
|
|
11
|
+
import { createLink, type LinkComponent } from '@tanstack/react-router';
|
|
12
|
+
import { forwardRef } from 'react';
|
|
13
|
+
|
|
14
|
+
interface MantineAnchorProps extends Omit<AnchorProps, 'href'> {}
|
|
15
|
+
interface MantineButtonProps extends Omit<ButtonProps, 'href'> {}
|
|
16
|
+
interface MantineActionIconProps extends Omit<ActionIconProps, 'href'> {}
|
|
17
|
+
interface MantineUnstyledButtonProps
|
|
18
|
+
extends Omit<UnstyledButtonProps, 'href'> {}
|
|
19
|
+
|
|
20
|
+
// Базовый компонент Anchor
|
|
21
|
+
const MantineLinkComponent = forwardRef<HTMLAnchorElement, MantineAnchorProps>(
|
|
22
|
+
({ classNames, ...props }, ref) => {
|
|
23
|
+
return (
|
|
24
|
+
<Anchor
|
|
25
|
+
ref={ref}
|
|
26
|
+
classNames={{ root: 'rolder-router-link-root', ...classNames }}
|
|
27
|
+
c="inherit"
|
|
28
|
+
underline="never"
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Компонент Button - передаем все пропсы кроме href
|
|
36
|
+
const MantineButtonComponent = forwardRef<
|
|
37
|
+
HTMLButtonElement,
|
|
38
|
+
MantineButtonProps
|
|
39
|
+
>((props, ref) => {
|
|
40
|
+
return <Button ref={ref} {...props} />;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Компонент ActionIcon - передаем все пропсы кроме href
|
|
44
|
+
const MantineActionIconComponent = forwardRef<
|
|
45
|
+
HTMLButtonElement,
|
|
46
|
+
MantineActionIconProps
|
|
47
|
+
>((props, ref) => {
|
|
48
|
+
return <ActionIcon ref={ref} {...props} />;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Компонент UnstyledButton - передаем все пропсы кроме href
|
|
52
|
+
const MantineUnstyledButtonComponent = forwardRef<
|
|
53
|
+
HTMLButtonElement,
|
|
54
|
+
MantineUnstyledButtonProps
|
|
55
|
+
>((props, ref) => {
|
|
56
|
+
return <UnstyledButton ref={ref} {...props} />;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Создаем RouterLink для каждого компонента
|
|
60
|
+
const RouterLinkBase: LinkComponent<typeof MantineLinkComponent> =
|
|
61
|
+
createLink(MantineLinkComponent);
|
|
62
|
+
|
|
63
|
+
const RouterLinkButton: LinkComponent<typeof MantineButtonComponent> =
|
|
64
|
+
createLink(MantineButtonComponent);
|
|
65
|
+
|
|
66
|
+
const RouterLinkActionIcon: LinkComponent<typeof MantineActionIconComponent> =
|
|
67
|
+
createLink(MantineActionIconComponent);
|
|
68
|
+
|
|
69
|
+
const RouterLinkUnstyledButton: LinkComponent<
|
|
70
|
+
typeof MantineUnstyledButtonComponent
|
|
71
|
+
> = createLink(MantineUnstyledButtonComponent);
|
|
72
|
+
|
|
73
|
+
// Экспортируем с namespace pattern
|
|
74
|
+
export const RouterLink = Object.assign(RouterLinkBase, {
|
|
75
|
+
Button: RouterLinkButton,
|
|
76
|
+
ActionIcon: RouterLinkActionIcon,
|
|
77
|
+
UnstyledButton: RouterLinkUnstyledButton,
|
|
78
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RichTextEditor } from '@mantine/tiptap';
|
|
2
|
+
import { ScrollArea } from '../scrollArea';
|
|
3
|
+
|
|
4
|
+
export const Content = ({ height }: { height: string }) => {
|
|
5
|
+
return (
|
|
6
|
+
<ScrollArea h={height} autoScroll radius="md">
|
|
7
|
+
<RichTextEditor.Content />
|
|
8
|
+
<ScrollArea.ScrollButton />
|
|
9
|
+
</ScrollArea>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getTaskListExtension, Link } from '@mantine/tiptap';
|
|
2
|
+
import Highlight from '@tiptap/extension-highlight';
|
|
3
|
+
import Placeholder from '@tiptap/extension-placeholder';
|
|
4
|
+
import { TableKit } from '@tiptap/extension-table';
|
|
5
|
+
import TaskItem from '@tiptap/extension-task-item';
|
|
6
|
+
import TipTapTaskList from '@tiptap/extension-task-list';
|
|
7
|
+
import TextAlign from '@tiptap/extension-text-align';
|
|
8
|
+
import type { Editor } from '@tiptap/react';
|
|
9
|
+
import { useEditor as useTipTapEditor } from '@tiptap/react';
|
|
10
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
11
|
+
import { createContext, useContext, useEffect, useState } from 'react';
|
|
12
|
+
import type { EditorProps } from './types';
|
|
13
|
+
|
|
14
|
+
interface EditorContext {
|
|
15
|
+
editor: Editor | null;
|
|
16
|
+
editable?: boolean;
|
|
17
|
+
setEditable: (value: boolean) => void;
|
|
18
|
+
disabledToolbar: boolean;
|
|
19
|
+
setDisabledToolbar: (value: boolean) => void;
|
|
20
|
+
focused: boolean;
|
|
21
|
+
setFocused: (value: boolean) => void;
|
|
22
|
+
editedByUser: boolean;
|
|
23
|
+
setEditedByUser: (value: boolean) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const EditorContext = createContext<EditorContext | null>(null);
|
|
27
|
+
|
|
28
|
+
export const Provider = ({
|
|
29
|
+
children,
|
|
30
|
+
initialContent,
|
|
31
|
+
initialEditable = true,
|
|
32
|
+
initialDisabledToolbar,
|
|
33
|
+
onChange,
|
|
34
|
+
}: EditorProps) => {
|
|
35
|
+
const editor = useTipTapEditor({
|
|
36
|
+
shouldRerenderOnTransaction: false,
|
|
37
|
+
immediatelyRender: false,
|
|
38
|
+
extensions: [
|
|
39
|
+
StarterKit.configure({ link: false }),
|
|
40
|
+
Placeholder.configure({ placeholder: 'Документ пуст' }),
|
|
41
|
+
Link,
|
|
42
|
+
Highlight,
|
|
43
|
+
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
|
44
|
+
getTaskListExtension(TipTapTaskList),
|
|
45
|
+
TaskItem.configure({ nested: true }),
|
|
46
|
+
TableKit,
|
|
47
|
+
],
|
|
48
|
+
content: initialContent,
|
|
49
|
+
onUpdate: ({ editor }) => {
|
|
50
|
+
onChange?.(editor.getHTML());
|
|
51
|
+
},
|
|
52
|
+
editable: initialEditable,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const [editable, setEditable] = useState(initialEditable);
|
|
56
|
+
const [disabledToolbar, setDisabledToolbar] = useState(
|
|
57
|
+
!!initialDisabledToolbar,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const [editedByUser, setEditedByUser] = useState(true);
|
|
61
|
+
const [focused, setFocused] = useState(false);
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
editor?.on('focus', () => setFocused(true));
|
|
65
|
+
editor?.on('blur', () => setFocused(false));
|
|
66
|
+
editor?.on('update', ({ transaction }) => {
|
|
67
|
+
if (transaction.docChanged && focused) setEditedByUser(true);
|
|
68
|
+
if (transaction.docChanged && !focused) setEditedByUser(false);
|
|
69
|
+
});
|
|
70
|
+
}, [editor, focused]);
|
|
71
|
+
|
|
72
|
+
const value: EditorContext = {
|
|
73
|
+
editor,
|
|
74
|
+
editable,
|
|
75
|
+
setEditable,
|
|
76
|
+
disabledToolbar,
|
|
77
|
+
setDisabledToolbar,
|
|
78
|
+
editedByUser,
|
|
79
|
+
setEditedByUser,
|
|
80
|
+
focused,
|
|
81
|
+
setFocused,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<EditorContext.Provider value={value}>{children}</EditorContext.Provider>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const useEditor = () => {
|
|
90
|
+
const context = useContext(EditorContext);
|
|
91
|
+
if (!context) {
|
|
92
|
+
throw new Error('useEditor must be used within EditorProvider');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return context;
|
|
96
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { RichTextEditor, type RichTextEditorProps } from '@mantine/tiptap';
|
|
2
|
+
import { useEditor } from './Provider';
|
|
3
|
+
|
|
4
|
+
export const Root = ({
|
|
5
|
+
children,
|
|
6
|
+
classNames,
|
|
7
|
+
...props
|
|
8
|
+
}: Omit<RichTextEditorProps, 'editor'>) => {
|
|
9
|
+
const { editor } = useEditor();
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<RichTextEditor
|
|
13
|
+
editor={editor}
|
|
14
|
+
classNames={{
|
|
15
|
+
root: 'rolder-editor-root',
|
|
16
|
+
content: 'rolder-editor-content',
|
|
17
|
+
toolbar: 'rolder-editor-toolbar',
|
|
18
|
+
...classNames,
|
|
19
|
+
}}
|
|
20
|
+
{...props}
|
|
21
|
+
>
|
|
22
|
+
{children}
|
|
23
|
+
</RichTextEditor>
|
|
24
|
+
);
|
|
25
|
+
};
|