@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.
Files changed (263) hide show
  1. package/package.json +3 -3
  2. package/rslib.config.ts +21 -0
  3. package/src/ai/ui/conversation/ConversationContext.ts +21 -0
  4. package/src/ai/ui/conversation/ConversationProvider.tsx +21 -0
  5. package/src/ai/ui/conversation/Empty.tsx +15 -0
  6. package/src/ai/ui/conversation/File.tsx +40 -0
  7. package/src/ai/ui/conversation/FileIcon.tsx +143 -0
  8. package/src/ai/ui/conversation/Loader.tsx +8 -0
  9. package/src/ai/ui/conversation/Message.tsx +34 -0
  10. package/src/ai/ui/conversation/Root.tsx +24 -0
  11. package/src/ai/ui/conversation/index.ts +16 -0
  12. package/{dist/ai/ui/conversation/types.d.ts → src/ai/ui/conversation/types.ts} +5 -4
  13. package/src/ai/ui/conversation/useChatMessage.ts +13 -0
  14. package/src/ai/ui/promptInput/File.tsx +98 -0
  15. package/src/ai/ui/promptInput/FileIcon.tsx +149 -0
  16. package/src/ai/ui/promptInput/Footer.tsx +5 -0
  17. package/src/ai/ui/promptInput/PromptInputContext.ts +24 -0
  18. package/src/ai/ui/promptInput/PromptInputProvider.tsx +54 -0
  19. package/src/ai/ui/promptInput/Root.tsx +29 -0
  20. package/src/ai/ui/promptInput/Submit.tsx +39 -0
  21. package/src/ai/ui/promptInput/Textarea.tsx +39 -0
  22. package/src/ai/ui/promptInput/index.ts +15 -0
  23. package/src/ai/ui/promptInput/types.ts +9 -0
  24. package/src/ai/utils/convertFileUIPartBlobToDataURL.ts +29 -0
  25. package/src/ai/utils/parseAiMessagePart.ts +19 -0
  26. package/src/app/AppDefaults.tsx +21 -0
  27. package/src/app/DefaultApp.tsx +50 -0
  28. package/src/app/cookieColorSchemeManager.ts +70 -0
  29. package/src/app/defaultRequestMiddlewares.ts +22 -0
  30. package/src/app/defaultTheme.ts +22 -0
  31. package/src/functions/getCookie.ts +36 -0
  32. package/src/functions/setCookie.ts +29 -0
  33. package/src/functions/setCookies.ts +24 -0
  34. package/src/hooks/useMutation.ts +14 -0
  35. package/src/hooks/useMutationWithInvalidate.ts +23 -0
  36. package/{dist/index.d.ts → src/index.ts} +45 -5
  37. package/{dist → src}/styles.css +21 -11
  38. package/src/surreal/connection.ts +72 -0
  39. package/src/surreal/deafaultCrud.ts +25 -0
  40. package/src/surreal/deserialize.ts +144 -0
  41. package/src/surreal/encryption.ts +51 -0
  42. package/src/ui/AnimatedChevron.tsx +32 -0
  43. package/src/ui/JsonInput.tsx +52 -0
  44. package/src/ui/RouterLink.tsx +78 -0
  45. package/src/ui/editor/Content.tsx +11 -0
  46. package/src/ui/editor/Provider.tsx +96 -0
  47. package/src/ui/editor/Root.tsx +25 -0
  48. package/src/ui/editor/Toolbar.tsx +92 -0
  49. package/src/ui/editor/index.ts +13 -0
  50. package/src/ui/editor/types.ts +7 -0
  51. package/src/ui/error/DefaultError.tsx +60 -0
  52. package/src/ui/error/DefaultNotFound.tsx +19 -0
  53. package/src/ui/error/Forbidden.tsx +18 -0
  54. package/src/ui/error/defaultErrorNotification.ts +9 -0
  55. package/src/ui/form/blurOnError.ts +21 -0
  56. package/src/ui/form/buttons/CancelButton.tsx +42 -0
  57. package/src/ui/form/buttons/SubmitButton.tsx +43 -0
  58. package/src/ui/form/buttons/SubscribeActionIcon.tsx +18 -0
  59. package/src/ui/form/buttons/SubscribeButton.tsx +17 -0
  60. package/src/ui/form/context.ts +45 -0
  61. package/src/ui/form/fields/JsonField.tsx +16 -0
  62. package/src/ui/form/fields/MultiSelectField.tsx +17 -0
  63. package/src/ui/form/fields/NumberField.tsx +17 -0
  64. package/src/ui/form/fields/PassowrdField.tsx +20 -0
  65. package/src/ui/form/fields/SelectField.tsx +17 -0
  66. package/src/ui/form/fields/SwitchField.tsx +17 -0
  67. package/src/ui/form/fields/TextField.tsx +17 -0
  68. package/src/ui/form/fields/TextPassowrdField.tsx +51 -0
  69. package/src/ui/form/fields/TextareaField.tsx +17 -0
  70. package/src/ui/form/fieldsSchema.ts +24 -0
  71. package/src/ui/hoverPaper/HoverPaper.tsx +17 -0
  72. package/src/ui/hoverPaper/usePaperHover.ts +9 -0
  73. package/src/ui/saveInput/JsonInput.tsx +40 -0
  74. package/src/ui/saveInput/NumberInput.tsx +40 -0
  75. package/src/ui/saveInput/SaveInput.tsx +15 -0
  76. package/src/ui/saveInput/Select.tsx +41 -0
  77. package/src/ui/saveInput/Switch.tsx +46 -0
  78. package/src/ui/saveInput/TextInput.tsx +40 -0
  79. package/src/ui/saveInput/Textarea.tsx +40 -0
  80. package/src/ui/scrollArea/ARCH.md +204 -0
  81. package/src/ui/scrollArea/README.md +369 -0
  82. package/{dist/ui/scrollArea/ScrollArea.d.ts → src/ui/scrollArea/ScrollArea.tsx} +41 -10
  83. package/src/ui/scrollArea/ScrollAreaButton.tsx +56 -0
  84. package/src/ui/scrollArea/ScrollAreaContent.tsx +36 -0
  85. package/{dist/ui/scrollArea/context.d.ts → src/ui/scrollArea/context.tsx} +18 -3
  86. package/src/ui/scrollArea/index.ts +10 -0
  87. package/src/ui/scrollArea/types.ts +77 -0
  88. package/src/ui/scrollArea/useScrollArea.ts +227 -0
  89. package/tsconfig.json +14 -0
  90. package/dist/ai/ui/conversation/ConversationContext.d.ts +0 -7
  91. package/dist/ai/ui/conversation/ConversationContext.js +0 -8
  92. package/dist/ai/ui/conversation/ConversationProvider.d.ts +0 -2
  93. package/dist/ai/ui/conversation/ConversationProvider.js +0 -14
  94. package/dist/ai/ui/conversation/Empty.d.ts +0 -1
  95. package/dist/ai/ui/conversation/Empty.js +0 -21
  96. package/dist/ai/ui/conversation/File.d.ts +0 -4
  97. package/dist/ai/ui/conversation/File.js +0 -42
  98. package/dist/ai/ui/conversation/FileIcon.d.ts +0 -3
  99. package/dist/ai/ui/conversation/FileIcon.js +0 -225
  100. package/dist/ai/ui/conversation/Loader.d.ts +0 -2
  101. package/dist/ai/ui/conversation/Loader.js +0 -12
  102. package/dist/ai/ui/conversation/Message.d.ts +0 -4
  103. package/dist/ai/ui/conversation/Message.js +0 -25
  104. package/dist/ai/ui/conversation/Root.d.ts +0 -2
  105. package/dist/ai/ui/conversation/Root.js +0 -26
  106. package/dist/ai/ui/conversation/index.d.ts +0 -13
  107. package/dist/ai/ui/conversation/index.js +0 -14
  108. package/dist/ai/ui/conversation/types.js +0 -0
  109. package/dist/ai/ui/conversation/useChatMessage.d.ts +0 -2
  110. package/dist/ai/ui/conversation/useChatMessage.js +0 -12
  111. package/dist/ai/ui/promptInput/File.d.ts +0 -2
  112. package/dist/ai/ui/promptInput/File.js +0 -117
  113. package/dist/ai/ui/promptInput/FileIcon.d.ts +0 -3
  114. package/dist/ai/ui/promptInput/FileIcon.js +0 -225
  115. package/dist/ai/ui/promptInput/Footer.d.ts +0 -2
  116. package/dist/ai/ui/promptInput/Footer.js +0 -8
  117. package/dist/ai/ui/promptInput/PromptInputContext.d.ts +0 -12
  118. package/dist/ai/ui/promptInput/PromptInputContext.js +0 -8
  119. package/dist/ai/ui/promptInput/PromptInputProvider.d.ts +0 -2
  120. package/dist/ai/ui/promptInput/PromptInputProvider.js +0 -50
  121. package/dist/ai/ui/promptInput/Root.d.ts +0 -3
  122. package/dist/ai/ui/promptInput/Root.js +0 -17
  123. package/dist/ai/ui/promptInput/Submit.d.ts +0 -2
  124. package/dist/ai/ui/promptInput/Submit.js +0 -40
  125. package/dist/ai/ui/promptInput/Textarea.d.ts +0 -2
  126. package/dist/ai/ui/promptInput/Textarea.js +0 -33
  127. package/dist/ai/ui/promptInput/index.d.ts +0 -8
  128. package/dist/ai/ui/promptInput/index.js +0 -13
  129. package/dist/ai/ui/promptInput/types.d.ts +0 -11
  130. package/dist/ai/ui/promptInput/types.js +0 -0
  131. package/dist/ai/utils/convertFileUIPartBlobToDataURL.d.ts +0 -5
  132. package/dist/ai/utils/convertFileUIPartBlobToDataURL.js +0 -21
  133. package/dist/ai/utils/parseAiMessagePart.d.ts +0 -2
  134. package/dist/ai/utils/parseAiMessagePart.js +0 -12
  135. package/dist/app/AppDefaults.d.ts +0 -3
  136. package/dist/app/AppDefaults.js +0 -27
  137. package/dist/app/DefaultApp.d.ts +0 -6
  138. package/dist/app/DefaultApp.js +0 -43
  139. package/dist/app/cookieColorSchemeManager.d.ts +0 -6
  140. package/dist/app/cookieColorSchemeManager.js +0 -46
  141. package/dist/app/defaultRequestMiddlewares.d.ts +0 -4
  142. package/dist/app/defaultRequestMiddlewares.js +0 -24
  143. package/dist/app/defaultTheme.d.ts +0 -141
  144. package/dist/app/defaultTheme.js +0 -24
  145. package/dist/functions/getCookie.d.ts +0 -3
  146. package/dist/functions/getCookie.js +0 -8
  147. package/dist/functions/setCookie.d.ts +0 -10
  148. package/dist/functions/setCookie.js +0 -19
  149. package/dist/functions/setCookies.d.ts +0 -14
  150. package/dist/functions/setCookies.js +0 -13
  151. package/dist/hooks/useMutation.d.ts +0 -4
  152. package/dist/hooks/useMutation.js +0 -8
  153. package/dist/hooks/useMutationWithInvalidate.d.ts +0 -4
  154. package/dist/hooks/useMutationWithInvalidate.js +0 -16
  155. package/dist/index.js +0 -26
  156. package/dist/surreal/connection.d.ts +0 -9
  157. package/dist/surreal/connection.js +0 -49
  158. package/dist/surreal/deafaultCrud.d.ts +0 -2
  159. package/dist/surreal/deafaultCrud.js +0 -18
  160. package/dist/surreal/deserialize.d.ts +0 -17
  161. package/dist/surreal/deserialize.js +0 -46
  162. package/dist/surreal/encryption.d.ts +0 -6
  163. package/dist/surreal/encryption.js +0 -30
  164. package/dist/ui/AnimatedChevron.d.ts +0 -6
  165. package/dist/ui/AnimatedChevron.js +0 -31
  166. package/dist/ui/JsonInput.d.ts +0 -2
  167. package/dist/ui/JsonInput.js +0 -45
  168. package/dist/ui/RouterLink.d.ts +0 -16
  169. package/dist/ui/RouterLink.js +0 -36
  170. package/dist/ui/editor/Content.d.ts +0 -3
  171. package/dist/ui/editor/Content.js +0 -13
  172. package/dist/ui/editor/Provider.d.ts +0 -17
  173. package/dist/ui/editor/Provider.js +0 -80
  174. package/dist/ui/editor/Root.d.ts +0 -2
  175. package/dist/ui/editor/Root.js +0 -18
  176. package/dist/ui/editor/Toolbar.d.ts +0 -5
  177. package/dist/ui/editor/Toolbar.js +0 -156
  178. package/dist/ui/editor/index.d.ts +0 -12
  179. package/dist/ui/editor/index.js +0 -11
  180. package/dist/ui/editor/types.d.ts +0 -7
  181. package/dist/ui/editor/types.js +0 -0
  182. package/dist/ui/error/DefaultError.d.ts +0 -2
  183. package/dist/ui/error/DefaultError.js +0 -62
  184. package/dist/ui/error/DefaultNotFound.d.ts +0 -1
  185. package/dist/ui/error/DefaultNotFound.js +0 -37
  186. package/dist/ui/error/Forbidden.d.ts +0 -1
  187. package/dist/ui/error/Forbidden.js +0 -32
  188. package/dist/ui/error/defaultErrorNotification.d.ts +0 -1
  189. package/dist/ui/error/defaultErrorNotification.js +0 -8
  190. package/dist/ui/error/index.js +0 -5
  191. package/dist/ui/form/blurOnError.d.ts +0 -4
  192. package/dist/ui/form/blurOnError.js +0 -11
  193. package/dist/ui/form/buttons/CancelButton.d.ts +0 -5
  194. package/dist/ui/form/buttons/CancelButton.js +0 -44
  195. package/dist/ui/form/buttons/SubmitButton.d.ts +0 -5
  196. package/dist/ui/form/buttons/SubmitButton.js +0 -47
  197. package/dist/ui/form/buttons/SubscribeActionIcon.d.ts +0 -4
  198. package/dist/ui/form/buttons/SubscribeActionIcon.js +0 -15
  199. package/dist/ui/form/buttons/SubscribeButton.d.ts +0 -5
  200. package/dist/ui/form/buttons/SubscribeButton.js +0 -16
  201. package/dist/ui/form/buttons/index.js +0 -4
  202. package/dist/ui/form/context.d.ts +0 -83
  203. package/dist/ui/form/context.js +0 -26
  204. package/dist/ui/form/fields/JsonField.d.ts +0 -2
  205. package/dist/ui/form/fields/JsonField.js +0 -13
  206. package/dist/ui/form/fields/MultiSelectField.d.ts +0 -2
  207. package/dist/ui/form/fields/MultiSelectField.js +0 -15
  208. package/dist/ui/form/fields/NumberField.d.ts +0 -2
  209. package/dist/ui/form/fields/NumberField.js +0 -15
  210. package/dist/ui/form/fields/PassowrdField.d.ts +0 -2
  211. package/dist/ui/form/fields/PassowrdField.js +0 -18
  212. package/dist/ui/form/fields/SelectField.d.ts +0 -2
  213. package/dist/ui/form/fields/SelectField.js +0 -15
  214. package/dist/ui/form/fields/SwitchField.d.ts +0 -2
  215. package/dist/ui/form/fields/SwitchField.js +0 -15
  216. package/dist/ui/form/fields/TextField.d.ts +0 -2
  217. package/dist/ui/form/fields/TextField.js +0 -15
  218. package/dist/ui/form/fields/TextPassowrdField.d.ts +0 -2
  219. package/dist/ui/form/fields/TextPassowrdField.js +0 -51
  220. package/dist/ui/form/fields/TextareaField.d.ts +0 -2
  221. package/dist/ui/form/fields/TextareaField.js +0 -15
  222. package/dist/ui/form/fields/index.js +0 -9
  223. package/dist/ui/form/fieldsSchema.d.ts +0 -12
  224. package/dist/ui/form/fieldsSchema.js +0 -13
  225. package/dist/ui/form/index.js +0 -4
  226. package/dist/ui/hoverPaper/HoverPaper.d.ts +0 -6
  227. package/dist/ui/hoverPaper/HoverPaper.js +0 -15
  228. package/dist/ui/hoverPaper/index.js +0 -3
  229. package/dist/ui/hoverPaper/usePaperHover.d.ts +0 -4
  230. package/dist/ui/hoverPaper/usePaperHover.js +0 -9
  231. package/dist/ui/saveInput/JsonInput.d.ts +0 -6
  232. package/dist/ui/saveInput/JsonInput.js +0 -34
  233. package/dist/ui/saveInput/NumberInput.d.ts +0 -6
  234. package/dist/ui/saveInput/NumberInput.js +0 -27
  235. package/dist/ui/saveInput/SaveInput.d.ts +0 -36
  236. package/dist/ui/saveInput/SaveInput.js +0 -15
  237. package/dist/ui/saveInput/Select.d.ts +0 -6
  238. package/dist/ui/saveInput/Select.js +0 -27
  239. package/dist/ui/saveInput/Switch.d.ts +0 -6
  240. package/dist/ui/saveInput/Switch.js +0 -30
  241. package/dist/ui/saveInput/TextInput.d.ts +0 -6
  242. package/dist/ui/saveInput/TextInput.js +0 -26
  243. package/dist/ui/saveInput/Textarea.d.ts +0 -6
  244. package/dist/ui/saveInput/Textarea.js +0 -26
  245. package/dist/ui/saveInput/index.js +0 -2
  246. package/dist/ui/scrollArea/ScrollArea.js +0 -30
  247. package/dist/ui/scrollArea/ScrollAreaButton.d.ts +0 -5
  248. package/dist/ui/scrollArea/ScrollAreaButton.js +0 -51
  249. package/dist/ui/scrollArea/ScrollAreaContent.d.ts +0 -6
  250. package/dist/ui/scrollArea/ScrollAreaContent.js +0 -29
  251. package/dist/ui/scrollArea/context.js +0 -10
  252. package/dist/ui/scrollArea/index.d.ts +0 -3
  253. package/dist/ui/scrollArea/index.js +0 -3
  254. package/dist/ui/scrollArea/types.d.ts +0 -65
  255. package/dist/ui/scrollArea/types.js +0 -0
  256. package/dist/ui/scrollArea/useScrollArea.d.ts +0 -9
  257. package/dist/ui/scrollArea/useScrollArea.js +0 -146
  258. /package/{dist/ui/error/index.d.ts → src/ui/error/index.ts} +0 -0
  259. /package/{dist/ui/form/buttons/index.d.ts → src/ui/form/buttons/index.ts} +0 -0
  260. /package/{dist/ui/form/fields/index.d.ts → src/ui/form/fields/index.ts} +0 -0
  261. /package/{dist/ui/form/index.d.ts → src/ui/form/index.ts} +0 -0
  262. /package/{dist/ui/hoverPaper/index.d.ts → src/ui/hoverPaper/index.ts} +0 -0
  263. /package/{dist/ui/saveInput/index.d.ts → src/ui/saveInput/index.ts} +0 -0
@@ -0,0 +1,92 @@
1
+ import { Loader } from '@mantine/core';
2
+ import { RichTextEditor } from '@mantine/tiptap';
3
+ import type { ReactNode } from 'react';
4
+ import { useEditor } from './Provider';
5
+
6
+ export const Toolbar = ({
7
+ saving,
8
+ children,
9
+ }: {
10
+ saving?: boolean;
11
+ children?: ReactNode;
12
+ }) => {
13
+ const { editor, disabledToolbar } = useEditor();
14
+
15
+ return !disabledToolbar ? (
16
+ <RichTextEditor.Toolbar sticky>
17
+ <RichTextEditor.ControlsGroup>
18
+ <RichTextEditor.Bold disabled={!editor?.isEditable} />
19
+ <RichTextEditor.Italic disabled={!editor?.isEditable} />
20
+ <RichTextEditor.Underline disabled={!editor?.isEditable} />
21
+ <RichTextEditor.Strikethrough disabled={!editor?.isEditable} />
22
+ <RichTextEditor.ClearFormatting disabled={!editor?.isEditable} />
23
+ <RichTextEditor.Highlight disabled={!editor?.isEditable} />
24
+ <RichTextEditor.Code disabled={!editor?.isEditable} />
25
+ </RichTextEditor.ControlsGroup>
26
+
27
+ <RichTextEditor.ControlsGroup>
28
+ <RichTextEditor.H1 disabled={!editor?.isEditable} />
29
+ <RichTextEditor.H2 disabled={!editor?.isEditable} />
30
+ <RichTextEditor.H3 disabled={!editor?.isEditable} />
31
+ <RichTextEditor.H4 disabled={!editor?.isEditable} />
32
+ </RichTextEditor.ControlsGroup>
33
+
34
+ <RichTextEditor.ControlsGroup>
35
+ <RichTextEditor.Blockquote disabled={!editor?.isEditable} />
36
+ <RichTextEditor.Hr disabled={!editor?.isEditable} />
37
+ <RichTextEditor.BulletList disabled={!editor?.isEditable} />
38
+ <RichTextEditor.OrderedList disabled={!editor?.isEditable} />
39
+ </RichTextEditor.ControlsGroup>
40
+
41
+ <RichTextEditor.ControlsGroup>
42
+ <RichTextEditor.TaskList disabled={!editor?.isEditable} />
43
+ <RichTextEditor.TaskListLift disabled={!editor?.isEditable} />
44
+ <RichTextEditor.TaskListSink disabled={!editor?.isEditable} />
45
+ </RichTextEditor.ControlsGroup>
46
+
47
+ <RichTextEditor.ControlsGroup>
48
+ <RichTextEditor.Link disabled={!editor?.isEditable} />
49
+ <RichTextEditor.Unlink disabled={!editor?.isEditable} />
50
+ </RichTextEditor.ControlsGroup>
51
+
52
+ <RichTextEditor.ControlsGroup>
53
+ <RichTextEditor.AlignLeft disabled={!editor?.isEditable} />
54
+ <RichTextEditor.AlignCenter disabled={!editor?.isEditable} />
55
+ <RichTextEditor.AlignJustify disabled={!editor?.isEditable} />
56
+ <RichTextEditor.AlignRight disabled={!editor?.isEditable} />
57
+ </RichTextEditor.ControlsGroup>
58
+
59
+ <RichTextEditor.ControlsGroup>
60
+ <RichTextEditor.Undo disabled={!editor?.isEditable} />
61
+ <RichTextEditor.Redo disabled={!editor?.isEditable} />
62
+ </RichTextEditor.ControlsGroup>
63
+
64
+ <RichTextEditor.ControlsGroup ml="auto">
65
+ {children}
66
+
67
+ <RichTextEditor.Control ml="10px" style={{ cursor: 'default' }}>
68
+ {saving ? (
69
+ <Loader size={14} />
70
+ ) : (
71
+ <svg
72
+ xmlns="http://www.w3.org/2000/svg"
73
+ width="16"
74
+ height="16"
75
+ viewBox="0 0 16 16"
76
+ fill="none"
77
+ stroke="currentColor"
78
+ strokeWidth="1.5"
79
+ strokeLinecap="round"
80
+ strokeLinejoin="round"
81
+ role="img"
82
+ aria-label="Saved"
83
+ >
84
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
85
+ <path d="M5 12l5 5l10 -10" />
86
+ </svg>
87
+ )}
88
+ </RichTextEditor.Control>
89
+ </RichTextEditor.ControlsGroup>
90
+ </RichTextEditor.Toolbar>
91
+ ) : null;
92
+ };
@@ -0,0 +1,13 @@
1
+ import { Content } from './Content';
2
+ import { Provider } from './Provider';
3
+ import { Root } from './Root';
4
+ import { Toolbar } from './Toolbar';
5
+
6
+ export const Editor = {
7
+ Root,
8
+ Content,
9
+ Toolbar,
10
+ Provider,
11
+ };
12
+
13
+ export { useEditor } from './Provider';
@@ -0,0 +1,7 @@
1
+ export interface EditorProps {
2
+ children: React.ReactNode;
3
+ initialContent?: string;
4
+ initialEditable?: boolean;
5
+ initialDisabledToolbar?: boolean;
6
+ onChange?: (value: string) => void;
7
+ }
@@ -0,0 +1,60 @@
1
+ import { Button, Group, Stack, Title } from '@mantine/core';
2
+ import type { ErrorComponentProps } from '@tanstack/react-router';
3
+ import { rootRouteId, useMatch, useRouter } from '@tanstack/react-router';
4
+ import { RouterLink } from '../RouterLink';
5
+
6
+ export const DefaultError = ({ error }: ErrorComponentProps) => {
7
+ const router = useRouter();
8
+ const isRoot = useMatch({
9
+ strict: false,
10
+ select: (state) => state.id === rootRouteId,
11
+ });
12
+
13
+ return (
14
+ <Stack align="center" justify="center" gap={0} ta="center" h="100vh">
15
+ <Title textWrap="balance" size={65}>
16
+ Произошла ошибка!
17
+ </Title>
18
+ {error.message && (
19
+ <Title
20
+ order={3}
21
+ ml={4}
22
+ textWrap="balance"
23
+ c="var(--mantine-color-error)"
24
+ >
25
+ {error.message}
26
+ </Title>
27
+ )}
28
+
29
+ <Group>
30
+ <Button
31
+ mt="xl"
32
+ size="lg"
33
+ variant="light"
34
+ onClick={() => {
35
+ router.invalidate();
36
+ }}
37
+ >
38
+ Попробовать еще раз
39
+ </Button>
40
+ {isRoot ? (
41
+ <RouterLink.Button to="/" mt="xl" size="lg" radius="md">
42
+ На главную
43
+ </RouterLink.Button>
44
+ ) : (
45
+ <Button
46
+ mt="xl"
47
+ radius="md"
48
+ size="lg"
49
+ onClick={(e) => {
50
+ e.preventDefault();
51
+ router.history.back();
52
+ }}
53
+ >
54
+ Назад
55
+ </Button>
56
+ )}
57
+ </Group>
58
+ </Stack>
59
+ );
60
+ };
@@ -0,0 +1,19 @@
1
+ import { Stack, Text, Title } from '@mantine/core';
2
+ import { RouterLink } from '../RouterLink';
3
+
4
+ export const DefaultNotFound = () => (
5
+ <Stack align="center" justify="center" gap={0} ta="center" h="100vh">
6
+ <Text c="dimmed" fw="bold">
7
+ 404
8
+ </Text>
9
+ <Title textWrap="balance" size={65}>
10
+ Страница не найдена
11
+ </Title>
12
+ <Title order={3} ml={4} textWrap="balance" c="dimmed">
13
+ Извините, но здесь нет страницы. Возможно она перемещена.
14
+ </Title>
15
+ <RouterLink.Button to="/" mt="xl" size="lg" radius="md">
16
+ На главную
17
+ </RouterLink.Button>
18
+ </Stack>
19
+ );
@@ -0,0 +1,18 @@
1
+ import { Stack, Title } from '@mantine/core';
2
+ import { RouterLink } from '../RouterLink';
3
+
4
+ export const Forbidden = () => (
5
+ <Stack align="center" justify="center" gap={0} ta="center" h="100vh">
6
+ <Title textWrap="balance" size={65}>
7
+ Доступ запрещен!
8
+ </Title>
9
+
10
+ <Title order={3} ml={4} textWrap="balance" c="var(--mantine-color-error)">
11
+ У вас нет доступа к этой странице
12
+ </Title>
13
+
14
+ <RouterLink.Button to="/" mt="xl" size="lg" radius="md">
15
+ На главную
16
+ </RouterLink.Button>
17
+ </Stack>
18
+ );
@@ -0,0 +1,9 @@
1
+ import { notifications } from '@mantine/notifications';
2
+
3
+ export const defaultErrorNotification = (error: Error) =>
4
+ notifications.show({
5
+ title: 'Системная ошибка',
6
+ message: error.message,
7
+ color: 'red',
8
+ autoClose: false,
9
+ });
@@ -0,0 +1,21 @@
1
+ import type { AnyFormApi } from '@tanstack/react-form';
2
+
3
+ export const blurOnError = ({ formApi }: { formApi: AnyFormApi }) => {
4
+ const inputNames = Array.from(
5
+ new Set(
6
+ Object.keys(formApi.state.fieldMeta).filter(
7
+ (i) => formApi.state.fieldMeta[i]?.errors.length,
8
+ ),
9
+ ),
10
+ );
11
+
12
+ for (const inputName of inputNames) {
13
+ const input = document.querySelector(
14
+ `#form input[name="${inputName}"]`,
15
+ ) as HTMLInputElement | null;
16
+ if (input) {
17
+ input.focus();
18
+ break;
19
+ }
20
+ }
21
+ };
@@ -0,0 +1,42 @@
1
+ import { Button, type ButtonProps } from '@mantine/core';
2
+ import { useFormContext } from '../context';
3
+
4
+ export const CancelButton = (
5
+ props: ButtonProps & { label?: string; onClick?: () => void },
6
+ ) => {
7
+ const form = useFormContext();
8
+ return (
9
+ <form.Subscribe selector={(state) => state.isSubmitting}>
10
+ {(isSubmitting) => (
11
+ <Button
12
+ type="reset"
13
+ size="xs"
14
+ variant="light"
15
+ leftSection={
16
+ <svg
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ width="16"
19
+ height="16"
20
+ viewBox="0 0 16 16"
21
+ fill="none"
22
+ stroke="currentColor"
23
+ strokeWidth="2"
24
+ strokeLinecap="round"
25
+ strokeLinejoin="round"
26
+ role="img"
27
+ aria-label="Cancel"
28
+ >
29
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
30
+ <path d="M3 12a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
31
+ <path d="M18.364 5.636l-12.728 12.728" />
32
+ </svg>
33
+ }
34
+ disabled={isSubmitting}
35
+ {...props}
36
+ >
37
+ {props.label || 'Отмена'}
38
+ </Button>
39
+ )}
40
+ </form.Subscribe>
41
+ );
42
+ };
@@ -0,0 +1,43 @@
1
+ import { Button, type ButtonProps } from '@mantine/core';
2
+ import { useFormContext } from '../context';
3
+
4
+ export const SubmitButton = (
5
+ props: ButtonProps & { label?: string; onClick?: () => void },
6
+ ) => {
7
+ const form = useFormContext();
8
+ return (
9
+ <form.Subscribe selector={(state) => state}>
10
+ {(state) => (
11
+ <Button
12
+ type="submit"
13
+ size="xs"
14
+ leftSection={
15
+ <svg
16
+ xmlns="http://www.w3.org/2000/svg"
17
+ width="20"
18
+ height="20"
19
+ viewBox="0 0 20 20"
20
+ fill="none"
21
+ stroke="currentColor"
22
+ strokeWidth="2"
23
+ strokeLinecap="round"
24
+ strokeLinejoin="round"
25
+ role="img"
26
+ aria-label="Submit"
27
+ >
28
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
29
+ <path d="M6 4h10l4 4v10a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2" />
30
+ <path d="M10 14a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
31
+ <path d="M14 4l0 4l-6 0l0 -4" />
32
+ </svg>
33
+ }
34
+ loading={state.isSubmitting}
35
+ disabled={!state.isDirty}
36
+ {...props}
37
+ >
38
+ {props.label || 'Сохранить'}
39
+ </Button>
40
+ )}
41
+ </form.Subscribe>
42
+ );
43
+ };
@@ -0,0 +1,18 @@
1
+ import { ActionIcon, type ActionIconProps } from '@mantine/core';
2
+ import { useFormContext } from '../context';
3
+
4
+ export const SubscribeActionIcon = (
5
+ props: ActionIconProps & { onClick?: () => void },
6
+ ) => {
7
+ const form = useFormContext();
8
+
9
+ return (
10
+ <form.Subscribe selector={(state) => state.isSubmitting}>
11
+ {(isSubmitting) => (
12
+ <ActionIcon disabled={isSubmitting} {...props}>
13
+ {props.children}
14
+ </ActionIcon>
15
+ )}
16
+ </form.Subscribe>
17
+ );
18
+ };
@@ -0,0 +1,17 @@
1
+ import { Button, type ButtonProps } from '@mantine/core';
2
+ import { useFormContext } from '../context';
3
+
4
+ export const SubscribeButton = (
5
+ props: ButtonProps & { label: string; onClick?: () => void },
6
+ ) => {
7
+ const form = useFormContext();
8
+ return (
9
+ <form.Subscribe selector={(state) => state.isSubmitting}>
10
+ {(isSubmitting) => (
11
+ <Button type="button" disabled={isSubmitting} {...props}>
12
+ {props.label}
13
+ </Button>
14
+ )}
15
+ </form.Subscribe>
16
+ );
17
+ };
@@ -0,0 +1,45 @@
1
+ import { createFormHook, createFormHookContexts } from '@tanstack/react-form';
2
+ import {
3
+ CancelButton,
4
+ SubmitButton,
5
+ SubscribeActionIcon,
6
+ SubscribeButton,
7
+ } from './buttons';
8
+ import {
9
+ JsonField,
10
+ MultiSelectField,
11
+ NumberField,
12
+ PassowrdField,
13
+ SelectField,
14
+ SwitchField,
15
+ TextareaField,
16
+ TextField,
17
+ TextPassowrdField,
18
+ } from './fields';
19
+
20
+ const { fieldContext, useFieldContext, formContext, useFormContext } =
21
+ createFormHookContexts();
22
+
23
+ const { useAppForm, withForm } = createFormHook({
24
+ fieldComponents: {
25
+ TextField,
26
+ NumberField,
27
+ TextareaField,
28
+ TextPassowrdField,
29
+ PassowrdField,
30
+ SelectField,
31
+ MultiSelectField,
32
+ SwitchField,
33
+ JsonField,
34
+ },
35
+ formComponents: {
36
+ SubmitButton,
37
+ CancelButton,
38
+ SubscribeButton,
39
+ SubscribeActionIcon,
40
+ },
41
+ fieldContext,
42
+ formContext,
43
+ });
44
+
45
+ export { useAppForm, useFieldContext, useFormContext, withForm };
@@ -0,0 +1,16 @@
1
+ import type { ReactCodeMirrorProps } from '@uiw/react-codemirror';
2
+ import { JsonInput } from '../../JsonInput';
3
+ import { useFieldContext } from '../context';
4
+
5
+ export const JsonField = (props: ReactCodeMirrorProps) => {
6
+ const field = useFieldContext<string>();
7
+
8
+ return (
9
+ <JsonInput
10
+ value={field.state.value || ''}
11
+ onChange={field.handleChange}
12
+ onBlur={field.handleBlur}
13
+ {...props}
14
+ />
15
+ );
16
+ };
@@ -0,0 +1,17 @@
1
+ import { MultiSelect, type MultiSelectProps } from '@mantine/core';
2
+ import { useFieldContext } from '../context';
3
+
4
+ export const MultiSelectField = (props: MultiSelectProps) => {
5
+ const field = useFieldContext<string[]>();
6
+
7
+ return (
8
+ <MultiSelect
9
+ name={field.name}
10
+ value={field.state.value || []}
11
+ onChange={field.handleChange}
12
+ onBlur={field.handleBlur}
13
+ error={field.state.meta.errors.map((error) => error.message).join(', ')}
14
+ {...props}
15
+ />
16
+ );
17
+ };
@@ -0,0 +1,17 @@
1
+ import { NumberInput, type NumberInputProps } from '@mantine/core';
2
+ import { useFieldContext } from '../context';
3
+
4
+ export const NumberField = (props: NumberInputProps) => {
5
+ const field = useFieldContext<string | number>();
6
+
7
+ return (
8
+ <NumberInput
9
+ name={field.name}
10
+ value={field.state.value || ''}
11
+ onChange={(v) => field.handleChange(v)}
12
+ onBlur={field.handleBlur}
13
+ error={field.state.meta.errors.map((error) => error.message).join(', ')}
14
+ {...props}
15
+ />
16
+ );
17
+ };
@@ -0,0 +1,20 @@
1
+ import { PasswordInput, type PasswordInputProps } from '@mantine/core';
2
+ import { useFieldContext } from '../context';
3
+
4
+ export const PassowrdField = (props: PasswordInputProps) => {
5
+ const field = useFieldContext<string>();
6
+
7
+ return (
8
+ <PasswordInput
9
+ size="lg"
10
+ label="Пароль"
11
+ placeholder="Введите пароль"
12
+ name={field.name}
13
+ value={field.state.value || ''}
14
+ onChange={(e) => field.handleChange(e.target.value)}
15
+ onBlur={field.handleBlur}
16
+ error={field.state.meta.errors.map((error) => error.message).join(', ')}
17
+ {...props}
18
+ />
19
+ );
20
+ };
@@ -0,0 +1,17 @@
1
+ import { Select, type SelectProps } from '@mantine/core';
2
+ import { useFieldContext } from '../context';
3
+
4
+ export const SelectField = (props: SelectProps) => {
5
+ const field = useFieldContext<string | null>();
6
+
7
+ return (
8
+ <Select
9
+ name={field.name}
10
+ value={field.state.value}
11
+ onChange={field.handleChange}
12
+ onBlur={field.handleBlur}
13
+ error={field.state.meta.errors.map((error) => error.message).join(', ')}
14
+ {...props}
15
+ />
16
+ );
17
+ };
@@ -0,0 +1,17 @@
1
+ import { Switch, type SwitchProps } from '@mantine/core';
2
+ import { useFieldContext } from '../context';
3
+
4
+ export const SwitchField = (props: SwitchProps) => {
5
+ const field = useFieldContext<boolean>();
6
+
7
+ return (
8
+ <Switch
9
+ name={field.name}
10
+ checked={field.state.value}
11
+ onChange={(e) => field.handleChange(e.currentTarget.checked)}
12
+ error={field.state.meta.errors.map((error) => error.message).join(', ')}
13
+ w="fit-content"
14
+ {...props}
15
+ />
16
+ );
17
+ };
@@ -0,0 +1,17 @@
1
+ import { TextInput, type TextInputProps } from '@mantine/core';
2
+ import { useFieldContext } from '../context';
3
+
4
+ export const TextField = (props: TextInputProps) => {
5
+ const field = useFieldContext<string>();
6
+
7
+ return (
8
+ <TextInput
9
+ name={field.name}
10
+ value={field.state.value || ''}
11
+ onChange={(e) => field.handleChange(e.target.value)}
12
+ onBlur={field.handleBlur}
13
+ error={field.state.meta.errors.map((error) => error.message).join(', ')}
14
+ {...props}
15
+ />
16
+ );
17
+ };
@@ -0,0 +1,51 @@
1
+ import { ActionIcon, TextInput, type TextInputProps } from '@mantine/core';
2
+ import generatePassword from 'omgopass';
3
+ import { useFieldContext } from '../context';
4
+
5
+ export const TextPassowrdField = (props: TextInputProps) => {
6
+ const field = useFieldContext<string>();
7
+
8
+ return (
9
+ <TextInput
10
+ label="Пароль"
11
+ placeholder="Введите пароль"
12
+ rightSection={
13
+ <ActionIcon
14
+ variant="light"
15
+ onClick={() =>
16
+ field.setValue(
17
+ generatePassword({
18
+ minSyllableLength: 2,
19
+ maxSyllableLength: 2,
20
+ }),
21
+ )
22
+ }
23
+ >
24
+ <svg
25
+ xmlns="http://www.w3.org/2000/svg"
26
+ width="16"
27
+ height="16"
28
+ viewBox="0 0 16 16"
29
+ fill="none"
30
+ stroke="currentColor"
31
+ strokeWidth="2"
32
+ strokeLinecap="round"
33
+ strokeLinejoin="round"
34
+ role="img"
35
+ aria-label="Refresh"
36
+ >
37
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
38
+ <path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4" />
39
+ <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4" />
40
+ </svg>
41
+ </ActionIcon>
42
+ }
43
+ name={field.name}
44
+ value={field.state.value || ''}
45
+ onChange={(e) => field.handleChange(e.target.value)}
46
+ onBlur={field.handleBlur}
47
+ error={field.state.meta.errors.map((error) => error.message).join(', ')}
48
+ {...props}
49
+ />
50
+ );
51
+ };
@@ -0,0 +1,17 @@
1
+ import { Textarea, type TextareaProps } from '@mantine/core';
2
+ import { useFieldContext } from '../context';
3
+
4
+ export const TextareaField = (props: TextareaProps) => {
5
+ const field = useFieldContext<string>();
6
+
7
+ return (
8
+ <Textarea
9
+ name={field.name}
10
+ value={field.state.value || ''}
11
+ onChange={(e) => field.handleChange(e.target.value)}
12
+ onBlur={field.handleBlur}
13
+ error={field.state.meta.errors.map((error) => error.message).join(', ')}
14
+ {...props}
15
+ />
16
+ );
17
+ };
@@ -0,0 +1,24 @@
1
+ import z from 'zod/v4';
2
+
3
+ export const fieldsSchema = {
4
+ id: z.string().min(1, 'ID не может быть пустым'),
5
+ str: z.string().min(1, 'Значение не может быть пустым'),
6
+ strArr: z.array(z.string().min(1, 'Значение не может быть пустым')),
7
+ strMin3: z.string().min(3, 'Должно содержать не менее 3 символов'),
8
+ select: z.string().min(1, 'Выберите значение'),
9
+ email: z.email('Неверный формат email'),
10
+ username: z
11
+ .string()
12
+ .min(3, 'Должен быть не менее 3 символов')
13
+ .max(24, 'Должен быть не более 24 символов'),
14
+ password: z
15
+ .string('Пароль не может быть пустым')
16
+ .min(8, 'Пароль должен содержать не менее 8 символов'),
17
+ kebab: z
18
+ .string()
19
+ .min(3, 'Минимум 3 символа')
20
+ .regex(
21
+ /^[a-z0-9]+(-[a-z0-9]+)*$/,
22
+ 'Должен быть в формате kebab-case (только строчные буквы, цифры и дефисы)',
23
+ ),
24
+ };
@@ -0,0 +1,17 @@
1
+ import { type ElementProps, Paper, type PaperProps } from '@mantine/core';
2
+ import { forwardRef } from 'react';
3
+
4
+ interface Props extends PaperProps, ElementProps<'div', keyof PaperProps> {
5
+ disabled?: boolean;
6
+ }
7
+
8
+ export const HoverPaper = forwardRef<HTMLDivElement, Props>(
9
+ ({ disabled, classNames, ...props }: Props, ref) => (
10
+ <Paper
11
+ ref={ref}
12
+ classNames={{ root: 'rolder-hover-paper-root', ...classNames }}
13
+ mod={{ disabled }}
14
+ {...props}
15
+ />
16
+ ),
17
+ );
@@ -0,0 +1,9 @@
1
+ import { useHover } from '@mantine/hooks';
2
+
3
+ export const usePaperHover = (): {
4
+ paperHovered: boolean;
5
+ paperRef: (node: HTMLDivElement | null) => void;
6
+ } => {
7
+ const { hovered: paperHovered, ref: paperRef } = useHover<HTMLDivElement>();
8
+ return { paperHovered, paperRef };
9
+ };