@uipath/apollo-wind 2.49.0 → 2.51.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.
- package/dist/components/forms/field-renderer.cjs +80 -6
- package/dist/components/forms/field-renderer.js +80 -6
- package/dist/components/forms/form-designer.cjs +10 -10
- package/dist/components/forms/form-designer.js +10 -10
- package/dist/components/forms/form-schema.d.ts +38 -2
- package/dist/components/forms/index.cjs +15 -5
- package/dist/components/forms/index.d.ts +7 -6
- package/dist/components/forms/index.js +6 -5
- package/dist/components/forms/metadata-form.cjs +97 -37
- package/dist/components/forms/metadata-form.d.ts +11 -3
- package/dist/components/forms/metadata-form.js +94 -37
- package/dist/components/forms/rules-engine.cjs +14 -3
- package/dist/components/forms/rules-engine.d.ts +16 -0
- package/dist/components/forms/rules-engine.js +14 -3
- package/dist/components/forms/schema-serializer.cjs +10 -0
- package/dist/components/forms/schema-serializer.js +10 -0
- package/dist/components/forms/string-list-field.cjs +154 -0
- package/dist/components/forms/string-list-field.d.ts +29 -0
- package/dist/components/forms/string-list-field.js +117 -0
- package/dist/components/forms/validation-converter.cjs +68 -23
- package/dist/components/forms/validation-converter.d.ts +23 -2
- package/dist/components/forms/validation-converter.js +64 -22
- package/dist/components/ui/datetime-picker.cjs +3 -1
- package/dist/components/ui/datetime-picker.d.ts +4 -0
- package/dist/components/ui/datetime-picker.js +3 -1
- package/dist/components/ui/form-field.cjs +17 -2
- package/dist/components/ui/form-field.d.ts +7 -0
- package/dist/components/ui/form-field.js +17 -2
- package/dist/components/ui/index.cjs +117 -107
- package/dist/components/ui/index.d.ts +3 -2
- package/dist/components/ui/index.js +2 -1
- package/dist/components/ui/info-tooltip.cjs +61 -0
- package/dist/components/ui/info-tooltip.d.ts +10 -0
- package/dist/components/ui/info-tooltip.js +27 -0
- package/dist/components/ui/prompt-editor/components/EditorToolbar.cjs +21 -56
- package/dist/components/ui/prompt-editor/components/EditorToolbar.d.ts +2 -4
- package/dist/components/ui/prompt-editor/components/EditorToolbar.js +21 -56
- package/dist/components/ui/prompt-editor/prompt-editor-config.cjs +0 -1
- package/dist/components/ui/prompt-editor/prompt-editor-config.d.ts +0 -1
- package/dist/components/ui/prompt-editor/prompt-editor-config.js +0 -1
- package/dist/components/ui/select.cjs +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/textarea.cjs +1 -1
- package/dist/components/ui/textarea.js +1 -1
- package/dist/index.cjs +14 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +4 -2
- package/dist/styles.css +13 -0
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Plus, Trash2 } from "lucide-react";
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import { Button } from "../ui/button.js";
|
|
5
|
+
import { FormField, FormFieldDescription, FormFieldError, FormFieldLabel } from "../ui/form-field.js";
|
|
6
|
+
import { Textarea } from "../ui/textarea.js";
|
|
7
|
+
function formatTemplate(template, values) {
|
|
8
|
+
return template.replace(/\{\{(\w+)\}\}/g, (match, token)=>token in values ? String(values[token]) : match);
|
|
9
|
+
}
|
|
10
|
+
function StringListField({ field, value, onChange, onBlur, error, disabled = false, required = false, inputRef }) {
|
|
11
|
+
const items = value ?? [];
|
|
12
|
+
const maxItems = field.maxItems ?? 1 / 0;
|
|
13
|
+
const canAdd = !disabled && items.length < maxItems;
|
|
14
|
+
const addItemLabel = field.addItemLabel ?? 'Add';
|
|
15
|
+
const removeItemAriaLabel = field.removeItemAriaLabel ?? 'Remove {{label}} {{position}}';
|
|
16
|
+
const [rowIds, setRowIds] = useState(()=>items.map(()=>crypto.randomUUID()));
|
|
17
|
+
const [prevLength, setPrevLength] = useState(items.length);
|
|
18
|
+
if (prevLength !== items.length) {
|
|
19
|
+
setPrevLength(items.length);
|
|
20
|
+
setRowIds((prev)=>prev.length < items.length ? [
|
|
21
|
+
...prev,
|
|
22
|
+
...Array.from({
|
|
23
|
+
length: items.length - prev.length
|
|
24
|
+
}, ()=>crypto.randomUUID())
|
|
25
|
+
] : prev.slice(0, items.length));
|
|
26
|
+
}
|
|
27
|
+
const addItem = useCallback(()=>{
|
|
28
|
+
setRowIds((prev)=>[
|
|
29
|
+
...prev,
|
|
30
|
+
crypto.randomUUID()
|
|
31
|
+
]);
|
|
32
|
+
onChange([
|
|
33
|
+
...items,
|
|
34
|
+
''
|
|
35
|
+
]);
|
|
36
|
+
}, [
|
|
37
|
+
items,
|
|
38
|
+
onChange
|
|
39
|
+
]);
|
|
40
|
+
const removeItem = useCallback((index)=>{
|
|
41
|
+
setRowIds((prev)=>prev.filter((_, i)=>i !== index));
|
|
42
|
+
onChange(items.filter((_, i)=>i !== index));
|
|
43
|
+
}, [
|
|
44
|
+
items,
|
|
45
|
+
onChange
|
|
46
|
+
]);
|
|
47
|
+
const updateItem = useCallback((index, next)=>{
|
|
48
|
+
onChange(items.map((item, i)=>i === index ? next : item));
|
|
49
|
+
}, [
|
|
50
|
+
items,
|
|
51
|
+
onChange
|
|
52
|
+
]);
|
|
53
|
+
return /*#__PURE__*/ jsxs(FormField, {
|
|
54
|
+
"data-slot": "string-list-field",
|
|
55
|
+
children: [
|
|
56
|
+
/*#__PURE__*/ jsx(FormFieldLabel, {
|
|
57
|
+
required: required,
|
|
58
|
+
tooltip: field.tooltip,
|
|
59
|
+
tooltipAriaLabel: field.tooltipAriaLabel,
|
|
60
|
+
children: field.label
|
|
61
|
+
}),
|
|
62
|
+
/*#__PURE__*/ jsx("div", {
|
|
63
|
+
className: "grid gap-1.5",
|
|
64
|
+
children: items.map((item, index)=>/*#__PURE__*/ jsxs("div", {
|
|
65
|
+
className: "flex items-start gap-2",
|
|
66
|
+
children: [
|
|
67
|
+
/*#__PURE__*/ jsx(Textarea, {
|
|
68
|
+
ref: 0 === index ? inputRef : void 0,
|
|
69
|
+
value: item,
|
|
70
|
+
onChange: (e)=>updateItem(index, e.target.value),
|
|
71
|
+
onBlur: onBlur,
|
|
72
|
+
minRows: field.minRows ?? 2,
|
|
73
|
+
maxLength: field.maxLength,
|
|
74
|
+
disabled: disabled,
|
|
75
|
+
"aria-label": `${field.label} ${index + 1}`,
|
|
76
|
+
"aria-invalid": error ? true : void 0,
|
|
77
|
+
className: "flex-1"
|
|
78
|
+
}),
|
|
79
|
+
/*#__PURE__*/ jsx(Button, {
|
|
80
|
+
type: "button",
|
|
81
|
+
variant: "ghost",
|
|
82
|
+
size: "sm",
|
|
83
|
+
icon: true,
|
|
84
|
+
disabled: disabled,
|
|
85
|
+
onClick: ()=>removeItem(index),
|
|
86
|
+
"aria-label": formatTemplate(removeItemAriaLabel, {
|
|
87
|
+
label: field.label,
|
|
88
|
+
position: index + 1
|
|
89
|
+
}),
|
|
90
|
+
className: "shrink-0 text-muted-foreground",
|
|
91
|
+
children: /*#__PURE__*/ jsx(Trash2, {})
|
|
92
|
+
})
|
|
93
|
+
]
|
|
94
|
+
}, rowIds[index] ?? index))
|
|
95
|
+
}),
|
|
96
|
+
canAdd && /*#__PURE__*/ jsx("div", {
|
|
97
|
+
children: /*#__PURE__*/ jsxs(Button, {
|
|
98
|
+
type: "button",
|
|
99
|
+
variant: "text",
|
|
100
|
+
size: "2xs",
|
|
101
|
+
onClick: addItem,
|
|
102
|
+
children: [
|
|
103
|
+
/*#__PURE__*/ jsx(Plus, {}),
|
|
104
|
+
addItemLabel
|
|
105
|
+
]
|
|
106
|
+
})
|
|
107
|
+
}),
|
|
108
|
+
/*#__PURE__*/ jsx(FormFieldDescription, {
|
|
109
|
+
children: field.description
|
|
110
|
+
}),
|
|
111
|
+
/*#__PURE__*/ jsx(FormFieldError, {
|
|
112
|
+
children: error
|
|
113
|
+
})
|
|
114
|
+
]
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
export { StringListField, formatTemplate };
|
|
@@ -24,24 +24,45 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
mergeValidationConfigs: ()=>mergeValidationConfigs,
|
|
27
28
|
buildZodSchemaFromFields: ()=>buildZodSchemaFromFields,
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
isEmptyFieldValue: ()=>isEmptyFieldValue,
|
|
30
|
+
validationConfigToZod: ()=>validationConfigToZod
|
|
30
31
|
});
|
|
31
32
|
const external_zod_namespaceObject = require("zod");
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const external_rules_engine_cjs_namespaceObject = require("./rules-engine.cjs");
|
|
34
|
+
function validationConfigToZod(config, fieldType, customValueType) {
|
|
35
|
+
let schema = getBaseSchemaForType(fieldType, customValueType);
|
|
34
36
|
if (!config) return schema.optional();
|
|
35
|
-
schema = applyStringConstraints(schema, config, fieldType);
|
|
36
|
-
schema = applyNumberConstraints(schema, config, fieldType);
|
|
37
|
-
schema = applyArrayConstraints(schema, config, fieldType);
|
|
37
|
+
schema = applyStringConstraints(schema, config, fieldType, customValueType);
|
|
38
|
+
schema = applyNumberConstraints(schema, config, fieldType, customValueType);
|
|
39
|
+
schema = applyArrayConstraints(schema, config, fieldType, customValueType);
|
|
38
40
|
if (config.required) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const requiredMessage = config.messages?.required || 'This field is required';
|
|
42
|
+
if (isStringType(fieldType, customValueType)) schema = schema.refine((value)=>!isEmptyFieldValue(value), {
|
|
43
|
+
message: requiredMessage
|
|
44
|
+
});
|
|
45
|
+
if (isArrayType(fieldType, customValueType) && (null == config.minItems || config.minItems < 1)) schema = schema.min(1, requiredMessage);
|
|
46
|
+
return applyCustomExpression(schema, config);
|
|
41
47
|
}
|
|
42
|
-
return schema.optional();
|
|
48
|
+
return applyCustomExpression(schema, config).optional();
|
|
49
|
+
}
|
|
50
|
+
function applyCustomExpression(schema, config) {
|
|
51
|
+
if (!config.custom) return schema;
|
|
52
|
+
const expression = config.custom;
|
|
53
|
+
const message = config.messages?.custom || 'This value is not valid';
|
|
54
|
+
return schema.superRefine((value, ctx)=>{
|
|
55
|
+
const result = external_rules_engine_cjs_namespaceObject.RulesEngine.tryEvaluateExpression(expression, {
|
|
56
|
+
value
|
|
57
|
+
});
|
|
58
|
+
if (!result.ok) return;
|
|
59
|
+
if (!result.value) ctx.addIssue({
|
|
60
|
+
code: 'custom',
|
|
61
|
+
message
|
|
62
|
+
});
|
|
63
|
+
});
|
|
43
64
|
}
|
|
44
|
-
function getBaseSchemaForType(fieldType) {
|
|
65
|
+
function getBaseSchemaForType(fieldType, customValueType) {
|
|
45
66
|
switch(fieldType){
|
|
46
67
|
case 'text':
|
|
47
68
|
case 'textarea':
|
|
@@ -58,6 +79,7 @@ function getBaseSchemaForType(fieldType) {
|
|
|
58
79
|
case 'radio':
|
|
59
80
|
return external_zod_namespaceObject.z.string();
|
|
60
81
|
case 'multiselect':
|
|
82
|
+
case 'string-list':
|
|
61
83
|
return external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string());
|
|
62
84
|
case 'date':
|
|
63
85
|
return external_zod_namespaceObject.z.coerce.date();
|
|
@@ -66,12 +88,30 @@ function getBaseSchemaForType(fieldType) {
|
|
|
66
88
|
case 'file':
|
|
67
89
|
return external_zod_namespaceObject.z.any();
|
|
68
90
|
case 'custom':
|
|
69
|
-
|
|
91
|
+
switch(customValueType){
|
|
92
|
+
case 'string':
|
|
93
|
+
return external_zod_namespaceObject.z.string();
|
|
94
|
+
case 'number':
|
|
95
|
+
return external_zod_namespaceObject.z.number();
|
|
96
|
+
case 'boolean':
|
|
97
|
+
return external_zod_namespaceObject.z.boolean();
|
|
98
|
+
case 'string-array':
|
|
99
|
+
return external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string());
|
|
100
|
+
default:
|
|
101
|
+
return external_zod_namespaceObject.z.any();
|
|
102
|
+
}
|
|
70
103
|
default:
|
|
71
104
|
return external_zod_namespaceObject.z.string();
|
|
72
105
|
}
|
|
73
106
|
}
|
|
74
|
-
function
|
|
107
|
+
function isEmptyFieldValue(value) {
|
|
108
|
+
if (null == value) return true;
|
|
109
|
+
if ('string' == typeof value) return '' === value.trim();
|
|
110
|
+
if (Array.isArray(value)) return 0 === value.length;
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
function isStringType(fieldType, customValueType) {
|
|
114
|
+
if ('custom' === fieldType) return 'string' === customValueType;
|
|
75
115
|
return [
|
|
76
116
|
'text',
|
|
77
117
|
'textarea',
|
|
@@ -80,19 +120,22 @@ function isStringType(fieldType) {
|
|
|
80
120
|
'radio'
|
|
81
121
|
].includes(fieldType);
|
|
82
122
|
}
|
|
83
|
-
function isNumberType(fieldType) {
|
|
123
|
+
function isNumberType(fieldType, customValueType) {
|
|
124
|
+
if ('custom' === fieldType) return 'number' === customValueType;
|
|
84
125
|
return [
|
|
85
126
|
'number',
|
|
86
127
|
'slider'
|
|
87
128
|
].includes(fieldType);
|
|
88
129
|
}
|
|
89
|
-
function isArrayType(fieldType) {
|
|
130
|
+
function isArrayType(fieldType, customValueType) {
|
|
131
|
+
if ('custom' === fieldType) return 'string-array' === customValueType;
|
|
90
132
|
return [
|
|
91
|
-
'multiselect'
|
|
133
|
+
'multiselect',
|
|
134
|
+
'string-list'
|
|
92
135
|
].includes(fieldType);
|
|
93
136
|
}
|
|
94
|
-
function applyStringConstraints(schema, config, fieldType) {
|
|
95
|
-
if (!isStringType(fieldType)) return schema;
|
|
137
|
+
function applyStringConstraints(schema, config, fieldType, customValueType) {
|
|
138
|
+
if (!isStringType(fieldType, customValueType)) return schema;
|
|
96
139
|
let stringSchema = schema;
|
|
97
140
|
if (null != config.minLength) stringSchema = stringSchema.min(config.minLength, config.messages?.minLength || `Must be at least ${config.minLength} characters`);
|
|
98
141
|
if (null != config.maxLength) stringSchema = stringSchema.max(config.maxLength, config.messages?.maxLength || `Must be at most ${config.maxLength} characters`);
|
|
@@ -106,8 +149,8 @@ function applyStringConstraints(schema, config, fieldType) {
|
|
|
106
149
|
if (config.url) stringSchema = stringSchema.url(config.messages?.url || 'Invalid URL');
|
|
107
150
|
return stringSchema;
|
|
108
151
|
}
|
|
109
|
-
function applyNumberConstraints(schema, config, fieldType) {
|
|
110
|
-
if (!isNumberType(fieldType)) return schema;
|
|
152
|
+
function applyNumberConstraints(schema, config, fieldType, customValueType) {
|
|
153
|
+
if (!isNumberType(fieldType, customValueType)) return schema;
|
|
111
154
|
let numberSchema = schema;
|
|
112
155
|
if (config.integer) numberSchema = numberSchema.int(config.messages?.integer || 'Must be a whole number');
|
|
113
156
|
if (null != config.min) numberSchema = numberSchema.min(config.min, config.messages?.min || `Must be at least ${config.min}`);
|
|
@@ -116,8 +159,8 @@ function applyNumberConstraints(schema, config, fieldType) {
|
|
|
116
159
|
if (config.negative) numberSchema = numberSchema.negative(config.messages?.negative || 'Must be a negative number');
|
|
117
160
|
return numberSchema;
|
|
118
161
|
}
|
|
119
|
-
function applyArrayConstraints(schema, config, fieldType) {
|
|
120
|
-
if (!isArrayType(fieldType)) return schema;
|
|
162
|
+
function applyArrayConstraints(schema, config, fieldType, customValueType) {
|
|
163
|
+
if (!isArrayType(fieldType, customValueType)) return schema;
|
|
121
164
|
let arraySchema = schema;
|
|
122
165
|
if (null != config.minItems) arraySchema = arraySchema.min(config.minItems, config.messages?.minItems || `Select at least ${config.minItems} item(s)`);
|
|
123
166
|
if (null != config.maxItems) arraySchema = arraySchema.max(config.maxItems, config.messages?.maxItems || `Select at most ${config.maxItems} item(s)`);
|
|
@@ -125,7 +168,7 @@ function applyArrayConstraints(schema, config, fieldType) {
|
|
|
125
168
|
}
|
|
126
169
|
function buildZodSchemaFromFields(fields) {
|
|
127
170
|
const shape = {};
|
|
128
|
-
for (const field of fields)shape[field.name] = validationConfigToZod(field.validation, field.type);
|
|
171
|
+
for (const field of fields)shape[field.name] = validationConfigToZod(field.validation, field.type, field.valueType);
|
|
129
172
|
return external_zod_namespaceObject.z.object(shape);
|
|
130
173
|
}
|
|
131
174
|
function mergeValidationConfigs(base, override) {
|
|
@@ -142,10 +185,12 @@ function mergeValidationConfigs(base, override) {
|
|
|
142
185
|
};
|
|
143
186
|
}
|
|
144
187
|
exports.buildZodSchemaFromFields = __webpack_exports__.buildZodSchemaFromFields;
|
|
188
|
+
exports.isEmptyFieldValue = __webpack_exports__.isEmptyFieldValue;
|
|
145
189
|
exports.mergeValidationConfigs = __webpack_exports__.mergeValidationConfigs;
|
|
146
190
|
exports.validationConfigToZod = __webpack_exports__.validationConfigToZod;
|
|
147
191
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
148
192
|
"buildZodSchemaFromFields",
|
|
193
|
+
"isEmptyFieldValue",
|
|
149
194
|
"mergeValidationConfigs",
|
|
150
195
|
"validationConfigToZod"
|
|
151
196
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import type {
|
|
2
|
+
import type { CustomValueType, FieldType, ValidationConfig } from './form-schema';
|
|
3
|
+
export type { CustomValueType };
|
|
3
4
|
/**
|
|
4
5
|
* Validation Converter
|
|
5
6
|
*
|
|
@@ -20,7 +21,21 @@ import type { ValidationConfig, FieldType } from './form-schema';
|
|
|
20
21
|
* );
|
|
21
22
|
* // Returns: z.string().min(2).email()
|
|
22
23
|
*/
|
|
23
|
-
export declare function validationConfigToZod(config: ValidationConfig | undefined, fieldType: FieldType
|
|
24
|
+
export declare function validationConfigToZod(config: ValidationConfig | undefined, fieldType: FieldType,
|
|
25
|
+
/**
|
|
26
|
+
* Declared shape of a `type: 'custom'` field's value. Custom fields otherwise validate as
|
|
27
|
+
* `z.any()`, where `required` and the array constraints are no-ops.
|
|
28
|
+
*/
|
|
29
|
+
customValueType?: CustomValueType): z.ZodTypeAny;
|
|
30
|
+
/**
|
|
31
|
+
* Check if field type uses string schema
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* The one definition of "empty" for a required field. Exported so the resolver and the
|
|
35
|
+
* conditional-required `superRefine` in metadata-form agree — they used to disagree, with
|
|
36
|
+
* the narrower resolver path missing whitespace-only strings.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isEmptyFieldValue(value: unknown): boolean;
|
|
24
39
|
/**
|
|
25
40
|
* Build a complete Zod object schema from field validations
|
|
26
41
|
*
|
|
@@ -31,6 +46,12 @@ export declare function buildZodSchemaFromFields(fields: Array<{
|
|
|
31
46
|
name: string;
|
|
32
47
|
type: FieldType;
|
|
33
48
|
validation?: ValidationConfig;
|
|
49
|
+
/**
|
|
50
|
+
* Declared shape of a `type: 'custom'` field. Accepted here as well as by
|
|
51
|
+
* `validationConfigToZod`, otherwise a schema built through this helper validates every
|
|
52
|
+
* typed custom field as `z.any()` and its `required` / `minItems` quietly do nothing.
|
|
53
|
+
*/
|
|
54
|
+
valueType?: CustomValueType;
|
|
34
55
|
}>): z.ZodObject<Record<string, z.ZodTypeAny>>;
|
|
35
56
|
/**
|
|
36
57
|
* Merge multiple ValidationConfigs (for rule-based validation changes)
|
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { RulesEngine } from "./rules-engine.js";
|
|
3
|
+
function validationConfigToZod(config, fieldType, customValueType) {
|
|
4
|
+
let schema = getBaseSchemaForType(fieldType, customValueType);
|
|
4
5
|
if (!config) return schema.optional();
|
|
5
|
-
schema = applyStringConstraints(schema, config, fieldType);
|
|
6
|
-
schema = applyNumberConstraints(schema, config, fieldType);
|
|
7
|
-
schema = applyArrayConstraints(schema, config, fieldType);
|
|
6
|
+
schema = applyStringConstraints(schema, config, fieldType, customValueType);
|
|
7
|
+
schema = applyNumberConstraints(schema, config, fieldType, customValueType);
|
|
8
|
+
schema = applyArrayConstraints(schema, config, fieldType, customValueType);
|
|
8
9
|
if (config.required) {
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
const requiredMessage = config.messages?.required || 'This field is required';
|
|
11
|
+
if (isStringType(fieldType, customValueType)) schema = schema.refine((value)=>!isEmptyFieldValue(value), {
|
|
12
|
+
message: requiredMessage
|
|
13
|
+
});
|
|
14
|
+
if (isArrayType(fieldType, customValueType) && (null == config.minItems || config.minItems < 1)) schema = schema.min(1, requiredMessage);
|
|
15
|
+
return applyCustomExpression(schema, config);
|
|
11
16
|
}
|
|
12
|
-
return schema.optional();
|
|
17
|
+
return applyCustomExpression(schema, config).optional();
|
|
13
18
|
}
|
|
14
|
-
function
|
|
19
|
+
function applyCustomExpression(schema, config) {
|
|
20
|
+
if (!config.custom) return schema;
|
|
21
|
+
const expression = config.custom;
|
|
22
|
+
const message = config.messages?.custom || 'This value is not valid';
|
|
23
|
+
return schema.superRefine((value, ctx)=>{
|
|
24
|
+
const result = RulesEngine.tryEvaluateExpression(expression, {
|
|
25
|
+
value
|
|
26
|
+
});
|
|
27
|
+
if (!result.ok) return;
|
|
28
|
+
if (!result.value) ctx.addIssue({
|
|
29
|
+
code: 'custom',
|
|
30
|
+
message
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function getBaseSchemaForType(fieldType, customValueType) {
|
|
15
35
|
switch(fieldType){
|
|
16
36
|
case 'text':
|
|
17
37
|
case 'textarea':
|
|
@@ -28,6 +48,7 @@ function getBaseSchemaForType(fieldType) {
|
|
|
28
48
|
case 'radio':
|
|
29
49
|
return z.string();
|
|
30
50
|
case 'multiselect':
|
|
51
|
+
case 'string-list':
|
|
31
52
|
return z.array(z.string());
|
|
32
53
|
case 'date':
|
|
33
54
|
return z.coerce.date();
|
|
@@ -36,12 +57,30 @@ function getBaseSchemaForType(fieldType) {
|
|
|
36
57
|
case 'file':
|
|
37
58
|
return z.any();
|
|
38
59
|
case 'custom':
|
|
39
|
-
|
|
60
|
+
switch(customValueType){
|
|
61
|
+
case 'string':
|
|
62
|
+
return z.string();
|
|
63
|
+
case 'number':
|
|
64
|
+
return z.number();
|
|
65
|
+
case 'boolean':
|
|
66
|
+
return z.boolean();
|
|
67
|
+
case 'string-array':
|
|
68
|
+
return z.array(z.string());
|
|
69
|
+
default:
|
|
70
|
+
return z.any();
|
|
71
|
+
}
|
|
40
72
|
default:
|
|
41
73
|
return z.string();
|
|
42
74
|
}
|
|
43
75
|
}
|
|
44
|
-
function
|
|
76
|
+
function isEmptyFieldValue(value) {
|
|
77
|
+
if (null == value) return true;
|
|
78
|
+
if ('string' == typeof value) return '' === value.trim();
|
|
79
|
+
if (Array.isArray(value)) return 0 === value.length;
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
function isStringType(fieldType, customValueType) {
|
|
83
|
+
if ('custom' === fieldType) return 'string' === customValueType;
|
|
45
84
|
return [
|
|
46
85
|
'text',
|
|
47
86
|
'textarea',
|
|
@@ -50,19 +89,22 @@ function isStringType(fieldType) {
|
|
|
50
89
|
'radio'
|
|
51
90
|
].includes(fieldType);
|
|
52
91
|
}
|
|
53
|
-
function isNumberType(fieldType) {
|
|
92
|
+
function isNumberType(fieldType, customValueType) {
|
|
93
|
+
if ('custom' === fieldType) return 'number' === customValueType;
|
|
54
94
|
return [
|
|
55
95
|
'number',
|
|
56
96
|
'slider'
|
|
57
97
|
].includes(fieldType);
|
|
58
98
|
}
|
|
59
|
-
function isArrayType(fieldType) {
|
|
99
|
+
function isArrayType(fieldType, customValueType) {
|
|
100
|
+
if ('custom' === fieldType) return 'string-array' === customValueType;
|
|
60
101
|
return [
|
|
61
|
-
'multiselect'
|
|
102
|
+
'multiselect',
|
|
103
|
+
'string-list'
|
|
62
104
|
].includes(fieldType);
|
|
63
105
|
}
|
|
64
|
-
function applyStringConstraints(schema, config, fieldType) {
|
|
65
|
-
if (!isStringType(fieldType)) return schema;
|
|
106
|
+
function applyStringConstraints(schema, config, fieldType, customValueType) {
|
|
107
|
+
if (!isStringType(fieldType, customValueType)) return schema;
|
|
66
108
|
let stringSchema = schema;
|
|
67
109
|
if (null != config.minLength) stringSchema = stringSchema.min(config.minLength, config.messages?.minLength || `Must be at least ${config.minLength} characters`);
|
|
68
110
|
if (null != config.maxLength) stringSchema = stringSchema.max(config.maxLength, config.messages?.maxLength || `Must be at most ${config.maxLength} characters`);
|
|
@@ -76,8 +118,8 @@ function applyStringConstraints(schema, config, fieldType) {
|
|
|
76
118
|
if (config.url) stringSchema = stringSchema.url(config.messages?.url || 'Invalid URL');
|
|
77
119
|
return stringSchema;
|
|
78
120
|
}
|
|
79
|
-
function applyNumberConstraints(schema, config, fieldType) {
|
|
80
|
-
if (!isNumberType(fieldType)) return schema;
|
|
121
|
+
function applyNumberConstraints(schema, config, fieldType, customValueType) {
|
|
122
|
+
if (!isNumberType(fieldType, customValueType)) return schema;
|
|
81
123
|
let numberSchema = schema;
|
|
82
124
|
if (config.integer) numberSchema = numberSchema.int(config.messages?.integer || 'Must be a whole number');
|
|
83
125
|
if (null != config.min) numberSchema = numberSchema.min(config.min, config.messages?.min || `Must be at least ${config.min}`);
|
|
@@ -86,8 +128,8 @@ function applyNumberConstraints(schema, config, fieldType) {
|
|
|
86
128
|
if (config.negative) numberSchema = numberSchema.negative(config.messages?.negative || 'Must be a negative number');
|
|
87
129
|
return numberSchema;
|
|
88
130
|
}
|
|
89
|
-
function applyArrayConstraints(schema, config, fieldType) {
|
|
90
|
-
if (!isArrayType(fieldType)) return schema;
|
|
131
|
+
function applyArrayConstraints(schema, config, fieldType, customValueType) {
|
|
132
|
+
if (!isArrayType(fieldType, customValueType)) return schema;
|
|
91
133
|
let arraySchema = schema;
|
|
92
134
|
if (null != config.minItems) arraySchema = arraySchema.min(config.minItems, config.messages?.minItems || `Select at least ${config.minItems} item(s)`);
|
|
93
135
|
if (null != config.maxItems) arraySchema = arraySchema.max(config.maxItems, config.messages?.maxItems || `Select at most ${config.maxItems} item(s)`);
|
|
@@ -95,7 +137,7 @@ function applyArrayConstraints(schema, config, fieldType) {
|
|
|
95
137
|
}
|
|
96
138
|
function buildZodSchemaFromFields(fields) {
|
|
97
139
|
const shape = {};
|
|
98
|
-
for (const field of fields)shape[field.name] = validationConfigToZod(field.validation, field.type);
|
|
140
|
+
for (const field of fields)shape[field.name] = validationConfigToZod(field.validation, field.type, field.valueType);
|
|
99
141
|
return z.object(shape);
|
|
100
142
|
}
|
|
101
143
|
function mergeValidationConfigs(base, override) {
|
|
@@ -111,4 +153,4 @@ function mergeValidationConfigs(base, override) {
|
|
|
111
153
|
}
|
|
112
154
|
};
|
|
113
155
|
}
|
|
114
|
-
export { buildZodSchemaFromFields, mergeValidationConfigs, validationConfigToZod };
|
|
156
|
+
export { buildZodSchemaFromFields, isEmptyFieldValue, mergeValidationConfigs, validationConfigToZod };
|
|
@@ -36,7 +36,7 @@ const external_input_cjs_namespaceObject = require("./input.cjs");
|
|
|
36
36
|
const external_label_cjs_namespaceObject = require("./label.cjs");
|
|
37
37
|
const external_popover_cjs_namespaceObject = require("./popover.cjs");
|
|
38
38
|
const index_cjs_namespaceObject = require("../../lib/index.cjs");
|
|
39
|
-
const datetime_picker_DateTimePicker = /*#__PURE__*/ external_react_namespaceObject.forwardRef(function({ value, onValueChange, disabled, placeholder = 'Pick a date and time', className, use12Hour = false }, ref) {
|
|
39
|
+
const datetime_picker_DateTimePicker = /*#__PURE__*/ external_react_namespaceObject.forwardRef(function({ value, onValueChange, disabled, placeholder = 'Pick a date and time', className, use12Hour = false, 'aria-labelledby': ariaLabelledBy, 'aria-invalid': ariaInvalid }, ref) {
|
|
40
40
|
const [open, setOpen] = external_react_namespaceObject.useState(false);
|
|
41
41
|
const [selectedDate, setSelectedDate] = external_react_namespaceObject.useState(value);
|
|
42
42
|
const [timeValue, setTimeValue] = external_react_namespaceObject.useState(value ? (0, external_date_fns_namespaceObject.format)(value, use12Hour ? 'hh:mm a' : 'HH:mm') : '');
|
|
@@ -76,6 +76,8 @@ const datetime_picker_DateTimePicker = /*#__PURE__*/ external_react_namespaceObj
|
|
|
76
76
|
asChild: true,
|
|
77
77
|
children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(external_button_cjs_namespaceObject.Button, {
|
|
78
78
|
ref: ref,
|
|
79
|
+
"aria-labelledby": ariaLabelledBy,
|
|
80
|
+
"aria-invalid": ariaInvalid,
|
|
79
81
|
variant: "outline",
|
|
80
82
|
className: (0, index_cjs_namespaceObject.cn)('w-full justify-start text-left font-normal [&>svg]:text-foreground-muted hover:[&>svg]:text-accent-foreground', 'future:h-10 future:rounded-xl future:border-0 future:bg-surface-overlay future:px-4 future:gap-4 future:text-foreground future:hover:bg-surface-hover future:focus-visible:ring-offset-2 future:focus-visible:ring-offset-background', className),
|
|
81
83
|
disabled: disabled,
|
|
@@ -6,5 +6,9 @@ export interface DateTimePickerProps {
|
|
|
6
6
|
placeholder?: string;
|
|
7
7
|
className?: string;
|
|
8
8
|
use12Hour?: boolean;
|
|
9
|
+
/** Id of the element naming this control, forwarded to the trigger button. */
|
|
10
|
+
'aria-labelledby'?: string;
|
|
11
|
+
/** Marks the trigger invalid, so the error is exposed on the control itself. */
|
|
12
|
+
'aria-invalid'?: boolean;
|
|
9
13
|
}
|
|
10
14
|
export declare const DateTimePicker: React.ForwardRefExoticComponent<DateTimePickerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -8,7 +8,7 @@ import { Input } from "./input.js";
|
|
|
8
8
|
import { Label } from "./label.js";
|
|
9
9
|
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
10
10
|
import { cn } from "../../lib/index.js";
|
|
11
|
-
const datetime_picker_DateTimePicker = /*#__PURE__*/ forwardRef(function({ value, onValueChange, disabled, placeholder = 'Pick a date and time', className, use12Hour = false }, ref) {
|
|
11
|
+
const datetime_picker_DateTimePicker = /*#__PURE__*/ forwardRef(function({ value, onValueChange, disabled, placeholder = 'Pick a date and time', className, use12Hour = false, 'aria-labelledby': ariaLabelledBy, 'aria-invalid': ariaInvalid }, ref) {
|
|
12
12
|
const [open, setOpen] = useState(false);
|
|
13
13
|
const [selectedDate, setSelectedDate] = useState(value);
|
|
14
14
|
const [timeValue, setTimeValue] = useState(value ? format(value, use12Hour ? 'hh:mm a' : 'HH:mm') : '');
|
|
@@ -48,6 +48,8 @@ const datetime_picker_DateTimePicker = /*#__PURE__*/ forwardRef(function({ value
|
|
|
48
48
|
asChild: true,
|
|
49
49
|
children: /*#__PURE__*/ jsxs(Button, {
|
|
50
50
|
ref: ref,
|
|
51
|
+
"aria-labelledby": ariaLabelledBy,
|
|
52
|
+
"aria-invalid": ariaInvalid,
|
|
51
53
|
variant: "outline",
|
|
52
54
|
className: cn('w-full justify-start text-left font-normal [&>svg]:text-foreground-muted hover:[&>svg]:text-accent-foreground', 'future:h-10 future:rounded-xl future:border-0 future:bg-surface-overlay future:px-4 future:gap-4 future:text-foreground future:hover:bg-surface-hover future:focus-visible:ring-offset-2 future:focus-visible:ring-offset-background', className),
|
|
53
55
|
disabled: disabled,
|
|
@@ -31,6 +31,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
31
31
|
});
|
|
32
32
|
const jsx_runtime_namespaceObject = require("react/jsx-runtime");
|
|
33
33
|
const external_react_namespaceObject = require("react");
|
|
34
|
+
const external_info_tooltip_cjs_namespaceObject = require("./info-tooltip.cjs");
|
|
34
35
|
const external_label_cjs_namespaceObject = require("./label.cjs");
|
|
35
36
|
const index_cjs_namespaceObject = require("../../lib/index.cjs");
|
|
36
37
|
const FormField = /*#__PURE__*/ external_react_namespaceObject.forwardRef(({ children, className, ...props }, ref)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
|
|
@@ -41,15 +42,29 @@ const FormField = /*#__PURE__*/ external_react_namespaceObject.forwardRef(({ chi
|
|
|
41
42
|
children: children
|
|
42
43
|
}));
|
|
43
44
|
FormField.displayName = 'FormField';
|
|
44
|
-
const FormFieldLabel = /*#__PURE__*/ external_react_namespaceObject.forwardRef(({ children, required = false, ...props }, ref)
|
|
45
|
+
const FormFieldLabel = /*#__PURE__*/ external_react_namespaceObject.forwardRef(({ children, required = false, tooltip, tooltipAriaLabel, className, ...props }, ref)=>{
|
|
46
|
+
const label = /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(external_label_cjs_namespaceObject.Label, {
|
|
45
47
|
ref: ref,
|
|
46
48
|
"data-slot": "form-field-label",
|
|
49
|
+
className: className,
|
|
47
50
|
...props,
|
|
48
51
|
children: [
|
|
49
52
|
children,
|
|
50
53
|
required && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_label_cjs_namespaceObject.RequiredIndicator, {})
|
|
51
54
|
]
|
|
52
|
-
})
|
|
55
|
+
});
|
|
56
|
+
if (null == tooltip || false === tooltip) return label;
|
|
57
|
+
return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("span", {
|
|
58
|
+
className: "inline-flex items-center",
|
|
59
|
+
children: [
|
|
60
|
+
label,
|
|
61
|
+
/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_info_tooltip_cjs_namespaceObject.InfoTooltip, {
|
|
62
|
+
content: tooltip,
|
|
63
|
+
"aria-label": tooltipAriaLabel ?? 'More information'
|
|
64
|
+
})
|
|
65
|
+
]
|
|
66
|
+
});
|
|
67
|
+
});
|
|
53
68
|
FormFieldLabel.displayName = 'FormFieldLabel';
|
|
54
69
|
const FormFieldDescription = /*#__PURE__*/ external_react_namespaceObject.forwardRef(({ children, className, ...props }, ref)=>{
|
|
55
70
|
if (!children) return null;
|
|
@@ -19,6 +19,13 @@ declare const FormField: React.ForwardRefExoticComponent<Omit<React.DetailedHTML
|
|
|
19
19
|
export interface FormFieldLabelProps extends React.ComponentPropsWithoutRef<typeof Label> {
|
|
20
20
|
/** Appends the required indicator after the label text. */
|
|
21
21
|
required?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Appends an info-tooltip trigger after the label text and the required indicator.
|
|
24
|
+
* Requires an ancestor `TooltipProvider` (Radix throws without one).
|
|
25
|
+
*/
|
|
26
|
+
tooltip?: React.ReactNode;
|
|
27
|
+
/** Accessible name of the tooltip trigger. Defaults to 'More information'. */
|
|
28
|
+
tooltipAriaLabel?: string;
|
|
22
29
|
}
|
|
23
30
|
/** Names the field. Pair with a control via `htmlFor`. */
|
|
24
31
|
declare const FormFieldLabel: React.ForwardRefExoticComponent<FormFieldLabelProps & React.RefAttributes<HTMLLabelElement>>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
|
+
import { InfoTooltip } from "./info-tooltip.js";
|
|
3
4
|
import { Label, RequiredIndicator } from "./label.js";
|
|
4
5
|
import { cn } from "../../lib/index.js";
|
|
5
6
|
const FormField = /*#__PURE__*/ forwardRef(({ children, className, ...props }, ref)=>/*#__PURE__*/ jsx("div", {
|
|
@@ -10,15 +11,29 @@ const FormField = /*#__PURE__*/ forwardRef(({ children, className, ...props }, r
|
|
|
10
11
|
children: children
|
|
11
12
|
}));
|
|
12
13
|
FormField.displayName = 'FormField';
|
|
13
|
-
const FormFieldLabel = /*#__PURE__*/ forwardRef(({ children, required = false, ...props }, ref)
|
|
14
|
+
const FormFieldLabel = /*#__PURE__*/ forwardRef(({ children, required = false, tooltip, tooltipAriaLabel, className, ...props }, ref)=>{
|
|
15
|
+
const label = /*#__PURE__*/ jsxs(Label, {
|
|
14
16
|
ref: ref,
|
|
15
17
|
"data-slot": "form-field-label",
|
|
18
|
+
className: className,
|
|
16
19
|
...props,
|
|
17
20
|
children: [
|
|
18
21
|
children,
|
|
19
22
|
required && /*#__PURE__*/ jsx(RequiredIndicator, {})
|
|
20
23
|
]
|
|
21
|
-
})
|
|
24
|
+
});
|
|
25
|
+
if (null == tooltip || false === tooltip) return label;
|
|
26
|
+
return /*#__PURE__*/ jsxs("span", {
|
|
27
|
+
className: "inline-flex items-center",
|
|
28
|
+
children: [
|
|
29
|
+
label,
|
|
30
|
+
/*#__PURE__*/ jsx(InfoTooltip, {
|
|
31
|
+
content: tooltip,
|
|
32
|
+
"aria-label": tooltipAriaLabel ?? 'More information'
|
|
33
|
+
})
|
|
34
|
+
]
|
|
35
|
+
});
|
|
36
|
+
});
|
|
22
37
|
FormFieldLabel.displayName = 'FormFieldLabel';
|
|
23
38
|
const FormFieldDescription = /*#__PURE__*/ forwardRef(({ children, className, ...props }, ref)=>{
|
|
24
39
|
if (!children) return null;
|