@dxos/react-ui-form 0.7.1-staging.599df14
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/LICENSE +8 -0
- package/README.md +4 -0
- package/dist/lib/browser/index.mjs +1008 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/node/index.cjs +1028 -0
- package/dist/lib/node/index.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -0
- package/dist/lib/node-esm/index.mjs +1010 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +16 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +9 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -0
- package/dist/types/src/components/FieldEditor/index.d.ts +2 -0
- package/dist/types/src/components/FieldEditor/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/Custom.d.ts +4 -0
- package/dist/types/src/components/Form/Custom.d.ts.map +1 -0
- package/dist/types/src/components/Form/Defaults.d.ts +7 -0
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -0
- package/dist/types/src/components/Form/Form.d.ts +26 -0
- package/dist/types/src/components/Form/Form.d.ts.map +1 -0
- package/dist/types/src/components/Form/Form.stories.d.ts +38 -0
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/Input.d.ts +29 -0
- package/dist/types/src/components/Form/Input.d.ts.map +1 -0
- package/dist/types/src/components/Form/factory.d.ts +8 -0
- package/dist/types/src/components/Form/factory.d.ts.map +1 -0
- package/dist/types/src/components/Form/index.d.ts +5 -0
- package/dist/types/src/components/Form/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts +10 -0
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +18 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +8 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/index.d.ts +2 -0
- package/dist/types/src/components/ViewEditor/index.d.ts.map +1 -0
- package/dist/types/src/components/deprecated/Form.d.ts +15 -0
- package/dist/types/src/components/deprecated/Form.d.ts.map +1 -0
- package/dist/types/src/components/deprecated/Form.stories.d.ts +13 -0
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -0
- package/dist/types/src/components/deprecated/FormInput.d.ts +11 -0
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -0
- package/dist/types/src/components/deprecated/index.d.ts +2 -0
- package/dist/types/src/components/deprecated/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +5 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/components/testing.d.ts +7 -0
- package/dist/types/src/components/testing.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useForm.d.ts +58 -0
- package/dist/types/src/hooks/useForm.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +6 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +46 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/util/field.d.ts +22 -0
- package/dist/types/src/util/field.d.ts.map +1 -0
- package/dist/types/src/util/formatting.d.ts +15 -0
- package/dist/types/src/util/formatting.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +3 -0
- package/dist/types/src/util/index.d.ts.map +1 -0
- package/package.json +76 -0
- package/src/components/FieldEditor/FieldEditor.stories.tsx +56 -0
- package/src/components/FieldEditor/FieldEditor.tsx +167 -0
- package/src/components/FieldEditor/index.ts +5 -0
- package/src/components/Form/Custom.tsx +65 -0
- package/src/components/Form/Defaults.tsx +130 -0
- package/src/components/Form/Form.stories.tsx +137 -0
- package/src/components/Form/Form.tsx +176 -0
- package/src/components/Form/Input.tsx +56 -0
- package/src/components/Form/factory.tsx +32 -0
- package/src/components/Form/index.ts +8 -0
- package/src/components/ViewEditor/Test.stories.tsx +45 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +73 -0
- package/src/components/ViewEditor/ViewEditor.tsx +158 -0
- package/src/components/ViewEditor/index.ts +5 -0
- package/src/components/deprecated/Form.stories.tsx +62 -0
- package/src/components/deprecated/Form.tsx +119 -0
- package/src/components/deprecated/FormInput.tsx +58 -0
- package/src/components/deprecated/index.ts +5 -0
- package/src/components/index.ts +9 -0
- package/src/components/testing.tsx +24 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useForm.ts +229 -0
- package/src/index.ts +11 -0
- package/src/translations.ts +59 -0
- package/src/util/field.ts +137 -0
- package/src/util/formatting.ts +132 -0
- package/src/util/index.ts +6 -0
|
@@ -0,0 +1,1008 @@
|
|
|
1
|
+
// packages/ui/react-ui-form/src/translations.ts
|
|
2
|
+
var translationKey = "react-ui-form";
|
|
3
|
+
var translations_default = [
|
|
4
|
+
{
|
|
5
|
+
"en-US": {
|
|
6
|
+
[translationKey]: {
|
|
7
|
+
"fields label": "Fields",
|
|
8
|
+
// TODO(burdon): Standardize field/property.
|
|
9
|
+
"button add property": "Add property",
|
|
10
|
+
"field property label": "Property",
|
|
11
|
+
"field property placeholder": "Property name",
|
|
12
|
+
"field format label": "Type",
|
|
13
|
+
"field path label": "Field path",
|
|
14
|
+
"add field": "Add field",
|
|
15
|
+
"delete field": "Delete field",
|
|
16
|
+
"field limit reached": "Maximum number of fields reached",
|
|
17
|
+
// TODO(burdon): Factor out?
|
|
18
|
+
"button cancel": "Cancel",
|
|
19
|
+
"button save": "Save",
|
|
20
|
+
// FormatEnum
|
|
21
|
+
"format boolean": "Boolean",
|
|
22
|
+
"format currency": "Currency",
|
|
23
|
+
"format date": "Date",
|
|
24
|
+
"format date-time": "DateTime",
|
|
25
|
+
"format did": "DID",
|
|
26
|
+
"format duration": "Duration",
|
|
27
|
+
"format email": "Email",
|
|
28
|
+
"format formula": "Formula",
|
|
29
|
+
"format hostname": "Hostname",
|
|
30
|
+
"format integer": "Integer",
|
|
31
|
+
"format json": "JSON",
|
|
32
|
+
"format latlng": "LatLng",
|
|
33
|
+
"format markdown": "Markdown",
|
|
34
|
+
"format percent": "Percent",
|
|
35
|
+
"format ref": "Reference",
|
|
36
|
+
"format regex": "RegExp",
|
|
37
|
+
"format string": "String",
|
|
38
|
+
"format text": "Long text",
|
|
39
|
+
"format time": "Time",
|
|
40
|
+
"format timestamp": "Timestamp",
|
|
41
|
+
"format user": "User",
|
|
42
|
+
"format number": "Number",
|
|
43
|
+
"format uri": "URI",
|
|
44
|
+
"format uuid": "UUID",
|
|
45
|
+
// Placeholders
|
|
46
|
+
"placeholder latitude": "Latitude",
|
|
47
|
+
"placeholder longitude": "Longitude"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// packages/ui/react-ui-form/src/components/deprecated/FormInput.tsx
|
|
54
|
+
import React from "react";
|
|
55
|
+
import { Input } from "@dxos/react-ui";
|
|
56
|
+
var DeprecatedFormInput = ({ label, description, secondary, children }) => {
|
|
57
|
+
const primary = /* @__PURE__ */ React.createElement("div", {
|
|
58
|
+
role: "none",
|
|
59
|
+
className: "flex w-full gap-4 py-1"
|
|
60
|
+
}, /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement("div", {
|
|
61
|
+
role: "none",
|
|
62
|
+
className: "flex flex-col w-full"
|
|
63
|
+
}, /* @__PURE__ */ React.createElement(Input.Label, {
|
|
64
|
+
classNames: "flex min-h-[40px] items-center"
|
|
65
|
+
}, label), description && /* @__PURE__ */ React.createElement(Input.DescriptionAndValidation, {
|
|
66
|
+
classNames: "mbs-0.5"
|
|
67
|
+
}, /* @__PURE__ */ React.createElement(Input.Description, null, description))), /* @__PURE__ */ React.createElement("div", {
|
|
68
|
+
role: "none"
|
|
69
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
70
|
+
role: "none",
|
|
71
|
+
className: "flex min-h-[40px] items-center"
|
|
72
|
+
}, children))));
|
|
73
|
+
if (secondary) {
|
|
74
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
75
|
+
role: "none",
|
|
76
|
+
className: "flex flex-col w-full"
|
|
77
|
+
}, primary, secondary);
|
|
78
|
+
}
|
|
79
|
+
return primary;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
|
|
83
|
+
import React6, { useCallback as useCallback2, useEffect as useEffect3, useState as useState2 } from "react";
|
|
84
|
+
import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
|
|
85
|
+
import { log as log3 } from "@dxos/log";
|
|
86
|
+
import { useTranslation as useTranslation3 } from "@dxos/react-ui";
|
|
87
|
+
import { getFormatSchema, getSchemaProperties as getSchemaProperties2, sortProperties } from "@dxos/schema";
|
|
88
|
+
|
|
89
|
+
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
90
|
+
import React5, { useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
91
|
+
import { AST, S } from "@dxos/echo-schema";
|
|
92
|
+
import { findNode, getDiscriminatedType, isDiscriminatedUnion } from "@dxos/effect";
|
|
93
|
+
import { log as log2 } from "@dxos/log";
|
|
94
|
+
import { IconButton, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
95
|
+
import { mx } from "@dxos/react-ui-theme";
|
|
96
|
+
import { getSchemaProperties } from "@dxos/schema";
|
|
97
|
+
import { isNotFalsy } from "@dxos/util";
|
|
98
|
+
|
|
99
|
+
// packages/ui/react-ui-form/src/components/Form/factory.tsx
|
|
100
|
+
import { FormatEnum } from "@dxos/echo-schema";
|
|
101
|
+
|
|
102
|
+
// packages/ui/react-ui-form/src/components/Form/Custom.tsx
|
|
103
|
+
import React3 from "react";
|
|
104
|
+
import { Input as Input2, useTranslation } from "@dxos/react-ui";
|
|
105
|
+
|
|
106
|
+
// packages/ui/react-ui-form/src/components/Form/Input.tsx
|
|
107
|
+
import React2 from "react";
|
|
108
|
+
import { Icon, Tooltip } from "@dxos/react-ui";
|
|
109
|
+
import { errorText } from "@dxos/react-ui-theme";
|
|
110
|
+
var InputHeader = ({ children, error }) => {
|
|
111
|
+
return /* @__PURE__ */ React2.createElement("div", {
|
|
112
|
+
role: "none",
|
|
113
|
+
className: "flex justify-between items-center mb-1"
|
|
114
|
+
}, children, error && /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
|
|
115
|
+
asChild: true
|
|
116
|
+
}, /* @__PURE__ */ React2.createElement(Icon, {
|
|
117
|
+
icon: "ph--warning--regular",
|
|
118
|
+
size: 4,
|
|
119
|
+
classNames: errorText
|
|
120
|
+
})), /* @__PURE__ */ React2.createElement(Tooltip.Content, {
|
|
121
|
+
side: "bottom"
|
|
122
|
+
}, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null), error)));
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// packages/ui/react-ui-form/src/components/Form/Custom.tsx
|
|
126
|
+
var LatLngInput = ({ property, type, label, disabled, getStatus, getValue, onValueChange, onBlur }) => {
|
|
127
|
+
const { t } = useTranslation(translationKey);
|
|
128
|
+
const { status, error } = getStatus?.(property);
|
|
129
|
+
const { lat = 0, lng = 0 } = getValue(property) ?? {};
|
|
130
|
+
return /* @__PURE__ */ React3.createElement(Input2.Root, {
|
|
131
|
+
validationValence: status
|
|
132
|
+
}, /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
133
|
+
error
|
|
134
|
+
}, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement("div", {
|
|
135
|
+
className: "grid grid-cols-2 gap-2"
|
|
136
|
+
}, /* @__PURE__ */ React3.createElement(Input2.TextInput, {
|
|
137
|
+
type: "number",
|
|
138
|
+
disabled,
|
|
139
|
+
placeholder: t("placeholder latitude"),
|
|
140
|
+
value: lat,
|
|
141
|
+
onChange: (event) => onValueChange(property, type, {
|
|
142
|
+
lat: safeParseFloat(event.target.value, 0),
|
|
143
|
+
lng
|
|
144
|
+
}),
|
|
145
|
+
onBlur
|
|
146
|
+
}), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
|
|
147
|
+
type: "number",
|
|
148
|
+
disabled,
|
|
149
|
+
placeholder: t("placeholder longitude"),
|
|
150
|
+
value: lng,
|
|
151
|
+
onChange: (event) => onValueChange(property, type, {
|
|
152
|
+
lat,
|
|
153
|
+
lng: safeParseFloat(event.target.value, 0)
|
|
154
|
+
}),
|
|
155
|
+
onBlur
|
|
156
|
+
})));
|
|
157
|
+
};
|
|
158
|
+
var safeParseFloat = (str, defaultValue) => {
|
|
159
|
+
try {
|
|
160
|
+
return parseFloat(str);
|
|
161
|
+
} catch {
|
|
162
|
+
return defaultValue ?? 0;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// packages/ui/react-ui-form/src/components/Form/Defaults.tsx
|
|
167
|
+
import React4 from "react";
|
|
168
|
+
import { Input as Input3, Select } from "@dxos/react-ui";
|
|
169
|
+
var TextInput = ({ property, type, label, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
|
|
170
|
+
const { status, error } = getStatus?.(property);
|
|
171
|
+
return /* @__PURE__ */ React4.createElement(Input3.Root, {
|
|
172
|
+
validationValence: status
|
|
173
|
+
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
174
|
+
error
|
|
175
|
+
}, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement(Input3.TextInput, {
|
|
176
|
+
disabled,
|
|
177
|
+
placeholder,
|
|
178
|
+
value: getValue(property) ?? "",
|
|
179
|
+
onChange: (event) => onValueChange(property, type, event.target.value),
|
|
180
|
+
onBlur
|
|
181
|
+
}));
|
|
182
|
+
};
|
|
183
|
+
var NumberInput = ({ property, type, label, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
|
|
184
|
+
const { status, error } = getStatus?.(property);
|
|
185
|
+
return /* @__PURE__ */ React4.createElement(Input3.Root, {
|
|
186
|
+
validationValence: status
|
|
187
|
+
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
188
|
+
error
|
|
189
|
+
}, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement(Input3.TextInput, {
|
|
190
|
+
type: "number",
|
|
191
|
+
disabled,
|
|
192
|
+
placeholder,
|
|
193
|
+
value: getValue(property) ?? 0,
|
|
194
|
+
onChange: (event) => onValueChange(property, type, event.target.value),
|
|
195
|
+
onBlur
|
|
196
|
+
}));
|
|
197
|
+
};
|
|
198
|
+
var BooleanInput = ({ property, type, label, getStatus, getValue, onValueChange }) => {
|
|
199
|
+
const { status, error } = getStatus?.(property);
|
|
200
|
+
return /* @__PURE__ */ React4.createElement(Input3.Root, {
|
|
201
|
+
validationValence: status
|
|
202
|
+
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
203
|
+
error
|
|
204
|
+
}, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement(Input3.Switch, {
|
|
205
|
+
checked: getValue(property),
|
|
206
|
+
onCheckedChange: (value) => onValueChange(property, type, value)
|
|
207
|
+
}));
|
|
208
|
+
};
|
|
209
|
+
var SelectInput = ({ property, type, label, disabled, placeholder, options, getStatus, getValue, onValueChange }) => {
|
|
210
|
+
const { status, error } = getStatus?.(property);
|
|
211
|
+
return /* @__PURE__ */ React4.createElement(Input3.Root, {
|
|
212
|
+
validationValence: status
|
|
213
|
+
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
214
|
+
error
|
|
215
|
+
}, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement(Select.Root, {
|
|
216
|
+
value: getValue(property),
|
|
217
|
+
onValueChange: (value) => onValueChange(property, type, value)
|
|
218
|
+
}, /* @__PURE__ */ React4.createElement(Select.TriggerButton, {
|
|
219
|
+
classNames: "is-full",
|
|
220
|
+
disabled,
|
|
221
|
+
placeholder
|
|
222
|
+
}), /* @__PURE__ */ React4.createElement(Select.Portal, null, /* @__PURE__ */ React4.createElement(Select.Content, null, /* @__PURE__ */ React4.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
|
|
223
|
+
// NOTE: Numeric values are converted to and from strings.
|
|
224
|
+
/* @__PURE__ */ React4.createElement(Select.Option, {
|
|
225
|
+
key: String(value),
|
|
226
|
+
value: String(value)
|
|
227
|
+
}, label2 ?? String(value))
|
|
228
|
+
)))))));
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// packages/ui/react-ui-form/src/components/Form/factory.tsx
|
|
232
|
+
var getInputComponent = (type, format) => {
|
|
233
|
+
switch (format) {
|
|
234
|
+
case FormatEnum.LatLng:
|
|
235
|
+
return LatLngInput;
|
|
236
|
+
}
|
|
237
|
+
switch (type) {
|
|
238
|
+
case "string":
|
|
239
|
+
return TextInput;
|
|
240
|
+
case "number":
|
|
241
|
+
return NumberInput;
|
|
242
|
+
case "boolean":
|
|
243
|
+
return BooleanInput;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
248
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
249
|
+
import { invariant } from "@dxos/invariant";
|
|
250
|
+
import { log } from "@dxos/log";
|
|
251
|
+
import { validateSchema } from "@dxos/schema";
|
|
252
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
|
|
253
|
+
var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSubmit }) => {
|
|
254
|
+
const [values, setValues] = useState(initialValues);
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
setValues(initialValues);
|
|
257
|
+
}, [
|
|
258
|
+
initialValues
|
|
259
|
+
]);
|
|
260
|
+
const [touched, setTouched] = useState(createKeySet(initialValues, false));
|
|
261
|
+
const [changed, setChanged] = useState(createKeySet(initialValues, false));
|
|
262
|
+
const [errors, setErrors] = useState({});
|
|
263
|
+
const validate = useCallback((values2) => {
|
|
264
|
+
let errors2 = validateSchema(schema, values2) ?? [];
|
|
265
|
+
if (errors2.length === 0 && onValidate) {
|
|
266
|
+
errors2 = onValidate(values2) ?? [];
|
|
267
|
+
}
|
|
268
|
+
setErrors(flatMap(errors2));
|
|
269
|
+
return errors2.length === 0;
|
|
270
|
+
}, [
|
|
271
|
+
schema,
|
|
272
|
+
onValidate
|
|
273
|
+
]);
|
|
274
|
+
const [schemaDirty, setSchemaDirty] = useState(false);
|
|
275
|
+
useEffect(() => setSchemaDirty(true), [
|
|
276
|
+
schema
|
|
277
|
+
]);
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
if (schemaDirty) {
|
|
280
|
+
validate(values);
|
|
281
|
+
setSchemaDirty(false);
|
|
282
|
+
}
|
|
283
|
+
}, [
|
|
284
|
+
values,
|
|
285
|
+
schemaDirty
|
|
286
|
+
]);
|
|
287
|
+
const canSubmit = useMemo(() => Object.keys(values).every((property) => touched[property] === false || !errors[property]), [
|
|
288
|
+
values,
|
|
289
|
+
touched,
|
|
290
|
+
errors
|
|
291
|
+
]);
|
|
292
|
+
const handleSubmit = useCallback(() => {
|
|
293
|
+
if (validate(values)) {
|
|
294
|
+
onSubmit?.(values, {
|
|
295
|
+
changed
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}, [
|
|
299
|
+
values,
|
|
300
|
+
validate,
|
|
301
|
+
onSubmit
|
|
302
|
+
]);
|
|
303
|
+
const getStatus = useCallback((property) => ({
|
|
304
|
+
status: errors[property] ? "error" : void 0,
|
|
305
|
+
error: errors[property] ? errors[property] : void 0
|
|
306
|
+
}), [
|
|
307
|
+
touched,
|
|
308
|
+
errors
|
|
309
|
+
]);
|
|
310
|
+
const getValue = (property) => {
|
|
311
|
+
return values[property];
|
|
312
|
+
};
|
|
313
|
+
const onValueChange = (property, type, value) => {
|
|
314
|
+
let parsedValue = value;
|
|
315
|
+
try {
|
|
316
|
+
if (type === "number") {
|
|
317
|
+
parsedValue = parseFloat(value) || 0;
|
|
318
|
+
}
|
|
319
|
+
} catch (err) {
|
|
320
|
+
log.catch(err, void 0, {
|
|
321
|
+
F: __dxlog_file,
|
|
322
|
+
L: 164,
|
|
323
|
+
S: void 0,
|
|
324
|
+
C: (f, a) => f(...a)
|
|
325
|
+
});
|
|
326
|
+
parsedValue = void 0;
|
|
327
|
+
}
|
|
328
|
+
const newValues = {
|
|
329
|
+
...values,
|
|
330
|
+
[property]: parsedValue
|
|
331
|
+
};
|
|
332
|
+
setValues(newValues);
|
|
333
|
+
setChanged((prev) => ({
|
|
334
|
+
...prev,
|
|
335
|
+
[property]: true
|
|
336
|
+
}));
|
|
337
|
+
onValuesChanged?.(newValues);
|
|
338
|
+
const isValid = validate(newValues);
|
|
339
|
+
if (isValid && onValid) {
|
|
340
|
+
onValid(newValues, {
|
|
341
|
+
changed
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
const onBlur = useCallback((event) => {
|
|
346
|
+
const { name } = event.target;
|
|
347
|
+
setTouched((touched2) => ({
|
|
348
|
+
...touched2,
|
|
349
|
+
[name]: true
|
|
350
|
+
}));
|
|
351
|
+
if (event.relatedTarget?.getAttribute("type") === "submit") {
|
|
352
|
+
setTouched(createKeySet(values, true));
|
|
353
|
+
}
|
|
354
|
+
validate(values);
|
|
355
|
+
}, [
|
|
356
|
+
validate,
|
|
357
|
+
values
|
|
358
|
+
]);
|
|
359
|
+
return {
|
|
360
|
+
// State.
|
|
361
|
+
values,
|
|
362
|
+
errors,
|
|
363
|
+
touched,
|
|
364
|
+
changed,
|
|
365
|
+
canSubmit,
|
|
366
|
+
handleSubmit,
|
|
367
|
+
// Field utils.
|
|
368
|
+
getStatus,
|
|
369
|
+
getValue,
|
|
370
|
+
onValueChange,
|
|
371
|
+
onBlur
|
|
372
|
+
};
|
|
373
|
+
};
|
|
374
|
+
var createKeySet = (obj, value) => {
|
|
375
|
+
invariant(obj, void 0, {
|
|
376
|
+
F: __dxlog_file,
|
|
377
|
+
L: 215,
|
|
378
|
+
S: void 0,
|
|
379
|
+
A: [
|
|
380
|
+
"obj",
|
|
381
|
+
""
|
|
382
|
+
]
|
|
383
|
+
});
|
|
384
|
+
return Object.keys(obj).reduce((acc, key) => ({
|
|
385
|
+
...acc,
|
|
386
|
+
[key]: value
|
|
387
|
+
}), {});
|
|
388
|
+
};
|
|
389
|
+
var flatMap = (errors) => {
|
|
390
|
+
return errors.reduce((result, { path, message }) => {
|
|
391
|
+
if (!(path in result)) {
|
|
392
|
+
result[path] = message;
|
|
393
|
+
}
|
|
394
|
+
return result;
|
|
395
|
+
}, {});
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
399
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/Form.tsx";
|
|
400
|
+
var padding = "px-2";
|
|
401
|
+
var Form = ({ classNames, schema, values: initialValues, path = [], readonly, filter, sort, autoSave, onValuesChanged, onValidate, onSubmit, onCancel, Custom }) => {
|
|
402
|
+
const { t } = useTranslation2(translationKey);
|
|
403
|
+
const onValid = useMemo2(() => autoSave ? onSubmit : void 0, [
|
|
404
|
+
autoSave,
|
|
405
|
+
onSubmit
|
|
406
|
+
]);
|
|
407
|
+
const { canSubmit, values, errors, handleSubmit, ...inputProps } = useForm({
|
|
408
|
+
schema,
|
|
409
|
+
initialValues,
|
|
410
|
+
onValuesChanged,
|
|
411
|
+
onValidate,
|
|
412
|
+
onValid,
|
|
413
|
+
onSubmit
|
|
414
|
+
});
|
|
415
|
+
const props = useMemo2(() => {
|
|
416
|
+
const props2 = getSchemaProperties(schema.ast, values);
|
|
417
|
+
const filtered = filter ? filter(props2) : props2;
|
|
418
|
+
const findIndex = (props3, prop) => {
|
|
419
|
+
const idx = props3.findIndex((p) => p === prop);
|
|
420
|
+
return idx === -1 ? Infinity : idx;
|
|
421
|
+
};
|
|
422
|
+
return sort ? filtered.sort(({ name: a }, { name: b }) => findIndex(sort, a) - findIndex(sort, b)) : filtered;
|
|
423
|
+
}, [
|
|
424
|
+
schema,
|
|
425
|
+
filter,
|
|
426
|
+
values
|
|
427
|
+
]);
|
|
428
|
+
useEffect2(() => {
|
|
429
|
+
if (errors && Object.keys(errors).length) {
|
|
430
|
+
log2.warn("validation", {
|
|
431
|
+
errors
|
|
432
|
+
}, {
|
|
433
|
+
F: __dxlog_file2,
|
|
434
|
+
L: 93,
|
|
435
|
+
S: void 0,
|
|
436
|
+
C: (f, a) => f(...a)
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}, [
|
|
440
|
+
errors
|
|
441
|
+
]);
|
|
442
|
+
return /* @__PURE__ */ React5.createElement("div", {
|
|
443
|
+
role: "form",
|
|
444
|
+
className: mx("flex flex-col w-full gap-2 py-2", classNames)
|
|
445
|
+
}, props.map(({ prop, name, type, format, title, description, examples }) => {
|
|
446
|
+
const key = [
|
|
447
|
+
...path,
|
|
448
|
+
name
|
|
449
|
+
];
|
|
450
|
+
const InputComponent = Custom?.[key.join(".")] ?? getInputComponent(type, format);
|
|
451
|
+
if (!InputComponent) {
|
|
452
|
+
if (type === "object") {
|
|
453
|
+
const baseNode = findNode(prop.type, isDiscriminatedUnion);
|
|
454
|
+
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(prop.type, AST.isTypeLiteral);
|
|
455
|
+
if (typeLiteral) {
|
|
456
|
+
const schema2 = S.make(typeLiteral);
|
|
457
|
+
return /* @__PURE__ */ React5.createElement("div", {
|
|
458
|
+
key: name,
|
|
459
|
+
role: "none"
|
|
460
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
461
|
+
className: padding
|
|
462
|
+
}, title ?? name), /* @__PURE__ */ React5.createElement(Form, {
|
|
463
|
+
schema: schema2,
|
|
464
|
+
path: key,
|
|
465
|
+
values: values[name] ?? {},
|
|
466
|
+
onValuesChanged: (childValues) => {
|
|
467
|
+
inputProps.onValueChange(name, "object", childValues);
|
|
468
|
+
},
|
|
469
|
+
Custom
|
|
470
|
+
}));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
log2("no renderer for property", {
|
|
474
|
+
name,
|
|
475
|
+
type
|
|
476
|
+
}, {
|
|
477
|
+
F: __dxlog_file2,
|
|
478
|
+
L: 132,
|
|
479
|
+
S: void 0,
|
|
480
|
+
C: (f, a) => f(...a)
|
|
481
|
+
});
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
const placeholder = examples?.length ? `Example: ${examples[0]}` : description;
|
|
485
|
+
return /* @__PURE__ */ React5.createElement("div", {
|
|
486
|
+
key: name,
|
|
487
|
+
role: "none",
|
|
488
|
+
className: padding
|
|
489
|
+
}, /* @__PURE__ */ React5.createElement(InputComponent, {
|
|
490
|
+
type,
|
|
491
|
+
format,
|
|
492
|
+
property: name,
|
|
493
|
+
disabled: readonly,
|
|
494
|
+
label: title ?? name,
|
|
495
|
+
placeholder,
|
|
496
|
+
...inputProps
|
|
497
|
+
}));
|
|
498
|
+
}).filter(isNotFalsy), (onCancel || onSubmit) && !autoSave && /* @__PURE__ */ React5.createElement("div", {
|
|
499
|
+
role: "none",
|
|
500
|
+
className: "flex justify-center"
|
|
501
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
502
|
+
role: "none",
|
|
503
|
+
className: mx(onCancel && !readonly && "grid grid-cols-2 gap-2")
|
|
504
|
+
}, onCancel && !readonly && /* @__PURE__ */ React5.createElement(IconButton, {
|
|
505
|
+
icon: "ph--x--regular",
|
|
506
|
+
label: t("button cancel"),
|
|
507
|
+
onClick: onCancel
|
|
508
|
+
}), onSubmit && /* @__PURE__ */ React5.createElement(IconButton, {
|
|
509
|
+
type: "submit",
|
|
510
|
+
disabled: !canSubmit,
|
|
511
|
+
icon: "ph--check--regular",
|
|
512
|
+
label: t("button save"),
|
|
513
|
+
onClick: handleSubmit
|
|
514
|
+
}))));
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
|
|
518
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
|
|
519
|
+
var FieldEditor = ({ view, projection, field, registry, onClose, onCancel }) => {
|
|
520
|
+
const { t } = useTranslation3(translationKey);
|
|
521
|
+
const [props, setProps] = useState2(projection.getFieldProjection(field.id).props);
|
|
522
|
+
useEffect3(() => setProps(projection.getFieldProjection(field.id).props), [
|
|
523
|
+
field,
|
|
524
|
+
projection
|
|
525
|
+
]);
|
|
526
|
+
const [schemas, setSchemas] = useState2([]);
|
|
527
|
+
useEffect3(() => {
|
|
528
|
+
if (!registry) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const subscription = registry.subscribe(setSchemas);
|
|
532
|
+
const t2 = setTimeout(async () => {
|
|
533
|
+
const schemas2 = await registry.query();
|
|
534
|
+
setSchemas(schemas2);
|
|
535
|
+
});
|
|
536
|
+
return () => {
|
|
537
|
+
clearTimeout(t2);
|
|
538
|
+
subscription?.();
|
|
539
|
+
};
|
|
540
|
+
}, [
|
|
541
|
+
registry
|
|
542
|
+
]);
|
|
543
|
+
const [schema, setSchema] = useState2();
|
|
544
|
+
useEffect3(() => {
|
|
545
|
+
setSchema(schemas.find((schema2) => schema2.typename === props?.referenceSchema));
|
|
546
|
+
}, [
|
|
547
|
+
schemas,
|
|
548
|
+
props?.referenceSchema
|
|
549
|
+
]);
|
|
550
|
+
const [{ fieldSchema }, setFieldSchema] = useState2({
|
|
551
|
+
fieldSchema: getFormatSchema(props?.format)
|
|
552
|
+
});
|
|
553
|
+
const handleValuesChanged = useCallback2((_props) => {
|
|
554
|
+
if (_props.format !== props.format) {
|
|
555
|
+
setFieldSchema({
|
|
556
|
+
fieldSchema: getFormatSchema(_props.format)
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (_props.referenceSchema !== props.referenceSchema) {
|
|
560
|
+
setSchema(schemas.find((schema2) => schema2.typename === _props.referenceSchema));
|
|
561
|
+
}
|
|
562
|
+
setProps((props2) => {
|
|
563
|
+
const type = formatToType[_props.format];
|
|
564
|
+
if (props2.type !== type) {
|
|
565
|
+
return {
|
|
566
|
+
...props2,
|
|
567
|
+
..._props,
|
|
568
|
+
type
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
return props2;
|
|
572
|
+
});
|
|
573
|
+
}, [
|
|
574
|
+
schemas,
|
|
575
|
+
props
|
|
576
|
+
]);
|
|
577
|
+
const handleValidate = useCallback2(({ property }) => {
|
|
578
|
+
if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
|
|
579
|
+
return [
|
|
580
|
+
{
|
|
581
|
+
path: "property",
|
|
582
|
+
message: `property is not unique: '${property}'`
|
|
583
|
+
}
|
|
584
|
+
];
|
|
585
|
+
}
|
|
586
|
+
}, [
|
|
587
|
+
view.fields,
|
|
588
|
+
field
|
|
589
|
+
]);
|
|
590
|
+
const handleSubmit = useCallback2((props2) => {
|
|
591
|
+
projection.setFieldProjection({
|
|
592
|
+
field,
|
|
593
|
+
props: props2
|
|
594
|
+
});
|
|
595
|
+
onClose();
|
|
596
|
+
}, [
|
|
597
|
+
projection,
|
|
598
|
+
field,
|
|
599
|
+
onClose
|
|
600
|
+
]);
|
|
601
|
+
const handleCancel = useCallback2(() => {
|
|
602
|
+
requestAnimationFrame(() => onCancel?.());
|
|
603
|
+
onClose();
|
|
604
|
+
}, [
|
|
605
|
+
onClose
|
|
606
|
+
]);
|
|
607
|
+
if (!fieldSchema) {
|
|
608
|
+
log3.warn("invalid format", {
|
|
609
|
+
props
|
|
610
|
+
}, {
|
|
611
|
+
F: __dxlog_file3,
|
|
612
|
+
L: 118,
|
|
613
|
+
S: void 0,
|
|
614
|
+
C: (f, a) => f(...a)
|
|
615
|
+
});
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
return /* @__PURE__ */ React6.createElement(Form, {
|
|
619
|
+
key: field.id,
|
|
620
|
+
autoFocus: true,
|
|
621
|
+
values: props,
|
|
622
|
+
schema: fieldSchema,
|
|
623
|
+
filter: (props2) => props2.filter((p) => p.name !== "type"),
|
|
624
|
+
sort: [
|
|
625
|
+
"property",
|
|
626
|
+
"format"
|
|
627
|
+
],
|
|
628
|
+
onValuesChanged: handleValuesChanged,
|
|
629
|
+
onValidate: handleValidate,
|
|
630
|
+
onSubmit: handleSubmit,
|
|
631
|
+
onCancel: handleCancel,
|
|
632
|
+
Custom: {
|
|
633
|
+
["format"]: (props2) => /* @__PURE__ */ React6.createElement(SelectInput, {
|
|
634
|
+
...props2,
|
|
635
|
+
options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
|
|
636
|
+
value,
|
|
637
|
+
label: t(`format ${value}`)
|
|
638
|
+
}))
|
|
639
|
+
}),
|
|
640
|
+
["referenceSchema"]: (props2) => /* @__PURE__ */ React6.createElement(SelectInput, {
|
|
641
|
+
...props2,
|
|
642
|
+
options: schemas.map((schema2) => ({
|
|
643
|
+
value: schema2.typename
|
|
644
|
+
}))
|
|
645
|
+
}),
|
|
646
|
+
["referencePath"]: (props2) => /* @__PURE__ */ React6.createElement(SelectInput, {
|
|
647
|
+
...props2,
|
|
648
|
+
options: schema ? getSchemaProperties2(schema.schema.ast).sort(sortProperties).map((p) => ({
|
|
649
|
+
value: p.name
|
|
650
|
+
})) : []
|
|
651
|
+
})
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
// packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
|
|
657
|
+
import React7, { useCallback as useCallback3, useMemo as useMemo3, useState as useState3 } from "react";
|
|
658
|
+
import { AST as AST2, Format, S as S2 } from "@dxos/echo-schema";
|
|
659
|
+
import { IconButton as IconButton2, useTranslation as useTranslation4 } from "@dxos/react-ui";
|
|
660
|
+
import { List } from "@dxos/react-ui-list";
|
|
661
|
+
import { ghostHover, inputTextLabel, mx as mx2 } from "@dxos/react-ui-theme";
|
|
662
|
+
import { FieldSchema, ViewProjection, VIEW_FIELD_LIMIT } from "@dxos/schema";
|
|
663
|
+
import { arrayMove } from "@dxos/util";
|
|
664
|
+
var grid = "grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]";
|
|
665
|
+
var ViewMetaSchema = S2.Struct({
|
|
666
|
+
name: S2.String.annotations({
|
|
667
|
+
[AST2.TitleAnnotationId]: "View"
|
|
668
|
+
}),
|
|
669
|
+
type: Format.DXN.annotations({
|
|
670
|
+
[AST2.TitleAnnotationId]: "Typename"
|
|
671
|
+
})
|
|
672
|
+
}).pipe(S2.mutable);
|
|
673
|
+
var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onDelete }) => {
|
|
674
|
+
const { t } = useTranslation4(translationKey);
|
|
675
|
+
const projection = useMemo3(() => new ViewProjection(schema, view), [
|
|
676
|
+
schema,
|
|
677
|
+
view
|
|
678
|
+
]);
|
|
679
|
+
const [field, setField] = useState3();
|
|
680
|
+
const viewValues = useMemo3(() => {
|
|
681
|
+
return {
|
|
682
|
+
name: view.name,
|
|
683
|
+
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
684
|
+
// TODO(burdon): Settings should have domain name owned by user.
|
|
685
|
+
type: view.query.type
|
|
686
|
+
};
|
|
687
|
+
}, [
|
|
688
|
+
view
|
|
689
|
+
]);
|
|
690
|
+
const handleViewUpdate = useCallback3(({ name, type }) => {
|
|
691
|
+
requestAnimationFrame(() => {
|
|
692
|
+
view.name = name;
|
|
693
|
+
view.query.type = type;
|
|
694
|
+
schema.updateTypename(type);
|
|
695
|
+
});
|
|
696
|
+
}, [
|
|
697
|
+
view,
|
|
698
|
+
schema
|
|
699
|
+
]);
|
|
700
|
+
const handleSelect = useCallback3((field2) => {
|
|
701
|
+
setField((f) => f === field2 ? void 0 : field2);
|
|
702
|
+
}, []);
|
|
703
|
+
const handleAdd = useCallback3(() => {
|
|
704
|
+
const field2 = projection.createFieldProjection();
|
|
705
|
+
setField(field2);
|
|
706
|
+
}, [
|
|
707
|
+
view
|
|
708
|
+
]);
|
|
709
|
+
const handleMove = useCallback3((fromIndex, toIndex) => {
|
|
710
|
+
arrayMove(view.fields, fromIndex, toIndex);
|
|
711
|
+
}, [
|
|
712
|
+
view.fields
|
|
713
|
+
]);
|
|
714
|
+
const handleClose = useCallback3(() => setField(void 0), []);
|
|
715
|
+
return /* @__PURE__ */ React7.createElement("div", {
|
|
716
|
+
role: "none",
|
|
717
|
+
className: mx2("flex flex-col w-full divide-y divide-separator", classNames)
|
|
718
|
+
}, /* @__PURE__ */ React7.createElement(Form, {
|
|
719
|
+
autoFocus: true,
|
|
720
|
+
autoSave: true,
|
|
721
|
+
schema: ViewMetaSchema,
|
|
722
|
+
values: viewValues,
|
|
723
|
+
onSubmit: handleViewUpdate
|
|
724
|
+
}), /* @__PURE__ */ React7.createElement("div", null, /* @__PURE__ */ React7.createElement("div", {
|
|
725
|
+
role: "none",
|
|
726
|
+
className: "p-2"
|
|
727
|
+
}, /* @__PURE__ */ React7.createElement("label", {
|
|
728
|
+
className: mx2(inputTextLabel)
|
|
729
|
+
}, t("fields label"))), /* @__PURE__ */ React7.createElement(List.Root, {
|
|
730
|
+
items: view.fields,
|
|
731
|
+
isItem: S2.is(FieldSchema),
|
|
732
|
+
getId: (field2) => field2.id,
|
|
733
|
+
onMove: handleMove
|
|
734
|
+
}, ({ items: fields }) => /* @__PURE__ */ React7.createElement(React7.Fragment, null, showHeading && /* @__PURE__ */ React7.createElement("div", {
|
|
735
|
+
role: "heading",
|
|
736
|
+
className: grid
|
|
737
|
+
}, /* @__PURE__ */ React7.createElement("div", null), /* @__PURE__ */ React7.createElement("div", {
|
|
738
|
+
className: "flex items-center text-sm"
|
|
739
|
+
}, t("field path label"))), /* @__PURE__ */ React7.createElement("div", {
|
|
740
|
+
role: "list",
|
|
741
|
+
className: "flex flex-col w-full"
|
|
742
|
+
}, fields?.map((field2) => /* @__PURE__ */ React7.createElement(List.Item, {
|
|
743
|
+
key: field2.id,
|
|
744
|
+
item: field2,
|
|
745
|
+
classNames: mx2(grid, ghostHover, "cursor-pointer")
|
|
746
|
+
}, /* @__PURE__ */ React7.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React7.createElement(List.ItemTitle, {
|
|
747
|
+
onClick: () => handleSelect(field2)
|
|
748
|
+
}, field2.path), /* @__PURE__ */ React7.createElement(List.ItemDeleteButton, {
|
|
749
|
+
disabled: view.fields.length <= 1,
|
|
750
|
+
onClick: () => onDelete(field2.id)
|
|
751
|
+
}))))))), field && /* @__PURE__ */ React7.createElement(FieldEditor, {
|
|
752
|
+
key: field.id,
|
|
753
|
+
view,
|
|
754
|
+
projection,
|
|
755
|
+
field,
|
|
756
|
+
registry,
|
|
757
|
+
onClose: handleClose
|
|
758
|
+
}), !readonly && !field && /* @__PURE__ */ React7.createElement("div", {
|
|
759
|
+
className: "flex p-2 justify-center"
|
|
760
|
+
}, /* @__PURE__ */ React7.createElement(IconButton2, {
|
|
761
|
+
icon: "ph--plus--regular",
|
|
762
|
+
label: t("button add property"),
|
|
763
|
+
onClick: handleAdd,
|
|
764
|
+
// TODO(burdon): Show field limit in ux (not tooltip).
|
|
765
|
+
disabled: view.fields.length >= VIEW_FIELD_LIMIT
|
|
766
|
+
})));
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
// packages/ui/react-ui-form/src/util/field.ts
|
|
770
|
+
import { FormatEnum as FormatEnum3, TypeEnum } from "@dxos/echo-schema";
|
|
771
|
+
var parseValue = ({ type, format, value }) => {
|
|
772
|
+
if (value === void 0 || value === null || value === "") {
|
|
773
|
+
return void 0;
|
|
774
|
+
}
|
|
775
|
+
const parseScalar = (type2) => {
|
|
776
|
+
switch (type2) {
|
|
777
|
+
case TypeEnum.Boolean: {
|
|
778
|
+
if (typeof value === "string") {
|
|
779
|
+
const lowercaseValue = value.toLowerCase();
|
|
780
|
+
if (lowercaseValue === "0" || lowercaseValue === "false") {
|
|
781
|
+
return false;
|
|
782
|
+
} else if (lowercaseValue === "1" || lowercaseValue === "true") {
|
|
783
|
+
return true;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return Boolean(value);
|
|
787
|
+
}
|
|
788
|
+
case TypeEnum.Number: {
|
|
789
|
+
const cleaned = typeof value === "string" ? value.replace(/[^0-9.-]+/g, "") : value;
|
|
790
|
+
const num = Number(cleaned);
|
|
791
|
+
return Number.isNaN(num) ? null : num;
|
|
792
|
+
}
|
|
793
|
+
case TypeEnum.String:
|
|
794
|
+
case TypeEnum.Ref:
|
|
795
|
+
return String(value);
|
|
796
|
+
default: {
|
|
797
|
+
return value;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
if (!format) {
|
|
802
|
+
return parseScalar(type);
|
|
803
|
+
}
|
|
804
|
+
switch (format) {
|
|
805
|
+
case FormatEnum3.Boolean:
|
|
806
|
+
return parseScalar(TypeEnum.Boolean);
|
|
807
|
+
case FormatEnum3.Number:
|
|
808
|
+
case FormatEnum3.Percent:
|
|
809
|
+
case FormatEnum3.Currency:
|
|
810
|
+
return parseScalar(TypeEnum.Number);
|
|
811
|
+
case FormatEnum3.String:
|
|
812
|
+
case FormatEnum3.Markdown: {
|
|
813
|
+
return parseScalar(TypeEnum.String);
|
|
814
|
+
}
|
|
815
|
+
case FormatEnum3.Ref:
|
|
816
|
+
throw new Error(`unexpected format: ${FormatEnum3.Ref}`);
|
|
817
|
+
case FormatEnum3.DateTime:
|
|
818
|
+
case FormatEnum3.Date:
|
|
819
|
+
case FormatEnum3.Time:
|
|
820
|
+
case FormatEnum3.Timestamp: {
|
|
821
|
+
const date = new Date(value);
|
|
822
|
+
return isNaN(date.getTime()) ? null : date;
|
|
823
|
+
}
|
|
824
|
+
default: {
|
|
825
|
+
return value;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
var cellClassesForFieldType = ({ type, format }) => {
|
|
830
|
+
if (!format) {
|
|
831
|
+
switch (type) {
|
|
832
|
+
case TypeEnum.Number:
|
|
833
|
+
return [
|
|
834
|
+
"text-right",
|
|
835
|
+
"font-mono"
|
|
836
|
+
];
|
|
837
|
+
case TypeEnum.Boolean:
|
|
838
|
+
return [
|
|
839
|
+
"text-right",
|
|
840
|
+
"font-mono"
|
|
841
|
+
];
|
|
842
|
+
case TypeEnum.String:
|
|
843
|
+
return void 0;
|
|
844
|
+
case TypeEnum.Ref:
|
|
845
|
+
return void 0;
|
|
846
|
+
default: {
|
|
847
|
+
return void 0;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
switch (format) {
|
|
852
|
+
case FormatEnum3.Markdown:
|
|
853
|
+
return void 0;
|
|
854
|
+
case FormatEnum3.Timestamp:
|
|
855
|
+
case FormatEnum3.DateTime:
|
|
856
|
+
case FormatEnum3.Date:
|
|
857
|
+
case FormatEnum3.Time:
|
|
858
|
+
return [
|
|
859
|
+
"font-mono"
|
|
860
|
+
];
|
|
861
|
+
case FormatEnum3.Currency:
|
|
862
|
+
return [
|
|
863
|
+
"text-right"
|
|
864
|
+
];
|
|
865
|
+
case FormatEnum3.JSON:
|
|
866
|
+
return [
|
|
867
|
+
"font-mono"
|
|
868
|
+
];
|
|
869
|
+
default:
|
|
870
|
+
return void 0;
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
var cellClassesForRowSelection = (selected) => selected ? [
|
|
874
|
+
"!bg-gridCellSelected"
|
|
875
|
+
] : void 0;
|
|
876
|
+
var pathNotUniqueError = (path) => ({
|
|
877
|
+
path: "path",
|
|
878
|
+
message: `'${path}' is already present`
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
// packages/ui/react-ui-form/src/util/formatting.ts
|
|
882
|
+
import { FormatEnum as FormatEnum4, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
|
|
883
|
+
var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
|
|
884
|
+
const formatScalar = (type2) => {
|
|
885
|
+
switch (type2) {
|
|
886
|
+
case TypeEnum2.Boolean:
|
|
887
|
+
return value.toLocaleString().toUpperCase();
|
|
888
|
+
case TypeEnum2.Number:
|
|
889
|
+
return value.toLocaleString(locale);
|
|
890
|
+
case TypeEnum2.String:
|
|
891
|
+
case TypeEnum2.Ref:
|
|
892
|
+
return String(value);
|
|
893
|
+
default: {
|
|
894
|
+
return String(value);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
if (!format) {
|
|
899
|
+
return formatScalar(type);
|
|
900
|
+
}
|
|
901
|
+
switch (format) {
|
|
902
|
+
case FormatEnum4.Boolean: {
|
|
903
|
+
return formatScalar(TypeEnum2.Boolean);
|
|
904
|
+
}
|
|
905
|
+
case FormatEnum4.Number: {
|
|
906
|
+
return formatScalar(TypeEnum2.Number);
|
|
907
|
+
}
|
|
908
|
+
case FormatEnum4.String:
|
|
909
|
+
case FormatEnum4.Ref: {
|
|
910
|
+
return formatScalar(TypeEnum2.String);
|
|
911
|
+
}
|
|
912
|
+
case FormatEnum4.Percent: {
|
|
913
|
+
return `${value * 100}%`;
|
|
914
|
+
}
|
|
915
|
+
case FormatEnum4.Currency: {
|
|
916
|
+
return value.toLocaleString(locale, {
|
|
917
|
+
style: "currency",
|
|
918
|
+
currency: "USD",
|
|
919
|
+
minimumFractionDigits: 2,
|
|
920
|
+
maximumFractionDigits: 2
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
case FormatEnum4.DateTime:
|
|
924
|
+
case FormatEnum4.Date:
|
|
925
|
+
case FormatEnum4.Time: {
|
|
926
|
+
const date = new Date(value);
|
|
927
|
+
if (format === FormatEnum4.DateTime) {
|
|
928
|
+
return date.toLocaleString(locale);
|
|
929
|
+
} else if (format === FormatEnum4.Date) {
|
|
930
|
+
return date.toLocaleDateString(locale);
|
|
931
|
+
} else {
|
|
932
|
+
return date.toLocaleTimeString(locale);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
default: {
|
|
936
|
+
if (value === null || value === "undefined") {
|
|
937
|
+
return "";
|
|
938
|
+
}
|
|
939
|
+
return String(value);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
var formatForEditing = ({ type, format, value, locale = void 0 }) => {
|
|
944
|
+
const formatScalar = (type2) => {
|
|
945
|
+
switch (type2) {
|
|
946
|
+
case TypeEnum2.Boolean:
|
|
947
|
+
case TypeEnum2.Number:
|
|
948
|
+
case TypeEnum2.String:
|
|
949
|
+
case TypeEnum2.Ref:
|
|
950
|
+
return String(value);
|
|
951
|
+
default:
|
|
952
|
+
return String(value);
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
if (!format) {
|
|
956
|
+
return formatScalar(type);
|
|
957
|
+
}
|
|
958
|
+
switch (format) {
|
|
959
|
+
case FormatEnum4.Boolean:
|
|
960
|
+
case FormatEnum4.Number:
|
|
961
|
+
case FormatEnum4.String:
|
|
962
|
+
case FormatEnum4.Ref: {
|
|
963
|
+
return formatScalar(type);
|
|
964
|
+
}
|
|
965
|
+
case FormatEnum4.Percent: {
|
|
966
|
+
return String(value * 100);
|
|
967
|
+
}
|
|
968
|
+
case FormatEnum4.Currency: {
|
|
969
|
+
return String(value);
|
|
970
|
+
}
|
|
971
|
+
case FormatEnum4.DateTime: {
|
|
972
|
+
const date = new Date(value);
|
|
973
|
+
return date.toISOString();
|
|
974
|
+
}
|
|
975
|
+
case FormatEnum4.Date: {
|
|
976
|
+
const date = new Date(value);
|
|
977
|
+
return date.toISOString().split("T")[0];
|
|
978
|
+
}
|
|
979
|
+
case FormatEnum4.Time: {
|
|
980
|
+
const date = new Date(value);
|
|
981
|
+
return date.toISOString().split("T")[1].split(".")[0];
|
|
982
|
+
}
|
|
983
|
+
default: {
|
|
984
|
+
return String(value);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
export {
|
|
989
|
+
BooleanInput,
|
|
990
|
+
DeprecatedFormInput,
|
|
991
|
+
FieldEditor,
|
|
992
|
+
Form,
|
|
993
|
+
InputHeader,
|
|
994
|
+
LatLngInput,
|
|
995
|
+
NumberInput,
|
|
996
|
+
SelectInput,
|
|
997
|
+
TextInput,
|
|
998
|
+
ViewEditor,
|
|
999
|
+
cellClassesForFieldType,
|
|
1000
|
+
cellClassesForRowSelection,
|
|
1001
|
+
formatForDisplay,
|
|
1002
|
+
formatForEditing,
|
|
1003
|
+
parseValue,
|
|
1004
|
+
pathNotUniqueError,
|
|
1005
|
+
translations_default as translations,
|
|
1006
|
+
useForm
|
|
1007
|
+
};
|
|
1008
|
+
//# sourceMappingURL=index.mjs.map
|