@owlmeans/web-panel 0.1.0

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 (226) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1195 -0
  3. package/build/.gitkeep +0 -0
  4. package/build/auth/context.d.ts +5 -0
  5. package/build/auth/context.d.ts.map +1 -0
  6. package/build/auth/context.js +17 -0
  7. package/build/auth/context.js.map +1 -0
  8. package/build/auth/exports.d.ts +25 -0
  9. package/build/auth/exports.d.ts.map +1 -0
  10. package/build/auth/exports.js +19 -0
  11. package/build/auth/exports.js.map +1 -0
  12. package/build/auth/index.d.ts +5 -0
  13. package/build/auth/index.d.ts.map +1 -0
  14. package/build/auth/index.js +4 -0
  15. package/build/auth/index.js.map +1 -0
  16. package/build/auth/modules.d.ts +2 -0
  17. package/build/auth/modules.d.ts.map +1 -0
  18. package/build/auth/modules.js +4 -0
  19. package/build/auth/modules.js.map +1 -0
  20. package/build/auth/plugins/basic-ed25519.d.ts +3 -0
  21. package/build/auth/plugins/basic-ed25519.d.ts.map +1 -0
  22. package/build/auth/plugins/basic-ed25519.js +40 -0
  23. package/build/auth/plugins/basic-ed25519.js.map +1 -0
  24. package/build/auth/plugins/exports.d.ts +4 -0
  25. package/build/auth/plugins/exports.d.ts.map +1 -0
  26. package/build/auth/plugins/exports.js +4 -0
  27. package/build/auth/plugins/exports.js.map +1 -0
  28. package/build/auth/plugins/index.d.ts +6 -0
  29. package/build/auth/plugins/index.d.ts.map +1 -0
  30. package/build/auth/plugins/index.js +10 -0
  31. package/build/auth/plugins/index.js.map +1 -0
  32. package/build/auth/plugins/re-captcha.d.ts +3 -0
  33. package/build/auth/plugins/re-captcha.d.ts.map +1 -0
  34. package/build/auth/plugins/re-captcha.js +51 -0
  35. package/build/auth/plugins/re-captcha.js.map +1 -0
  36. package/build/auth/plugins/tunnel-consumer.d.ts +3 -0
  37. package/build/auth/plugins/tunnel-consumer.d.ts.map +1 -0
  38. package/build/auth/plugins/tunnel-consumer.js +40 -0
  39. package/build/auth/plugins/tunnel-consumer.js.map +1 -0
  40. package/build/auth/types.d.ts +10 -0
  41. package/build/auth/types.d.ts.map +1 -0
  42. package/build/auth/types.js +2 -0
  43. package/build/auth/types.js.map +1 -0
  44. package/build/components/block.d.ts +4 -0
  45. package/build/components/block.d.ts.map +1 -0
  46. package/build/components/block.js +15 -0
  47. package/build/components/block.js.map +1 -0
  48. package/build/components/button/index.d.ts +3 -0
  49. package/build/components/button/index.d.ts.map +1 -0
  50. package/build/components/button/index.js +2 -0
  51. package/build/components/button/index.js.map +1 -0
  52. package/build/components/button/selector.d.ts +4 -0
  53. package/build/components/button/selector.d.ts.map +1 -0
  54. package/build/components/button/selector.js +8 -0
  55. package/build/components/button/selector.js.map +1 -0
  56. package/build/components/button/types.d.ts +7 -0
  57. package/build/components/button/types.d.ts.map +1 -0
  58. package/build/components/button/types.js +2 -0
  59. package/build/components/button/types.js.map +1 -0
  60. package/build/components/form/button/component.d.ts +5 -0
  61. package/build/components/form/button/component.d.ts.map +1 -0
  62. package/build/components/form/button/component.js +34 -0
  63. package/build/components/form/button/component.js.map +1 -0
  64. package/build/components/form/button/index.d.ts +3 -0
  65. package/build/components/form/button/index.d.ts.map +1 -0
  66. package/build/components/form/button/index.js +3 -0
  67. package/build/components/form/button/index.js.map +1 -0
  68. package/build/components/form/button/types.d.ts +15 -0
  69. package/build/components/form/button/types.d.ts.map +1 -0
  70. package/build/components/form/button/types.js +2 -0
  71. package/build/components/form/button/types.js.map +1 -0
  72. package/build/components/form/component.d.ts +4 -0
  73. package/build/components/form/component.d.ts.map +1 -0
  74. package/build/components/form/component.js +59 -0
  75. package/build/components/form/component.js.map +1 -0
  76. package/build/components/form/index.d.ts +3 -0
  77. package/build/components/form/index.d.ts.map +1 -0
  78. package/build/components/form/index.js +2 -0
  79. package/build/components/form/index.js.map +1 -0
  80. package/build/components/form/text/component.d.ts +4 -0
  81. package/build/components/form/text/component.d.ts.map +1 -0
  82. package/build/components/form/text/component.js +34 -0
  83. package/build/components/form/text/component.js.map +1 -0
  84. package/build/components/form/text/index.d.ts +3 -0
  85. package/build/components/form/text/index.d.ts.map +1 -0
  86. package/build/components/form/text/index.js +3 -0
  87. package/build/components/form/text/index.js.map +1 -0
  88. package/build/components/form/text/types.d.ts +11 -0
  89. package/build/components/form/text/types.d.ts.map +1 -0
  90. package/build/components/form/text/types.js +2 -0
  91. package/build/components/form/text/types.js.map +1 -0
  92. package/build/components/form/types.d.ts +6 -0
  93. package/build/components/form/types.d.ts.map +1 -0
  94. package/build/components/form/types.js +2 -0
  95. package/build/components/form/types.js.map +1 -0
  96. package/build/components/helper.d.ts +6 -0
  97. package/build/components/helper.d.ts.map +1 -0
  98. package/build/components/helper.js +70 -0
  99. package/build/components/helper.js.map +1 -0
  100. package/build/components/index.d.ts +14 -0
  101. package/build/components/index.d.ts.map +1 -0
  102. package/build/components/index.js +13 -0
  103. package/build/components/index.js.map +1 -0
  104. package/build/components/layout/component.d.ts +4 -0
  105. package/build/components/layout/component.d.ts.map +1 -0
  106. package/build/components/layout/component.js +6 -0
  107. package/build/components/layout/component.js.map +1 -0
  108. package/build/components/layout/index.d.ts +3 -0
  109. package/build/components/layout/index.d.ts.map +1 -0
  110. package/build/components/layout/index.js +3 -0
  111. package/build/components/layout/index.js.map +1 -0
  112. package/build/components/layout/types.d.ts +4 -0
  113. package/build/components/layout/types.d.ts.map +1 -0
  114. package/build/components/layout/types.js +2 -0
  115. package/build/components/layout/types.js.map +1 -0
  116. package/build/components/link.d.ts +4 -0
  117. package/build/components/link.d.ts.map +1 -0
  118. package/build/components/link.js +27 -0
  119. package/build/components/link.js.map +1 -0
  120. package/build/components/panel-app/component.d.ts +4 -0
  121. package/build/components/panel-app/component.d.ts.map +1 -0
  122. package/build/components/panel-app/component.js +12 -0
  123. package/build/components/panel-app/component.js.map +1 -0
  124. package/build/components/panel-app/index.d.ts +3 -0
  125. package/build/components/panel-app/index.d.ts.map +1 -0
  126. package/build/components/panel-app/index.js +3 -0
  127. package/build/components/panel-app/index.js.map +1 -0
  128. package/build/components/panel-app/types.d.ts +6 -0
  129. package/build/components/panel-app/types.d.ts.map +1 -0
  130. package/build/components/panel-app/types.js +2 -0
  131. package/build/components/panel-app/types.js.map +1 -0
  132. package/build/components/status.d.ts +4 -0
  133. package/build/components/status.d.ts.map +1 -0
  134. package/build/components/status.js +19 -0
  135. package/build/components/status.js.map +1 -0
  136. package/build/components/text.d.ts +4 -0
  137. package/build/components/text.d.ts.map +1 -0
  138. package/build/components/text.js +9 -0
  139. package/build/components/text.js.map +1 -0
  140. package/build/components/types.d.ts +32 -0
  141. package/build/components/types.d.ts.map +1 -0
  142. package/build/components/types.js +2 -0
  143. package/build/components/types.js.map +1 -0
  144. package/build/components/uploader/image.d.ts +4 -0
  145. package/build/components/uploader/image.d.ts.map +1 -0
  146. package/build/components/uploader/image.js +24 -0
  147. package/build/components/uploader/image.js.map +1 -0
  148. package/build/components/uploader/index.d.ts +2 -0
  149. package/build/components/uploader/index.d.ts.map +1 -0
  150. package/build/components/uploader/index.js +2 -0
  151. package/build/components/uploader/index.js.map +1 -0
  152. package/build/components/uploader/types.d.ts +5 -0
  153. package/build/components/uploader/types.d.ts.map +1 -0
  154. package/build/components/uploader/types.js +2 -0
  155. package/build/components/uploader/types.js.map +1 -0
  156. package/build/context.d.ts +4 -0
  157. package/build/context.d.ts.map +1 -0
  158. package/build/context.js +12 -0
  159. package/build/context.js.map +1 -0
  160. package/build/exports.d.ts +19 -0
  161. package/build/exports.d.ts.map +1 -0
  162. package/build/exports.js +17 -0
  163. package/build/exports.js.map +1 -0
  164. package/build/index.d.ts +8 -0
  165. package/build/index.d.ts.map +1 -0
  166. package/build/index.js +7 -0
  167. package/build/index.js.map +1 -0
  168. package/build/main.d.ts +5 -0
  169. package/build/main.d.ts.map +1 -0
  170. package/build/main.js +14 -0
  171. package/build/main.js.map +1 -0
  172. package/build/modules.d.ts +2 -0
  173. package/build/modules.d.ts.map +1 -0
  174. package/build/modules.js +4 -0
  175. package/build/modules.js.map +1 -0
  176. package/build/types.d.ts +9 -0
  177. package/build/types.d.ts.map +1 -0
  178. package/build/types.js +2 -0
  179. package/build/types.js.map +1 -0
  180. package/package.json +89 -0
  181. package/src/auth/context.ts +25 -0
  182. package/src/auth/exports.ts +29 -0
  183. package/src/auth/index.ts +5 -0
  184. package/src/auth/modules.ts +5 -0
  185. package/src/auth/plugins/basic-ed25519.tsx +57 -0
  186. package/src/auth/plugins/exports.ts +4 -0
  187. package/src/auth/plugins/index.ts +15 -0
  188. package/src/auth/plugins/re-captcha.tsx +73 -0
  189. package/src/auth/plugins/tunnel-consumer.tsx +65 -0
  190. package/src/auth/types.ts +11 -0
  191. package/src/components/block.tsx +27 -0
  192. package/src/components/button/index.ts +3 -0
  193. package/src/components/button/selector.tsx +16 -0
  194. package/src/components/button/types.ts +7 -0
  195. package/src/components/form/button/component.tsx +53 -0
  196. package/src/components/form/button/index.ts +3 -0
  197. package/src/components/form/button/types.ts +16 -0
  198. package/src/components/form/component.tsx +101 -0
  199. package/src/components/form/index.ts +3 -0
  200. package/src/components/form/text/component.tsx +45 -0
  201. package/src/components/form/text/index.ts +3 -0
  202. package/src/components/form/text/types.ts +11 -0
  203. package/src/components/form/types.ts +6 -0
  204. package/src/components/helper.ts +79 -0
  205. package/src/components/index.ts +17 -0
  206. package/src/components/layout/component.tsx +7 -0
  207. package/src/components/layout/index.ts +3 -0
  208. package/src/components/layout/types.ts +4 -0
  209. package/src/components/link.tsx +34 -0
  210. package/src/components/panel-app/component.tsx +21 -0
  211. package/src/components/panel-app/index.ts +3 -0
  212. package/src/components/panel-app/types.ts +6 -0
  213. package/src/components/status.tsx +23 -0
  214. package/src/components/text.tsx +14 -0
  215. package/src/components/types.ts +35 -0
  216. package/src/components/uploader/image.tsx +31 -0
  217. package/src/components/uploader/index.ts +2 -0
  218. package/src/components/uploader/types.ts +6 -0
  219. package/src/context.ts +18 -0
  220. package/src/exports.ts +22 -0
  221. package/src/index.ts +9 -0
  222. package/src/main.tsx +19 -0
  223. package/src/modules.ts +5 -0
  224. package/src/types.ts +11 -0
  225. package/tsconfig.json +16 -0
  226. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,65 @@
1
+ import LinearProgress from '@mui/material/LinearProgress'
2
+ import { AuthenticationStage } from '@owlmeans/auth'
3
+ import type { RelyToken } from '@owlmeans/auth'
4
+ import type { TunnelAuthenticationRenderer } from '@owlmeans/client-auth/manager/plugins'
5
+ import { PinSchema } from '@owlmeans/client-auth/manager/plugins'
6
+ import { Form } from '../../components/form/component.js'
7
+ import { TextInput } from '../../components/form/text/component.js'
8
+ // import { Text } from '../../components/text.js'
9
+ import { useMemo } from 'react'
10
+ import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope'
11
+ import { Block } from '../../components/block.js'
12
+ import { BlockScaling } from '@owlmeans/client-panel'
13
+ import { Status } from '../../components/status.js'
14
+ import type { SxProps } from '@mui/material/styles'
15
+ import { useTheme } from '@mui/material/styles'
16
+ import { Button } from '../../components/form/button/component.js'
17
+ import { QRCodeCanvas } from 'qrcode.react'
18
+ import Box from '@mui/material/Box'
19
+
20
+ export const TunnelConsumerUIPlugin: TunnelAuthenticationRenderer = ({ type, stage, control, params, submit }) => {
21
+ const rely = useMemo(() => {
22
+ if (control.allowance == null) {
23
+ return null
24
+ }
25
+ return makeEnvelopeModel<RelyToken>(
26
+ makeEnvelopeModel<string>(control.allowance.challenge, EnvelopeKind.Wrap).message(true)
27
+ , EnvelopeKind.Wrap
28
+ ).message()
29
+ }, [stage])
30
+
31
+ const theme = useTheme()
32
+ const prefix = "prefix" in params ? params.prefix as string : ""
33
+ const i18n = { ns: "lib", resource: 'client-panel-auth' }
34
+
35
+ const loadingStyle: SxProps = {
36
+ width: { xs: '100%', sm: '100%', md: '50%' }
37
+ }
38
+
39
+ switch (stage) {
40
+ case AuthenticationStage.Authenticate:
41
+ return <Form decorate name={type} validation={PinSchema} onSubmit={submit} i18n={i18n}>
42
+ <Box width="fit-content" margin="auto">
43
+ {rely?.token != null && <QRCodeCanvas size={256}
44
+ value={`${prefix}${rely?.token ?? ""}`}
45
+ fgColor={theme.palette.primary.dark}
46
+ bgColor={theme.palette.background.paper}
47
+ />}
48
+ </Box>
49
+ {/* <Text>{rely?.token}</Text> */}
50
+ <TextInput name="pin" label hint />
51
+ </Form>
52
+ case AuthenticationStage.Error:
53
+ return <Block horizontal={BlockScaling.Half} i18n={i18n} Actions={() =>
54
+ // @TODO document reload looks a bit dirty - cause we can loose the flow
55
+ <Button label="reset" onClick={async () => { document.location.reload() }} />
56
+ // <Button label="reset" onClick={async () => {
57
+ // await control.updateStage(AuthenticationStage.Authenticate)
58
+ // }} />
59
+ }>
60
+ <Status error={control.error} ok={false} />
61
+ </Block>
62
+ }
63
+
64
+ return <LinearProgress sx={loadingStyle} />
65
+ }
@@ -0,0 +1,11 @@
1
+ import type { ClientConfig } from '@owlmeans/client-context'
2
+ import type { ClientContext } from '@owlmeans/client'
3
+ import type { WithFlowConfig } from '@owlmeans/flow'
4
+ import type { FlowService } from '@owlmeans/web-flow'
5
+
6
+ export interface AppConfig extends ClientConfig, WithFlowConfig {
7
+ }
8
+
9
+ export interface AppContext<C extends AppConfig = AppConfig> extends ClientContext<C> {
10
+ flow: () => FlowService
11
+ }
@@ -0,0 +1,27 @@
1
+ import { useMemo } from 'react'
2
+ import type { FC } from 'react'
3
+ import type { BlockProps } from './types.js'
4
+ import Card from '@mui/material/Card'
5
+ import CardContent from '@mui/material/CardContent'
6
+ import { PanelContext, usePanelHelper } from '@owlmeans/client-panel'
7
+ import type { SxProps } from '@mui/material/styles'
8
+ import useTheme from '@mui/material/styles/useTheme.js'
9
+ import CardActions from '@mui/material/CardActions'
10
+ import { scalingToStyles } from './helper.js'
11
+
12
+ export const Block: FC<BlockProps> = ({ children, horizontal, vertical, Actions, i18n, styles }) => {
13
+ const theme = useTheme()
14
+
15
+ const style: SxProps = useMemo(() => scalingToStyles(horizontal, vertical, theme), [horizontal])
16
+
17
+ const panelProps = { ...usePanelHelper(), ...i18n }
18
+
19
+ return <PanelContext {...panelProps}>
20
+ <Card sx={{ ...style, ...styles } as SxProps}>
21
+ <CardContent>{children}</CardContent>
22
+ {Actions != null && <CardActions sx={{ flexDirection: "row", justifyContent: "flex-end", pr: 2, pb: 2 }}>
23
+ <Actions />
24
+ </CardActions>}
25
+ </Card>
26
+ </PanelContext>
27
+ }
@@ -0,0 +1,3 @@
1
+
2
+ export * from './selector.js'
3
+ export type * from './types.js'
@@ -0,0 +1,16 @@
1
+
2
+ import ButtonGroup from '@mui/material/ButtonGroup'
3
+ import type { FC } from 'react'
4
+ import type { SelectorProps } from './types.js'
5
+ import { Button } from '../form/button/component.js'
6
+
7
+ export const ButtonSelector: FC<SelectorProps> = ({ name, options, current, onSelect }) => {
8
+ const prefix = name != null ? `${name}.` : ''
9
+ return <ButtonGroup>
10
+ {options.map(
11
+ option => <Button key={option} label={`${prefix}${option}`}
12
+ onClick={() => onSelect?.(option)}
13
+ variant={current === option ? 'contained' : 'outlined'} />
14
+ )}
15
+ </ButtonGroup>
16
+ }
@@ -0,0 +1,7 @@
1
+
2
+ export interface SelectorProps<T = any> {
3
+ name?: string
4
+ current: T
5
+ options: T[]
6
+ onSelect?: (value: T) => void
7
+ }
@@ -0,0 +1,53 @@
1
+ import { useMemo } from 'react'
2
+ import type { FC } from 'react'
3
+ import { memo } from 'react'
4
+ import type { ButtonProps, SubmitProps } from './types'
5
+
6
+ import MUIButton from '@mui/material/Button'
7
+ import CircularProgress from '@mui/material/CircularProgress'
8
+
9
+ import { useFormContext } from 'react-hook-form'
10
+ import { I18nProps, useCommonI18n, useI18nApp, useI18nLib } from '@owlmeans/client-i18n'
11
+ import { useContext } from '@owlmeans/client'
12
+ import { useFormI18n, usePanelHelper } from '@owlmeans/client-panel'
13
+
14
+ export const Button: FC<ButtonProps> = memo(({ label, onClick, i18n, loader, size, fullWidth, variant = 'contained' }) => {
15
+ const context = useContext()
16
+ const panel = usePanelHelper()
17
+ const t = useCommonI18n(
18
+ i18n?.resource ?? panel.resource ?? context.cfg.service,
19
+ i18n?.ns ?? panel.ns,
20
+ i18n?.prefix ?? panel.prefix
21
+ )
22
+ const appT = useI18nApp(context.cfg.service, 'buttons')
23
+ const libT = useI18nLib('client-panel', 'buttons')
24
+ label = useMemo(() => i18n?.suppress ? label : t(label, {
25
+ defaultValue: appT(label, { defaultValue: libT(label) })
26
+ }), [i18n?.suppress, label])
27
+
28
+ size = size ?? 'medium'
29
+ const progressSize = size === 'large'
30
+ ? 20
31
+ : size === 'medium' ? 16 : 14
32
+
33
+ return <MUIButton variant={variant as any} size={size} fullWidth={fullWidth}
34
+ startIcon={loader != null && loader.opened === true ? <CircularProgress size={progressSize} /> : undefined}
35
+ disabled={loader != null && loader.opened === true}
36
+ onClick={onClick}>{label}</MUIButton>
37
+ })
38
+
39
+ export const SubmitButton: FC<SubmitProps> = memo((props) => {
40
+ let { i18n, label } = props
41
+ const { handleSubmit } = useFormContext()
42
+ const t = useFormI18n()
43
+
44
+ label = label ?? 'submit'
45
+ const _i18n: I18nProps["i18n"] = { ...i18n }
46
+ _i18n.suppress = true
47
+
48
+ return <Button {...props} label={t(label)} i18n={_i18n}
49
+ onClick={handleSubmit(
50
+ props.onSubmit ?? props.onClick ?? (() => { console.info('Empty submit') }),
51
+ problem => console.error('Failed to submit form with error: ', problem)
52
+ )} />
53
+ })
@@ -0,0 +1,3 @@
1
+
2
+ export * from './component.js'
3
+ export * from './types.js'
@@ -0,0 +1,16 @@
1
+ import type { Toggleable } from '@owlmeans/client'
2
+ import type { I18nProps } from '@owlmeans/client-i18n'
3
+
4
+ export interface ButtonProps extends I18nProps {
5
+ size?: 'small' | 'medium' | 'large'
6
+ fullWidth?: boolean
7
+ variant?: string
8
+ label: string
9
+ loader?: Toggleable
10
+ onClick?: () => void
11
+ }
12
+
13
+ export interface SubmitProps extends Omit<ButtonProps, "label"> {
14
+ label?: string
15
+ onSubmit?: (data: any) => Promise<void> | void
16
+ }
@@ -0,0 +1,101 @@
1
+ import type { FC } from 'react'
2
+ import { useCallback, useMemo } from 'react'
3
+ import { FormProvider, useForm } from 'react-hook-form'
4
+ import { ajvResolver } from '@hookform/resolvers/ajv'
5
+ import Grid from '@mui/material/Grid'
6
+ import Card from '@mui/material/Card'
7
+ import CardContent from '@mui/material/CardContent'
8
+ import CardActions from '@mui/material/CardActions'
9
+ import { FormContext, schemaToFormDefault } from '@owlmeans/client-panel'
10
+ import type { JSONSchemaType } from 'ajv'
11
+ import Ajv from 'ajv'
12
+ import formatsPlugin from 'ajv-formats'
13
+ import type { SxProps } from '@mui/material'
14
+ import { SubmitButton } from './button/component.js'
15
+ import { useToggle } from '@owlmeans/client'
16
+ import { scalingToStyles } from '../helper.js'
17
+ import { ResilientError } from '@owlmeans/error'
18
+ import { Status } from '../status.js'
19
+ import useTheme from '@mui/material/styles/useTheme.js'
20
+ import type { WebFormProps } from './types.js'
21
+
22
+ const ajv = new Ajv({ coerceTypes: true })
23
+ formatsPlugin(ajv)
24
+
25
+ export const Form: FC<WebFormProps> = (props) => {
26
+ const {
27
+ defaults, children, formRef, validation, name, horizontal, vertical,
28
+ decorate, onSubmit, i18n, styles
29
+ } = props
30
+ const theme = useTheme()
31
+ const _defaults = useMemo(
32
+ () => defaults ?? (validation != null ? schemaToFormDefault(validation) : undefined),
33
+ [name, defaults != null, validation != null]
34
+ )
35
+
36
+ const loader = useToggle(false)
37
+
38
+ const form = useForm({
39
+ mode: 'all',
40
+ defaultValues: _defaults,
41
+ resolver: validation
42
+ ? ajvResolver(validation as JSONSchemaType<unknown>, { formats: ajv.formats, coerceTypes: true })
43
+ : undefined,
44
+ delayError: 300
45
+ })
46
+
47
+ const update = useCallback(((data: Record<string, any>) => {
48
+ const fields = validation != null
49
+ ? Object.keys((validation as JSONSchemaType<any>).properties)
50
+ : Object.keys(data)
51
+ fields.forEach(key => {
52
+ form.setValue(key, data[key])
53
+ })
54
+ }) as <T>(data: T) => void, [name])
55
+
56
+ const setError = useCallback((error: unknown, target: string = 'root') => {
57
+ form.setError(target, {
58
+ message: ResilientError.ensure(
59
+ error instanceof Error ? error : `${error}`
60
+ ).marshal().message
61
+ })
62
+ }, [name])
63
+
64
+
65
+ if (formRef != null) {
66
+ formRef.current = { form, update, loader, error: setError }
67
+ }
68
+
69
+ const style: SxProps = useMemo(() => scalingToStyles(horizontal, vertical, theme), [horizontal])
70
+
71
+ const content = () =>
72
+ <Grid container direction="column" justifyContent="flex-start" alignItems="stretch"
73
+ rowSpacing={2} sx={!decorate ? style : {}}>{
74
+ Array.isArray(children)
75
+ ? children.map((child, index) =>
76
+ <Grid item key={index}>{child}</Grid>
77
+ ) : children
78
+ }</Grid>
79
+
80
+ if (decorate === true) {
81
+ const root = form.getFieldState('root')
82
+ return <FormProvider {...form}>
83
+ <FormContext {...props} loader={loader}>
84
+ <Card sx={{ ...style, display: 'flex', flexDirection: 'column', justifyContent: 'space-between', ...styles } as SxProps}>
85
+ <CardContent>
86
+ {content()}
87
+ {root.invalid && root.error?.message &&
88
+ <Status ok={false} i18n={i18n} error={ResilientError.ensure(root.error.message)} />}
89
+ </CardContent>
90
+ {onSubmit != null ? <CardActions sx={{ flexDirection: 'row', justifyContent: 'flex-end', pr: 2, pb: 2 }}>
91
+ <SubmitButton loader={loader} onSubmit={async data => onSubmit(data, update)} />
92
+ </CardActions> : undefined}
93
+ </Card>
94
+ </FormContext>
95
+ </FormProvider>
96
+ }
97
+
98
+ return <FormProvider {...form}>
99
+ <FormContext {...props} loader={loader}>{content()}</FormContext>
100
+ </FormProvider>
101
+ }
@@ -0,0 +1,3 @@
1
+
2
+ export * from './component.js'
3
+ export type * from './types.js'
@@ -0,0 +1,45 @@
1
+ import type { FC } from 'react'
2
+ import { useFormContext, Controller } from 'react-hook-form'
3
+ import { TextInputProps } from './types.js'
4
+
5
+ import TextField from '@mui/material/TextField'
6
+ import { useFormError, useFormI18n } from '@owlmeans/client-panel'
7
+ import { useClientFormContext } from '@owlmeans/client-panel'
8
+
9
+ export const TextInput: FC<TextInputProps> = ({ name, label, placeholder, hint, type, def, disableAutocomplete }) => {
10
+ const { control } = useFormContext()
11
+ const t = useFormI18n()
12
+ const key = name
13
+ if (typeof label === 'boolean' && label) {
14
+ label = t(`${key}.label`)
15
+ } else {
16
+ label = undefined
17
+ }
18
+ if (typeof placeholder === 'boolean' && placeholder) {
19
+ placeholder = t(`${key}.placeholder`)
20
+ } else {
21
+ placeholder = undefined
22
+ }
23
+ if (typeof hint === 'boolean' && hint) {
24
+ hint = t(`${key}.hint`)
25
+ } else {
26
+ hint = undefined
27
+ }
28
+
29
+ return <Controller control={control} name={name} defaultValue={def} render={
30
+ ({ field, fieldState }) => {
31
+ const error = useFormError(name, fieldState.error)
32
+ const { loader } = useClientFormContext()
33
+
34
+ return <TextField fullWidth {...field}
35
+ error={fieldState.error != null}
36
+ disabled={loader != null && loader.opened === true}
37
+ label={label}
38
+ type={type ?? 'text'}
39
+ placeholder={placeholder}
40
+ autoComplete={disableAutocomplete ? 'off' : 'on'}
41
+ helperText={error ?? hint}
42
+ />
43
+ }
44
+ } />
45
+ }
@@ -0,0 +1,3 @@
1
+
2
+ export * from './component.js'
3
+ export * from './types.js'
@@ -0,0 +1,11 @@
1
+ import type { FormFieldProps } from '@owlmeans/client-panel'
2
+ import type { HTMLInputTypeAttribute } from 'react'
3
+
4
+ export interface TextInputProps extends FormFieldProps {
5
+ name: string
6
+ label?: string | boolean
7
+ placeholder?: string | boolean
8
+ hint?: string | boolean
9
+ type?: HTMLInputTypeAttribute
10
+ disableAutocomplete?: boolean
11
+ }
@@ -0,0 +1,6 @@
1
+ import type { SxProps } from '@mui/material/styles'
2
+ import type { FormProps } from '@owlmeans/client-panel'
3
+
4
+ export interface WebFormProps extends FormProps {
5
+ styles?: SxProps
6
+ }
@@ -0,0 +1,79 @@
1
+ import type { SxProps, Theme } from '@mui/material/styles'
2
+ import useTheme from '@mui/material/styles/useTheme'
3
+ import useMediaQuery from '@mui/material/useMediaQuery'
4
+ import { BlockScaling } from '@owlmeans/client-panel'
5
+
6
+ export const scalingToStyles = (
7
+ horizontal?: BlockScaling,
8
+ vertical?: BlockScaling,
9
+ theme?: Theme
10
+ ): SxProps => {
11
+ const style: SxProps = {}
12
+ switch (horizontal) {
13
+ case BlockScaling.Half:
14
+ Object.assign(style, {
15
+ maxWidth: '50%',
16
+ [theme?.breakpoints.down('md') ?? 'xs']: {
17
+ maxWidth: '90%'
18
+ },
19
+ flexGrow: 1
20
+ })
21
+ break
22
+ case BlockScaling.Wide:
23
+ Object.assign(style, { mx: '10%', flexGrow: 1 })
24
+ break
25
+ case BlockScaling.Full:
26
+ Object.assign(style, { flexGrow: 1 })
27
+ break
28
+ }
29
+
30
+ switch (vertical) {
31
+ case BlockScaling.Half:
32
+ Object.assign(style, {
33
+ maxHeight: '50%',
34
+ [theme?.breakpoints.down('md') ?? 'xs']: {
35
+ maxHeight: '90%'
36
+ },
37
+ flexGrow: 1
38
+ })
39
+ break
40
+ case BlockScaling.Wide:
41
+ Object.assign(style, { my: '10%', flexGrow: 1 })
42
+ break
43
+ case BlockScaling.Full:
44
+ Object.assign(style, { height: '100%', flexGrow: 1 })
45
+ break
46
+ }
47
+
48
+ return style
49
+ }
50
+
51
+ export const useBreakPoint = () => {
52
+ const theme = useTheme()
53
+
54
+ // Define breakpoints
55
+ const isXs = useMediaQuery(theme.breakpoints.only('xs'))
56
+ const isSm = useMediaQuery(theme.breakpoints.only('sm'))
57
+ const isMd = useMediaQuery(theme.breakpoints.only('md'))
58
+ const isLg = useMediaQuery(theme.breakpoints.only('lg'))
59
+ const isXl = useMediaQuery(theme.breakpoints.only('xl'))
60
+
61
+ let currentBreakpoint = isXs ? 'xs' : 'unknown' // Default value
62
+ if (isSm) currentBreakpoint = 'sm'
63
+ if (isMd) currentBreakpoint = 'md'
64
+ if (isLg) currentBreakpoint = 'lg'
65
+ if (isXl) currentBreakpoint = 'xl'
66
+
67
+ return currentBreakpoint
68
+ }
69
+
70
+ export const useMapBreakpoint = <T>(map: Record<string, T>, def?: T, breakpoint?: string): T => {
71
+ const _breakpoint = useBreakPoint()
72
+ breakpoint = breakpoint ?? _breakpoint
73
+ const result = map[breakpoint] ?? def
74
+ if (result == null) {
75
+ throw new SyntaxError(`Breakpoint should always return value. We have ${breakpoint}, but ${Object.keys(map).join(', ')} are available`)
76
+ }
77
+
78
+ return result
79
+ }
@@ -0,0 +1,17 @@
1
+ export type * from './types.js'
2
+
3
+ export * from './panel-app/index.js'
4
+ export * from './layout/index.js'
5
+
6
+ export * from './form/index.js'
7
+ export * from './form/text/index.js'
8
+ export * from './form/button/index.js'
9
+ export * from './uploader/index.js'
10
+
11
+ export * from './button/index.js'
12
+
13
+ export * from './block.js'
14
+ export * from './text.js'
15
+ export * from './link.js'
16
+ export * from './helper.js'
17
+ export * from './status.js'
@@ -0,0 +1,7 @@
1
+ import type { FC } from 'react'
2
+ import type { LayoutProps } from './types.js'
3
+ import Box from '@mui/material/Box'
4
+
5
+ export const Layout: FC<LayoutProps> = ({ children }) => {
6
+ return <Box>{children}</Box>
7
+ }
@@ -0,0 +1,3 @@
1
+
2
+ export * from './types.js'
3
+ export * from './component.js'
@@ -0,0 +1,4 @@
1
+ import type { PropsWithChildren } from 'react'
2
+
3
+ export interface LayoutProps extends PropsWithChildren {
4
+ }
@@ -0,0 +1,34 @@
1
+ import { usePanelI18n } from '@owlmeans/client-panel'
2
+ import type { FC } from 'react'
3
+ import type { LinkProps } from './types.js'
4
+ import MUILink from '@mui/material/Link'
5
+ import type { Variant } from '@mui/material/styles/createTypography.js'
6
+ import { useValue } from '@owlmeans/client'
7
+ import { useContext } from '@owlmeans/web-client'
8
+ import type { ClientModule } from '@owlmeans/client-module'
9
+
10
+ export const Link: FC<LinkProps> = ({ src, module, name, variant, children, center, open, styles }) => {
11
+ const t = usePanelI18n()
12
+ const context = useContext()
13
+
14
+ const href = useValue(async () => {
15
+ if (src != null) {
16
+ return src
17
+ }
18
+ if (module != null) {
19
+ module = typeof module === 'string' ? context.module<ClientModule<string>>(module) : module
20
+ const [url] = await module.call<string>()
21
+
22
+ return url
23
+ }
24
+ return null
25
+ }, [src, module])
26
+
27
+ const label = name != null
28
+ ? t(name)
29
+ : children != null || module == null
30
+ ? undefined : t(`modules.${typeof module == 'string' ? module : module.alias}`)
31
+ const target = open ? '_blank' : undefined
32
+ return <MUILink href={href ?? undefined} target={target} variant={variant as Variant}
33
+ sx={{ textAlign: center ? 'center' : 'inherit', ...styles }}>{label ?? children}</MUILink>
34
+ }
@@ -0,0 +1,21 @@
1
+ import type { FC } from 'react'
2
+ import { useMemo } from 'react'
3
+ import type { PanelAppProps } from './types.js'
4
+
5
+ import { App } from '@owlmeans/client'
6
+ import { I18nContext } from '@owlmeans/client-i18n'
7
+ import CssBaseline from '@mui/material/CssBaseline'
8
+ import ThemeProvider from '@mui/material/styles/ThemeProvider.js'
9
+ import createTheme from '@mui/material/styles/createTheme.js'
10
+
11
+ export const PanelApp: FC<PanelAppProps> = ({ context, provide, children, theme }) => {
12
+ theme = useMemo(() => theme ?? createTheme(), [theme])
13
+ return <ThemeProvider theme={theme}>
14
+ <I18nContext config={context.cfg}>
15
+ <App context={context} provide={provide}>
16
+ <CssBaseline />
17
+ {children}
18
+ </App >
19
+ </I18nContext>
20
+ </ThemeProvider>
21
+ }
@@ -0,0 +1,3 @@
1
+
2
+ export * from './types.js'
3
+ export * from './component.js'
@@ -0,0 +1,6 @@
1
+ import type { Theme } from '@mui/material/styles'
2
+ import type { AppProps } from '@owlmeans/client'
3
+
4
+ export interface PanelAppProps extends AppProps {
5
+ theme?: Theme
6
+ }
@@ -0,0 +1,23 @@
1
+ import type { FC } from 'react'
2
+ import { useMemo } from 'react'
3
+ import type { StatusProps } from './types.js'
4
+ import { usePanelI18n } from '@owlmeans/client-panel'
5
+ import { ResilientError } from '@owlmeans/error'
6
+ import Alert from '@mui/material/Alert'
7
+
8
+ const prepareMessage = (msg: string) => msg.replace(/\:/g, '.')
9
+
10
+ export const Status: FC<StatusProps> = ({ ok, name, i18n, children, variant, message, error }) => {
11
+ variant = useMemo(() => variant ?? (ok ? 'success' : 'error'), [ok, variant])
12
+ const t = usePanelI18n(name ?? variant, i18n)
13
+ message = useMemo(() => {
14
+ const resilient = error != null ? ResilientError.ensure(error) : null
15
+ return message != null ? t(message) : resilient != null ? t(
16
+ [
17
+ `${resilient.type}.${prepareMessage(resilient.message)}`,
18
+ prepareMessage(resilient.message)
19
+ ],
20
+ ) : t(variant)
21
+ }, [message, error?.name, ok, variant])
22
+ return <Alert severity={variant as any}>{children ?? message}</Alert>
23
+ }
@@ -0,0 +1,14 @@
1
+
2
+ import { usePanelI18n } from '@owlmeans/client-panel'
3
+ import type { FC } from 'react'
4
+ import type { TextProps } from './types.js'
5
+ import Typography from '@mui/material/Typography'
6
+ import type { Variant } from '@mui/material/styles/createTypography.js'
7
+
8
+ export const Text: FC<TextProps> = ({ variant, name, children, center, styles, nested, i18n }) => {
9
+ const t = usePanelI18n(undefined, i18n)
10
+
11
+ const label = name != null ? t(name) : undefined
12
+ return <Typography component={nested ? 'span' : 'p'} variant={variant as Variant}
13
+ sx={{ textAlign: center ? 'center' : 'inherit', ...styles }}>{label ?? children}</Typography>
14
+ }
@@ -0,0 +1,35 @@
1
+ import type { SxProps } from '@mui/material/styles'
2
+ import type { I18nProps } from '@owlmeans/client-i18n'
3
+ import type { BlockScaling } from '@owlmeans/client-panel'
4
+ import type { FC, PropsWithChildren } from 'react'
5
+ import type { Variant } from '@mui/material/styles/createTypography'
6
+ import type { ClientModule } from '@owlmeans/client-module'
7
+
8
+ export interface BlockProps extends PropsWithChildren<I18nProps> {
9
+ horizontal?: BlockScaling
10
+ vertical?: BlockScaling
11
+ Actions?: FC
12
+ styles?: SxProps
13
+ }
14
+
15
+ export interface TextProps extends PropsWithChildren<I18nProps> {
16
+ name?: string
17
+ variant?: Variant
18
+ center?: boolean
19
+ styles?: SxProps
20
+ nested?: boolean
21
+ }
22
+
23
+ export interface LinkProps extends TextProps {
24
+ src?: string
25
+ module?: string | ClientModule
26
+ open?: boolean
27
+ }
28
+
29
+ export interface StatusProps extends PropsWithChildren<I18nProps> {
30
+ name?: string
31
+ ok?: boolean
32
+ variant?: string
33
+ error?: Error
34
+ message?: string
35
+ }