@omnsight/osint-entity-components 0.2.4 → 0.2.6

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 (60) hide show
  1. package/dist/index.js +8 -8
  2. package/dist/index.mjs +796 -781
  3. package/package.json +15 -2
  4. package/src/App.tsx +397 -141
  5. package/src/avatars/layouts/AvatarDropdown.tsx +1 -1
  6. package/src/avatars/layouts/AvatarSpan.tsx +7 -2
  7. package/src/forms/BaseForm.tsx +138 -0
  8. package/src/forms/EditableAttributes.tsx +131 -0
  9. package/src/forms/EventForm/EditableForm.tsx +78 -0
  10. package/src/forms/EventForm/EditingForm.tsx +401 -0
  11. package/src/forms/EventForm/IconFormSection.tsx +54 -0
  12. package/src/forms/EventForm/StaticForm.tsx +272 -0
  13. package/src/forms/EventForm/index.ts +1 -0
  14. package/src/forms/InsightForm/EditableForm.tsx +70 -0
  15. package/src/forms/InsightForm/EditingForm.tsx +79 -0
  16. package/src/forms/InsightForm/StaticForm.tsx +139 -0
  17. package/src/forms/InsightForm/index.ts +1 -0
  18. package/src/forms/MonitoringSourceForm/EditableForm.tsx +59 -0
  19. package/src/forms/MonitoringSourceForm/EditingForm.tsx +192 -0
  20. package/src/forms/MonitoringSourceForm/StaticForm.tsx +107 -0
  21. package/src/forms/MonitoringSourceForm/index.ts +1 -0
  22. package/src/forms/OrganizationForm/EditableForm.tsx +74 -0
  23. package/src/forms/OrganizationForm/EditingForm.tsx +177 -0
  24. package/src/forms/OrganizationForm/IconFormSection.tsx +60 -0
  25. package/src/forms/OrganizationForm/StaticForm.tsx +209 -0
  26. package/src/forms/OrganizationForm/index.ts +1 -0
  27. package/src/forms/PersonForm/EditableForm.tsx +74 -0
  28. package/src/forms/PersonForm/EditingForm.tsx +187 -0
  29. package/src/forms/PersonForm/IconFormSection.tsx +54 -0
  30. package/src/forms/PersonForm/StaticForm.tsx +202 -0
  31. package/src/forms/PersonForm/index.ts +1 -0
  32. package/src/forms/RelationForm/EditableForm.tsx +74 -0
  33. package/src/forms/RelationForm/EditingForm.tsx +147 -0
  34. package/src/forms/RelationForm/StaticForm.tsx +182 -0
  35. package/src/forms/RelationForm/index.ts +1 -0
  36. package/src/forms/SourceForm/EditableForm.tsx +74 -0
  37. package/src/forms/SourceForm/EditingForm.tsx +199 -0
  38. package/src/forms/SourceForm/IconFormSection.tsx +54 -0
  39. package/src/forms/SourceForm/StaticForm.tsx +209 -0
  40. package/src/forms/SourceForm/index.ts +1 -0
  41. package/src/forms/WebsiteForm/EditableForm.tsx +74 -0
  42. package/src/forms/WebsiteForm/EditingForm.tsx +216 -0
  43. package/src/forms/WebsiteForm/IconFormSection.tsx +54 -0
  44. package/src/forms/WebsiteForm/StaticForm.tsx +225 -0
  45. package/src/forms/WebsiteForm/index.ts +1 -0
  46. package/src/forms/accessLevel.ts +48 -0
  47. package/src/forms/index.ts +8 -0
  48. package/src/icons/Event/Select.tsx +7 -6
  49. package/src/icons/Organization/Select.tsx +7 -6
  50. package/src/icons/Person/Select.tsx +7 -6
  51. package/src/icons/Source/Select.tsx +7 -6
  52. package/src/icons/Website/Select.tsx +9 -8
  53. package/src/inputs/CountrySelect.tsx +45 -0
  54. package/src/inputs/CustomDatePicker.tsx +51 -0
  55. package/src/inputs/CustomDateTimePicker.tsx +51 -0
  56. package/src/inputs/RangeDatePicker.tsx +99 -0
  57. package/src/inputs/TimezoneSelect.tsx +20 -0
  58. package/src/locales/en.json +135 -0
  59. package/src/locales/zh.json +135 -0
  60. package/src/main.tsx +20 -4
@@ -0,0 +1,202 @@
1
+ import {
2
+ Stack,
3
+ Text,
4
+ Group,
5
+ Divider,
6
+ UnstyledButton,
7
+ Collapse,
8
+ Title,
9
+ rem,
10
+ Tooltip,
11
+ Box,
12
+ Select,
13
+ } from '@mantine/core';
14
+ import { useTranslation } from 'react-i18next';
15
+ import { type Person, type Permissive } from 'omni-osint-crud-client';
16
+ import { EditableAttributes } from '../EditableAttributes';
17
+ import { type CSSProperties, useState, type PropsWithChildren } from 'react';
18
+ import { ChevronDownIcon, UserIcon } from '@heroicons/react/24/outline';
19
+ import {
20
+ getAccessLevel,
21
+ getRoles,
22
+ useReadOptions,
23
+ useWriteOptions,
24
+ } from '../accessLevel';
25
+ import { Controller } from 'react-hook-form';
26
+ import { BaseForm } from '../BaseForm';
27
+ import { PersonIcon } from '@omnsight/osint-entity-components/icons';
28
+
29
+ interface Props extends PropsWithChildren {
30
+ person: Person;
31
+ isAdmin?: boolean;
32
+ onUpdate?: (data: Permissive) => void;
33
+ onClose?: () => void;
34
+ onDoubleClick: () => void;
35
+ exitButton?: React.ReactNode;
36
+ style?: CSSProperties;
37
+ editModeEnabled: boolean;
38
+ }
39
+
40
+ export const StaticForm: React.FC<Props> = ({
41
+ person,
42
+ isAdmin = false,
43
+ onUpdate,
44
+ onClose,
45
+ onDoubleClick,
46
+ exitButton,
47
+ children,
48
+ style,
49
+ editModeEnabled,
50
+ }) => {
51
+ const { t } = useTranslation();
52
+ const [attributesOpen, setAttributesOpen] = useState(false);
53
+
54
+ const handlClose = () => {
55
+ onClose?.();
56
+ };
57
+
58
+ const readOptions = useReadOptions(isAdmin);
59
+ const writeOptions = useWriteOptions();
60
+
61
+ return (
62
+ <BaseForm<Person>
63
+ style={style}
64
+ icon={<PersonIcon person={person} />}
65
+ title={person.name || t('components.forms.PersonForm.title')}
66
+ onClose={handlClose}
67
+ defaultValues={person}
68
+ onUpdate={onUpdate}
69
+ exitButton={exitButton}
70
+ onlyShowEditOnDirty={true}
71
+ >
72
+ {({ control, formState: { errors } }) => {
73
+ return (
74
+ <Stack
75
+ pos="relative"
76
+ gap="xs"
77
+ style={{ cursor: editModeEnabled ? 'pointer' : 'default' }}
78
+ onDoubleClick={onDoubleClick}
79
+ >
80
+ <Group gap={4}>
81
+ <Text size="sm" c="dimmed">
82
+ {t('placeholder.type')}:
83
+ </Text>
84
+ <Text size="sm">{person.type}</Text>
85
+ </Group>
86
+
87
+ <Group gap={4}>
88
+ <Text>{t('placeholder.role')}:</Text>
89
+ <Text>{person.role}</Text>
90
+ </Group>
91
+
92
+ <Group gap={4}>
93
+ <Text>{t('placeholder.nationality')}:</Text>
94
+ <Text>{person.nationality}</Text>
95
+ </Group>
96
+
97
+ <Group gap={4}>
98
+ <Text size="sm" c="dimmed">
99
+ {t('placeholder.birthDate')}:
100
+ </Text>
101
+ <Text size="sm">
102
+ {person.birth_date
103
+ ? new Date(person.birth_date * 1000).toLocaleDateString()
104
+ : t('placeholder.birthDate')}
105
+ </Text>
106
+ </Group>
107
+
108
+ <Text size="sm">{(person.tags || []).join(', ')}</Text>
109
+
110
+ {children}
111
+
112
+ {onUpdate && (
113
+ <Group gap="xs" w="100%">
114
+ <Box
115
+ style={{
116
+ flex: 1,
117
+ display: "flex",
118
+ alignItems: "center",
119
+ justifyContent: "center",
120
+ }}
121
+ >
122
+ <Tooltip label={person.owner?.toUpperCase()[0] || ""}>
123
+ <UserIcon style={{ width: rem(18), height: rem(18) }} />
124
+ </Tooltip>
125
+ </Box>
126
+ <Box
127
+ style={{
128
+ flex: 3,
129
+ display: "flex",
130
+ }}
131
+ >
132
+ {t("placeholder.accessLabel")}:
133
+ </Box>
134
+ <Controller
135
+ name="read"
136
+ control={control}
137
+ rules={{ required: t("common.required") }}
138
+ render={({ field }) => {
139
+ return (
140
+ <Box style={{ flex: 4 }}>
141
+ <Select
142
+ value={getAccessLevel(field.value ?? [])}
143
+ onChange={(value) => field.onChange(getRoles(value))}
144
+ placeholder={t("placeholder.readAccess")}
145
+ data={readOptions}
146
+ clearable
147
+ error={errors.read?.message}
148
+ />
149
+ </Box>
150
+ );
151
+ }}
152
+ />
153
+ <Controller
154
+ name="write"
155
+ control={control}
156
+ rules={{ required: t("common.required") }}
157
+ render={({ field }) => {
158
+ return (
159
+ <Box style={{ flex: 4 }}>
160
+ <Select
161
+ value={getAccessLevel(field.value ?? [])}
162
+ onChange={(value) => field.onChange(getRoles(value))}
163
+ placeholder={t("placeholder.writeAccess")}
164
+ data={writeOptions}
165
+ clearable
166
+ error={errors.write?.message}
167
+ />
168
+ </Box>
169
+ );
170
+ }}
171
+ />
172
+ </Group>
173
+ )}
174
+
175
+ <Divider my="sm" />
176
+
177
+ <UnstyledButton onClick={() => setAttributesOpen((o) => !o)}>
178
+ <Group justify="space-between">
179
+ <Title order={5}>{t('placeholder.attributes')}</Title>
180
+ <ChevronDownIcon
181
+ style={{
182
+ width: 16,
183
+ transform: attributesOpen ? 'rotate(180deg)' : 'rotate(0deg)',
184
+ transition: 'transform 200ms ease',
185
+ }}
186
+ />
187
+ </Group>
188
+ </UnstyledButton>
189
+
190
+ <Collapse in={attributesOpen}>
191
+ <EditableAttributes
192
+ value={person.attributes || {}}
193
+ isEditing={false}
194
+ onChange={() => {}}
195
+ />
196
+ </Collapse>
197
+ </Stack>
198
+ );
199
+ }}
200
+ </BaseForm>
201
+ );
202
+ };
@@ -0,0 +1 @@
1
+ export { PersonForm } from './EditableForm';
@@ -0,0 +1,74 @@
1
+ import { useState, type PropsWithChildren, type CSSProperties } from "react";
2
+ import type { Relation, Permissive } from "omni-osint-crud-client";
3
+ import { EditingForm } from "./EditingForm";
4
+ import { StaticForm } from "./StaticForm";
5
+
6
+ interface Props extends PropsWithChildren {
7
+ relation: Relation;
8
+ isAdmin?: boolean;
9
+ onSubmit?: (data: Relation) => void;
10
+ onUpdate?: (data: Partial<Relation>) => void;
11
+ onUpdatePermissive?: (data: Permissive) => void;
12
+ onClose?: () => void;
13
+ exitButton?: React.ReactNode;
14
+ style?: CSSProperties;
15
+ }
16
+
17
+ export const RelationForm: React.FC<Props> = ({
18
+ relation,
19
+ isAdmin = false,
20
+ onSubmit,
21
+ onUpdate,
22
+ onUpdatePermissive,
23
+ onClose,
24
+ exitButton,
25
+ children,
26
+ style,
27
+ }) => {
28
+ const [isEditing, setIsEditing] = useState(onSubmit !== undefined || false);
29
+
30
+ if (
31
+ onSubmit !== undefined &&
32
+ (onUpdate !== undefined || onUpdatePermissive !== undefined)
33
+ ) {
34
+ throw new Error(
35
+ "onSubmit cannot be defined at the same time with onUpdate or onUpdatePermissive",
36
+ );
37
+ }
38
+
39
+ const handlClose = () => {
40
+ if (onUpdate !== undefined) {
41
+ setIsEditing(false);
42
+ }
43
+ onClose?.();
44
+ };
45
+
46
+ const handleDoubleClick = () => {
47
+ if (onUpdate !== undefined) {
48
+ setIsEditing(true);
49
+ }
50
+ };
51
+
52
+ return isEditing ? (
53
+ <EditingForm
54
+ relation={relation}
55
+ onSubmit={onSubmit}
56
+ onUpdate={onUpdate}
57
+ onClose={handlClose}
58
+ style={style}
59
+ />
60
+ ) : (
61
+ <StaticForm
62
+ relation={relation}
63
+ isAdmin={isAdmin}
64
+ onUpdate={onUpdatePermissive}
65
+ onClose={handlClose}
66
+ onDoubleClick={handleDoubleClick}
67
+ exitButton={exitButton || <></>}
68
+ style={style}
69
+ editModeEnabled={onUpdate !== undefined}
70
+ >
71
+ {children}
72
+ </StaticForm>
73
+ );
74
+ };
@@ -0,0 +1,147 @@
1
+ import React, { useState, type CSSProperties } from 'react';
2
+ import {
3
+ Stack,
4
+ Group,
5
+ Text,
6
+ Divider,
7
+ UnstyledButton,
8
+ Collapse,
9
+ Title,
10
+ TextInput,
11
+ NumberInput,
12
+ } from '@mantine/core';
13
+ import { useTranslation } from 'react-i18next';
14
+ import { type Relation } from 'omni-osint-crud-client';
15
+ import { EditableAttributes } from '../EditableAttributes';
16
+ import { ChevronDownIcon } from '@heroicons/react/24/outline';
17
+ import { Controller } from 'react-hook-form';
18
+ import { BaseForm } from '../BaseForm';
19
+
20
+ interface Props {
21
+ relation: Relation;
22
+ onSubmit?: (data: Relation) => void;
23
+ onUpdate?: (data: Partial<Relation>) => void;
24
+ onClose?: () => void;
25
+ children?: React.ReactNode;
26
+ style?: CSSProperties;
27
+ }
28
+
29
+ export const EditingForm: React.FC<Props> = ({
30
+ relation,
31
+ onSubmit,
32
+ onUpdate,
33
+ onClose,
34
+ children,
35
+ style,
36
+ }) => {
37
+ const { t } = useTranslation();
38
+ const [attributesOpen, setAttributesOpen] = useState(false);
39
+
40
+ const handlClose = () => {
41
+ onClose?.();
42
+ };
43
+
44
+ return (
45
+ <BaseForm<Relation>
46
+ style={style}
47
+ title={t('components.forms.RelationForm.title')}
48
+ onClose={handlClose}
49
+ defaultValues={relation}
50
+ onSubmit={onSubmit}
51
+ onUpdate={onUpdate}
52
+ onlyShowEditOnDirty={false}
53
+ >
54
+ {({ control, formState: { errors } }) => (
55
+ <Stack
56
+ pos="relative"
57
+ gap="xs"
58
+ style={{ cursor: 'default' }}
59
+ >
60
+ <Group gap={4}>
61
+ <Text size="sm" fw={500}>
62
+ {t('placeholder.label')}:
63
+ </Text>
64
+ <Controller
65
+ name="label"
66
+ control={control}
67
+ rules={{ required: t('common.required') }}
68
+ render={({ field }) => (
69
+ <TextInput
70
+ {...field}
71
+ value={field.value || ''}
72
+ placeholder={t('placeholder.label')}
73
+ error={errors.label?.message}
74
+ />
75
+ )}
76
+ />
77
+ </Group>
78
+
79
+ <Group gap={4}>
80
+ <Text size="sm">{t('placeholder.name')}:</Text>
81
+ <Controller
82
+ name="name"
83
+ control={control}
84
+ rules={{
85
+ required: t('common.required'),
86
+ pattern: {
87
+ value: /^[a-zA-Z_-]+$/,
88
+ message: t('components.forms.RelationForm.namePattern'),
89
+ },
90
+ }}
91
+ render={({ field }) => (
92
+ <TextInput
93
+ {...field}
94
+ value={field.value || ''}
95
+ placeholder={t('placeholder.name')}
96
+ error={errors.name?.message}
97
+ />
98
+ )}
99
+ />
100
+ </Group>
101
+
102
+ <Group gap={4}>
103
+ <Text size="sm">{t('placeholder.confidence')}:</Text>
104
+ <Controller
105
+ name="confidence"
106
+ control={control}
107
+ render={({ field }) => (
108
+ <NumberInput
109
+ {...field}
110
+ value={field.value ?? 0}
111
+ placeholder={t('placeholder.confidence')}
112
+ />
113
+ )}
114
+ />
115
+ </Group>
116
+
117
+ {children}
118
+
119
+ <Divider my="sm" />
120
+
121
+ <UnstyledButton onClick={() => setAttributesOpen((o) => !o)}>
122
+ <Group justify="space-between">
123
+ <Title order={5}>{t('placeholder.attributes')}</Title>
124
+ <ChevronDownIcon
125
+ style={{
126
+ width: 16,
127
+ transform: attributesOpen ? 'rotate(180deg)' : 'rotate(0deg)',
128
+ transition: 'transform 200ms ease',
129
+ }}
130
+ />
131
+ </Group>
132
+ </UnstyledButton>
133
+
134
+ <Collapse in={attributesOpen}>
135
+ <Controller
136
+ name="attributes"
137
+ control={control}
138
+ render={({ field }) => (
139
+ <EditableAttributes {...field} value={field.value || {}} isEditing={true} />
140
+ )}
141
+ />
142
+ </Collapse>
143
+ </Stack>
144
+ )}
145
+ </BaseForm>
146
+ );
147
+ };
@@ -0,0 +1,182 @@
1
+ import React, {
2
+ useState,
3
+ type CSSProperties,
4
+ type PropsWithChildren,
5
+ } from "react";
6
+ import {
7
+ Stack,
8
+ Group,
9
+ Text,
10
+ Divider,
11
+ UnstyledButton,
12
+ Collapse,
13
+ Title,
14
+ rem,
15
+ Tooltip,
16
+ Box,
17
+ Select,
18
+ } from "@mantine/core";
19
+ import { useTranslation } from "react-i18next";
20
+ import { type Relation, type Permissive } from "omni-osint-crud-client";
21
+ import { EditableAttributes } from "../EditableAttributes";
22
+ import { ChevronDownIcon, UserIcon } from "@heroicons/react/24/outline";
23
+ import {
24
+ getAccessLevel,
25
+ getRoles,
26
+ useReadOptions,
27
+ useWriteOptions,
28
+ } from "../accessLevel";
29
+ import { Controller } from "react-hook-form";
30
+ import { BaseForm } from "../BaseForm";
31
+
32
+ interface Props extends PropsWithChildren {
33
+ relation: Relation;
34
+ isAdmin?: boolean;
35
+ onUpdate?: (data: Permissive) => void;
36
+ onClose?: () => void;
37
+ onDoubleClick: () => void;
38
+ exitButton?: React.ReactNode;
39
+ style?: CSSProperties;
40
+ editModeEnabled: boolean;
41
+ }
42
+
43
+ export const StaticForm: React.FC<Props> = ({
44
+ relation,
45
+ isAdmin = false,
46
+ onUpdate,
47
+ onClose,
48
+ onDoubleClick,
49
+ exitButton,
50
+ children,
51
+ style,
52
+ editModeEnabled,
53
+ }) => {
54
+ const { t } = useTranslation();
55
+ const [attributesOpen, setAttributesOpen] = useState(false);
56
+
57
+ const handlClose = () => {
58
+ onClose?.();
59
+ };
60
+
61
+ const readOptions = useReadOptions(isAdmin);
62
+ const writeOptions = useWriteOptions();
63
+
64
+ return (
65
+ <BaseForm<Relation>
66
+ style={style}
67
+ title={relation.label || t("components.forms.RelationForm.title")}
68
+ onClose={handlClose}
69
+ defaultValues={relation}
70
+ onUpdate={onUpdate}
71
+ exitButton={exitButton}
72
+ onlyShowEditOnDirty={true}
73
+ >
74
+ {({ control, formState: { errors } }) => (
75
+ <Stack
76
+ pos="relative"
77
+ gap="xs"
78
+ style={{ cursor: editModeEnabled ? "pointer" : "default" }}
79
+ onDoubleClick={onDoubleClick}
80
+ >
81
+ <Group gap={4}>
82
+ <Text size="sm">{t("placeholder.name")}:</Text>
83
+ <Text size="sm">{relation.name}</Text>
84
+ </Group>
85
+
86
+ <Group gap={4}>
87
+ <Text size="sm">{t("placeholder.confidence")}:</Text>
88
+ <Text size="sm">{relation.confidence}</Text>
89
+ </Group>
90
+
91
+ {children}
92
+
93
+ {onUpdate && (
94
+ <Group gap="xs" w="100%">
95
+ <Box
96
+ style={{
97
+ flex: 1,
98
+ display: "flex",
99
+ alignItems: "center",
100
+ justifyContent: "center",
101
+ }}
102
+ >
103
+ <Tooltip label={relation.owner?.toUpperCase()[0] || ""}>
104
+ <UserIcon style={{ width: rem(18), height: rem(18) }} />
105
+ </Tooltip>
106
+ </Box>
107
+ <Box
108
+ style={{
109
+ flex: 3,
110
+ display: "flex",
111
+ }}
112
+ >
113
+ {t("placeholder.accessLabel")}:
114
+ </Box>
115
+ <Controller
116
+ name="read"
117
+ control={control}
118
+ rules={{ required: t("common.required") }}
119
+ render={({ field }) => {
120
+ return (
121
+ <Box style={{ flex: 4 }}>
122
+ <Select
123
+ value={getAccessLevel(field.value ?? [])}
124
+ onChange={(value) => field.onChange(getRoles(value))}
125
+ placeholder={t("placeholder.readAccess")}
126
+ data={readOptions}
127
+ clearable
128
+ error={errors.read?.message}
129
+ />
130
+ </Box>
131
+ );
132
+ }}
133
+ />
134
+ <Controller
135
+ name="write"
136
+ control={control}
137
+ rules={{ required: t("common.required") }}
138
+ render={({ field }) => {
139
+ return (
140
+ <Box style={{ flex: 4 }}>
141
+ <Select
142
+ value={getAccessLevel(field.value ?? [])}
143
+ onChange={(value) => field.onChange(getRoles(value))}
144
+ placeholder={t("placeholder.writeAccess")}
145
+ data={writeOptions}
146
+ clearable
147
+ error={errors.write?.message}
148
+ />
149
+ </Box>
150
+ );
151
+ }}
152
+ />
153
+ </Group>
154
+ )}
155
+
156
+ <Divider my="sm" />
157
+
158
+ <UnstyledButton onClick={() => setAttributesOpen((o) => !o)}>
159
+ <Group justify="space-between">
160
+ <Title order={5}>{t("placeholder.attributes")}</Title>
161
+ <ChevronDownIcon
162
+ style={{
163
+ width: 16,
164
+ transform: attributesOpen ? "rotate(180deg)" : "rotate(0deg)",
165
+ transition: "transform 200ms ease",
166
+ }}
167
+ />
168
+ </Group>
169
+ </UnstyledButton>
170
+
171
+ <Collapse in={attributesOpen}>
172
+ <EditableAttributes
173
+ value={relation.attributes || {}}
174
+ isEditing={false}
175
+ onChange={() => {}}
176
+ />
177
+ </Collapse>
178
+ </Stack>
179
+ )}
180
+ </BaseForm>
181
+ );
182
+ };
@@ -0,0 +1 @@
1
+ export { RelationForm } from './EditableForm';
@@ -0,0 +1,74 @@
1
+ import { useState, type PropsWithChildren, type CSSProperties } from "react";
2
+ import type { Source, Permissive } from "omni-osint-crud-client";
3
+ import { EditingForm } from "./EditingForm";
4
+ import { StaticForm } from "./StaticForm";
5
+
6
+ interface Props extends PropsWithChildren {
7
+ source: Source;
8
+ isAdmin?: boolean;
9
+ onSubmit?: (data: Source) => void;
10
+ onUpdate?: (data: Partial<Source>) => void;
11
+ onUpdatePermissive?: (data: Permissive) => void;
12
+ onClose?: () => void;
13
+ exitButton?: React.ReactNode;
14
+ style?: CSSProperties;
15
+ }
16
+
17
+ export const SourceForm: React.FC<Props> = ({
18
+ source,
19
+ isAdmin = false,
20
+ onSubmit,
21
+ onUpdate,
22
+ onUpdatePermissive,
23
+ onClose,
24
+ exitButton,
25
+ children,
26
+ style,
27
+ }) => {
28
+ const [isEditing, setIsEditing] = useState(onSubmit !== undefined);
29
+
30
+ if (
31
+ onSubmit !== undefined &&
32
+ (onUpdate !== undefined || onUpdatePermissive !== undefined)
33
+ ) {
34
+ throw new Error(
35
+ "onSubmit cannot be defined at the same time with onUpdate or onUpdatePermissive",
36
+ );
37
+ }
38
+
39
+ const handlClose = () => {
40
+ if (onUpdate !== undefined) {
41
+ setIsEditing(false);
42
+ }
43
+ onClose?.();
44
+ };
45
+
46
+ const handleDoubleClick = () => {
47
+ if (onUpdate !== undefined) {
48
+ setIsEditing(true);
49
+ }
50
+ };
51
+
52
+ return isEditing ? (
53
+ <EditingForm
54
+ source={source}
55
+ onSubmit={onSubmit}
56
+ onUpdate={onUpdate}
57
+ onClose={handlClose}
58
+ style={style}
59
+ />
60
+ ) : (
61
+ <StaticForm
62
+ source={source}
63
+ isAdmin={isAdmin}
64
+ onUpdate={onUpdatePermissive}
65
+ onClose={handlClose}
66
+ onDoubleClick={handleDoubleClick}
67
+ exitButton={exitButton || <></>}
68
+ style={style}
69
+ editModeEnabled={onUpdate !== undefined}
70
+ >
71
+ {children}
72
+ </StaticForm>
73
+ );
74
+ };