@webbycrown/advanced-fields 1.0.2 → 1.0.4
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/README.md +28 -1
- package/dist/_chunks/en-BPwepUqK.js +84 -0
- package/{admin/src/translations/en.json → dist/_chunks/en-CJmJkBE0.mjs} +6 -8
- package/dist/_chunks/index-AT-E0hrg.js +129 -0
- package/dist/_chunks/index-C-A6rX1K.mjs +268 -0
- package/dist/_chunks/index-CCaZAiQR.js +268 -0
- package/dist/_chunks/index-DXVpJkpj.mjs +282 -0
- package/dist/_chunks/index-TmoGbq0j.js +282 -0
- package/{admin/src/components/AdvancedInput/index.jsx → dist/_chunks/index-cLPilu5W.mjs} +42 -92
- package/dist/admin/index.js +879 -0
- package/dist/admin/index.mjs +880 -0
- package/package.json +2 -5
- package/admin/jsconfig.json +0 -10
- package/admin/src/components/AdvancedCheckbox/index.jsx +0 -377
- package/admin/src/components/AdvancedRadio/index.jsx +0 -344
- package/admin/src/components/Initializer.jsx +0 -18
- package/admin/src/components/PluginIcon.jsx +0 -108
- package/admin/src/index.js +0 -787
- package/admin/src/pages/App.jsx +0 -13
- package/admin/src/pluginId.js +0 -3
- package/admin/src/utils/getTranslation.js +0 -5
- package/index.js +0 -158
- package/strapi-server.js +0 -186
- package/strapi-server.mjs +0 -74
package/README.md
CHANGED
|
@@ -8,6 +8,16 @@ Professional custom fields for Strapi CMS that provide advanced functionality wi
|
|
|
8
8
|
|
|
9
9
|
📦 **NPM Package**: [@webbycrown/advanced-fields](https://www.npmjs.com/package/@webbycrown/advanced-fields)
|
|
10
10
|
|
|
11
|
+
## 🎥 Overview & Usage Demo
|
|
12
|
+
|
|
13
|
+
A short introduction and quick overview of **Advanced Fields for Strapi**, showcasing available field types, configuration options, and real-world usage inside the Strapi admin panel.
|
|
14
|
+
|
|
15
|
+
[](https://www.youtube.com/watch?v=-WRuUEWwGfk)
|
|
16
|
+
|
|
17
|
+
▶️ **[Watch Full Video on YouTube](https://www.youtube.com/watch?v=-WRuUEWwGfk)**
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
11
21
|
## ✨ Features
|
|
12
22
|
|
|
13
23
|
### 🔤 Advanced Input
|
|
@@ -57,6 +67,7 @@ yarn add @webbycrown/advanced-fields
|
|
|
57
67
|
- Advanced Checkbox
|
|
58
68
|
- Advanced Radio
|
|
59
69
|
|
|
70
|
+
|
|
60
71
|
## 📖 Usage Examples
|
|
61
72
|
|
|
62
73
|
### Advanced Input Configuration
|
|
@@ -263,8 +274,24 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
263
274
|
|
|
264
275
|
## 📊 Changelog
|
|
265
276
|
|
|
277
|
+
### v1.0.4
|
|
278
|
+
- 🐛 Fixed minor bugs in field validation
|
|
279
|
+
- 📝 Updated README to include Demo Video section
|
|
280
|
+
- ⚡ Improved documentation clarity for plugin setup and usage
|
|
281
|
+
|
|
282
|
+
### v1.0.3
|
|
283
|
+
- 📝 Documentation updates and minor formatting improvements.
|
|
284
|
+
|
|
285
|
+
### v1.0.2
|
|
286
|
+
- 🐛 Fixed bugs.
|
|
287
|
+
- 🛠️ Resolved minor issues affecting
|
|
288
|
+
|
|
289
|
+
### v1.0.1
|
|
290
|
+
- 📝 Updated the README file.
|
|
291
|
+
- ✨ Improved clarity and formatting for better readability.
|
|
292
|
+
|
|
266
293
|
### v1.0.0
|
|
267
|
-
- ✨ Initial release
|
|
294
|
+
- ✨ Initial release
|
|
268
295
|
- 🔤 Advanced Input field with validation
|
|
269
296
|
- ☑️ Advanced Checkbox (single/multiple)
|
|
270
297
|
- 🔘 Advanced Radio (single/multiple)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const en = {
|
|
4
|
+
"advanced-fields.plugin.name": "Advanced Fields",
|
|
5
|
+
"advanced-fields.input.label": "Advanced Input",
|
|
6
|
+
"advanced-fields.input.description": "Professional-grade text input with comprehensive validation and advanced configuration options",
|
|
7
|
+
"advanced-fields.checkbox.label": "Advanced Checkbox",
|
|
8
|
+
"advanced-fields.checkbox.description": "Unified checkbox field supporting both single and multiple selections with advanced configuration",
|
|
9
|
+
"advanced-fields.selectinput.label": "Select Input",
|
|
10
|
+
"advanced-fields.selectinput.description": "Select an option",
|
|
11
|
+
"advanced-fields.input.settings.base.label": "Basic settings",
|
|
12
|
+
"advanced-fields.input.options.required.label": "Required field",
|
|
13
|
+
"advanced-fields.input.options.required.description": "You won't be able to create an entry if this field is empty",
|
|
14
|
+
"advanced-fields.input.options.unique.label": "Unique field",
|
|
15
|
+
"advanced-fields.input.options.unique.description": "You won't be able to create an entry if there is an existing entry with identical content",
|
|
16
|
+
"advanced-fields.input.options.maxLength.label": "Maximum length",
|
|
17
|
+
"advanced-fields.input.options.maxLength.description": "Set the maximum number of characters allowed",
|
|
18
|
+
"advanced-fields.input.options.minLength.label": "Minimum length",
|
|
19
|
+
"advanced-fields.input.options.minLength.description": "Set the minimum number of characters allowed",
|
|
20
|
+
"advanced-fields.input.options.defaultValue.label": "Default value",
|
|
21
|
+
"advanced-fields.input.options.defaultValue.description": "Set the default value for this field",
|
|
22
|
+
"advanced-fields.input.settings.advanced.label": "Advanced settings",
|
|
23
|
+
"advanced-fields.input.options.regex.label": "RegExp pattern",
|
|
24
|
+
"advanced-fields.input.options.regex.description": "The text of the regular expression",
|
|
25
|
+
"advanced-fields.input.options.placeholder.label": "Placeholder",
|
|
26
|
+
"advanced-fields.input.options.placeholder.description": "Display text when the field is empty",
|
|
27
|
+
"advanced-fields.input.options.private.label": "Private field",
|
|
28
|
+
"advanced-fields.input.options.private.description": "This field will not be included in API responses",
|
|
29
|
+
"advanced-fields.input.options.customErrorMessage.label": "Custom error message",
|
|
30
|
+
"advanced-fields.input.options.customErrorMessage.description": "Display a custom error message when validation fails",
|
|
31
|
+
"advanced-fields.checkbox.settings.base.label": "Basic settings",
|
|
32
|
+
"advanced-fields.checkbox.options.required.label": "Required field",
|
|
33
|
+
"advanced-fields.checkbox.options.required.description": "You won't be able to create an entry if this field is empty",
|
|
34
|
+
"advanced-fields.checkbox.options.checkboxType.label": "Checkbox Type",
|
|
35
|
+
"advanced-fields.checkbox.options.checkboxType.description": "Choose between single checkbox or multiple checkboxes",
|
|
36
|
+
"advanced-fields.checkbox.options.checkboxType.single": "Single Checkbox",
|
|
37
|
+
"advanced-fields.checkbox.options.checkboxType.multiple": "Multiple Checkboxes",
|
|
38
|
+
"advanced-fields.checkbox.options.defaultValue.label": "Default State",
|
|
39
|
+
"advanced-fields.checkbox.options.defaultValue.description": "Pre-checked state for single checkbox",
|
|
40
|
+
"advanced-fields.checkbox.options.defaultValue.options.true": "Checked (True)",
|
|
41
|
+
"advanced-fields.checkbox.options.defaultValue.options.false": "Unchecked (False)",
|
|
42
|
+
"advanced-fields.checkbox.settings.advanced.label": "Advanced settings",
|
|
43
|
+
"advanced-fields.checkbox.options.customErrorMessage.label": "Custom error message",
|
|
44
|
+
"advanced-fields.checkbox.options.customErrorMessage.description": "Display a custom error message when validation fails",
|
|
45
|
+
"advanced-fields.checkbox.options.customErrorMessage.placeholder": "Enter custom error message",
|
|
46
|
+
"advanced-fields.checkbox.options.private.label": "Private field",
|
|
47
|
+
"advanced-fields.checkbox.options.private.description": "This field will not be included in API responses",
|
|
48
|
+
"advanced-fields.checkbox.options.labelOn.label": "Checked Label",
|
|
49
|
+
"advanced-fields.checkbox.options.labelOn.description": "Custom label displayed when single checkbox is checked",
|
|
50
|
+
"advanced-fields.checkbox.options.labelOff.label": "Unchecked Label",
|
|
51
|
+
"advanced-fields.checkbox.options.labelOff.description": "Custom label displayed when single checkbox is unchecked",
|
|
52
|
+
"advanced-fields.checkbox.options.checkboxOptions.label": "Checkbox Options",
|
|
53
|
+
"advanced-fields.checkbox.options.checkboxOptions.description": "Define available options for multiple checkboxes (one per line: value|label)",
|
|
54
|
+
"advanced-fields.checkbox.options.checkboxOptions.placeholder": "option1|Option 1\noption2|Option 2\noption3|Option 3",
|
|
55
|
+
"advanced-fields.checkbox.options.defaultSelected.label": "Default Selected",
|
|
56
|
+
"advanced-fields.checkbox.options.defaultSelected.description": "Pre-selected options for multiple checkboxes (one per line)",
|
|
57
|
+
"advanced-fields.checkbox.options.defaultSelected.placeholder": "option1\noption2",
|
|
58
|
+
"advanced-fields.checkbox.options.minChoices.label": "Minimum Choices",
|
|
59
|
+
"advanced-fields.checkbox.options.minChoices.description": "Minimum number of options that must be selected (0 for no minimum)",
|
|
60
|
+
"advanced-fields.checkbox.options.maxChoices.label": "Maximum Choices",
|
|
61
|
+
"advanced-fields.checkbox.options.maxChoices.description": "Maximum number of options that can be selected (0 for no maximum)",
|
|
62
|
+
"advanced-fields.checkbox.options.layout.label": "Layout Style",
|
|
63
|
+
"advanced-fields.checkbox.options.layout.description": "Visual layout of multiple checkbox options",
|
|
64
|
+
"advanced-fields.checkbox.options.layout.vertical": "Vertical Stack",
|
|
65
|
+
"advanced-fields.checkbox.options.layout.horizontal": "Horizontal Row",
|
|
66
|
+
"advanced-fields.checkbox.options.layout.grid": "Grid Layout",
|
|
67
|
+
"advanced-fields.radio.label": "Multiple Type Choice",
|
|
68
|
+
"advanced-fields.radio.description": "Select one or more options (radio style)",
|
|
69
|
+
"advanced-fields.radio.settings.base.label": "Basic settings",
|
|
70
|
+
"advanced-fields.radio.options.required.label": "Multiple Type Choice",
|
|
71
|
+
"advanced-fields.radio.options.required.description": "You won't be able to create an entry if this field is empty",
|
|
72
|
+
"advanced-fields.radio.options.radioOptions.label": "Multiple Type Choice",
|
|
73
|
+
"advanced-fields.radio.options.radioOptions.description": 'Enter one option per line in format: value|label (e.g., "1|Option 1")',
|
|
74
|
+
"advanced-fields.radio.options.radioOptions.placeholder": "1|Option 1\n2|Option 2\n3|Option 3",
|
|
75
|
+
"advanced-fields.radio.options.selectionType.label": "Selection Type",
|
|
76
|
+
"advanced-fields.radio.options.selectionType.description": "Allow selecting only one or multiple type choices",
|
|
77
|
+
"advanced-fields.radio.options.customErrorMessage.label": "Custom error message",
|
|
78
|
+
"advanced-fields.radio.options.customErrorMessage.description": "Display a custom error message when validation fails",
|
|
79
|
+
"advanced-fields.radio.options.customErrorMessage.placeholder": "Enter custom error message",
|
|
80
|
+
"advanced-fields.radio.no-options": "No options defined. Please configure this field in the content type settings.",
|
|
81
|
+
"advanced-fields.radio.options.type.single": "Single selection",
|
|
82
|
+
"advanced-fields.radio.options.type.multiple": "Multiple selection"
|
|
83
|
+
};
|
|
84
|
+
exports.default = en;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{
|
|
1
|
+
const en = {
|
|
2
2
|
"advanced-fields.plugin.name": "Advanced Fields",
|
|
3
3
|
"advanced-fields.input.label": "Advanced Input",
|
|
4
4
|
"advanced-fields.input.description": "Professional-grade text input with comprehensive validation and advanced configuration options",
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
"advanced-fields.checkbox.description": "Unified checkbox field supporting both single and multiple selections with advanced configuration",
|
|
7
7
|
"advanced-fields.selectinput.label": "Select Input",
|
|
8
8
|
"advanced-fields.selectinput.description": "Select an option",
|
|
9
|
-
|
|
10
9
|
"advanced-fields.input.settings.base.label": "Basic settings",
|
|
11
10
|
"advanced-fields.input.options.required.label": "Required field",
|
|
12
11
|
"advanced-fields.input.options.required.description": "You won't be able to create an entry if this field is empty",
|
|
@@ -18,7 +17,6 @@
|
|
|
18
17
|
"advanced-fields.input.options.minLength.description": "Set the minimum number of characters allowed",
|
|
19
18
|
"advanced-fields.input.options.defaultValue.label": "Default value",
|
|
20
19
|
"advanced-fields.input.options.defaultValue.description": "Set the default value for this field",
|
|
21
|
-
|
|
22
20
|
"advanced-fields.input.settings.advanced.label": "Advanced settings",
|
|
23
21
|
"advanced-fields.input.options.regex.label": "RegExp pattern",
|
|
24
22
|
"advanced-fields.input.options.regex.description": "The text of the regular expression",
|
|
@@ -28,7 +26,6 @@
|
|
|
28
26
|
"advanced-fields.input.options.private.description": "This field will not be included in API responses",
|
|
29
27
|
"advanced-fields.input.options.customErrorMessage.label": "Custom error message",
|
|
30
28
|
"advanced-fields.input.options.customErrorMessage.description": "Display a custom error message when validation fails",
|
|
31
|
-
|
|
32
29
|
"advanced-fields.checkbox.settings.base.label": "Basic settings",
|
|
33
30
|
"advanced-fields.checkbox.options.required.label": "Required field",
|
|
34
31
|
"advanced-fields.checkbox.options.required.description": "You won't be able to create an entry if this field is empty",
|
|
@@ -40,7 +37,6 @@
|
|
|
40
37
|
"advanced-fields.checkbox.options.defaultValue.description": "Pre-checked state for single checkbox",
|
|
41
38
|
"advanced-fields.checkbox.options.defaultValue.options.true": "Checked (True)",
|
|
42
39
|
"advanced-fields.checkbox.options.defaultValue.options.false": "Unchecked (False)",
|
|
43
|
-
|
|
44
40
|
"advanced-fields.checkbox.settings.advanced.label": "Advanced settings",
|
|
45
41
|
"advanced-fields.checkbox.options.customErrorMessage.label": "Custom error message",
|
|
46
42
|
"advanced-fields.checkbox.options.customErrorMessage.description": "Display a custom error message when validation fails",
|
|
@@ -66,14 +62,13 @@
|
|
|
66
62
|
"advanced-fields.checkbox.options.layout.vertical": "Vertical Stack",
|
|
67
63
|
"advanced-fields.checkbox.options.layout.horizontal": "Horizontal Row",
|
|
68
64
|
"advanced-fields.checkbox.options.layout.grid": "Grid Layout",
|
|
69
|
-
|
|
70
65
|
"advanced-fields.radio.label": "Multiple Type Choice",
|
|
71
66
|
"advanced-fields.radio.description": "Select one or more options (radio style)",
|
|
72
67
|
"advanced-fields.radio.settings.base.label": "Basic settings",
|
|
73
68
|
"advanced-fields.radio.options.required.label": "Multiple Type Choice",
|
|
74
69
|
"advanced-fields.radio.options.required.description": "You won't be able to create an entry if this field is empty",
|
|
75
70
|
"advanced-fields.radio.options.radioOptions.label": "Multiple Type Choice",
|
|
76
|
-
"advanced-fields.radio.options.radioOptions.description":
|
|
71
|
+
"advanced-fields.radio.options.radioOptions.description": 'Enter one option per line in format: value|label (e.g., "1|Option 1")',
|
|
77
72
|
"advanced-fields.radio.options.radioOptions.placeholder": "1|Option 1\n2|Option 2\n3|Option 3",
|
|
78
73
|
"advanced-fields.radio.options.selectionType.label": "Selection Type",
|
|
79
74
|
"advanced-fields.radio.options.selectionType.description": "Allow selecting only one or multiple type choices",
|
|
@@ -83,4 +78,7 @@
|
|
|
83
78
|
"advanced-fields.radio.no-options": "No options defined. Please configure this field in the content type settings.",
|
|
84
79
|
"advanced-fields.radio.options.type.single": "Single selection",
|
|
85
80
|
"advanced-fields.radio.options.type.multiple": "Multiple selection"
|
|
86
|
-
}
|
|
81
|
+
};
|
|
82
|
+
export {
|
|
83
|
+
en as default
|
|
84
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const reactIntl = require("react-intl");
|
|
5
|
+
const designSystem = require("@strapi/design-system");
|
|
6
|
+
const react = require("react");
|
|
7
|
+
const AdvancedInput = ({
|
|
8
|
+
attribute = {},
|
|
9
|
+
description = { id: "", defaultMessage: "" },
|
|
10
|
+
disabled,
|
|
11
|
+
error,
|
|
12
|
+
intlLabel = { id: "", defaultMessage: "" },
|
|
13
|
+
labelAction,
|
|
14
|
+
name,
|
|
15
|
+
onChange,
|
|
16
|
+
required,
|
|
17
|
+
value
|
|
18
|
+
}) => {
|
|
19
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
20
|
+
const [inputValue, setInputValue] = react.useState(value || "");
|
|
21
|
+
const [validationError, setValidationError] = react.useState(null);
|
|
22
|
+
const [hasInteracted, setHasInteracted] = react.useState(false);
|
|
23
|
+
const {
|
|
24
|
+
minLength = 0,
|
|
25
|
+
maxLength = 0,
|
|
26
|
+
min = 0,
|
|
27
|
+
max = 0,
|
|
28
|
+
step = 1,
|
|
29
|
+
rows = 4,
|
|
30
|
+
options = {}
|
|
31
|
+
} = attribute;
|
|
32
|
+
const {
|
|
33
|
+
placeholder = "",
|
|
34
|
+
defaultValue = "",
|
|
35
|
+
customErrorMessage = "",
|
|
36
|
+
regex = "",
|
|
37
|
+
fieldNote = ""
|
|
38
|
+
} = options;
|
|
39
|
+
const fieldNoteFromAttribute = attribute.options?.fieldNote || "";
|
|
40
|
+
react.useEffect(() => {
|
|
41
|
+
const initialValue = value === void 0 ? defaultValue : value;
|
|
42
|
+
setInputValue(initialValue);
|
|
43
|
+
if (error) {
|
|
44
|
+
setValidationError(error);
|
|
45
|
+
}
|
|
46
|
+
if (onChange) {
|
|
47
|
+
onChange({ target: { value: initialValue } });
|
|
48
|
+
}
|
|
49
|
+
}, [value, defaultValue, onChange, error]);
|
|
50
|
+
const validateInput = (val) => {
|
|
51
|
+
if (required && (!val || val.toString().trim().length === 0)) {
|
|
52
|
+
return customErrorMessage || "This field is required";
|
|
53
|
+
}
|
|
54
|
+
if (!val || val.toString().trim().length === 0) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const stringValue = val.toString().trim();
|
|
58
|
+
if (minLength > 0 && stringValue.length < minLength) {
|
|
59
|
+
return customErrorMessage || `Minimum length is ${minLength} characters`;
|
|
60
|
+
}
|
|
61
|
+
if (maxLength > 0 && stringValue.length > maxLength) {
|
|
62
|
+
return customErrorMessage || `Maximum length is ${maxLength} characters`;
|
|
63
|
+
}
|
|
64
|
+
if (regex && stringValue) {
|
|
65
|
+
try {
|
|
66
|
+
const regexPattern = new RegExp(regex);
|
|
67
|
+
if (!regexPattern.test(stringValue)) {
|
|
68
|
+
return customErrorMessage || "Invalid format";
|
|
69
|
+
}
|
|
70
|
+
} catch (e) {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
const handleChange = (e) => {
|
|
76
|
+
const newValue = e.target.value;
|
|
77
|
+
setInputValue(newValue);
|
|
78
|
+
setHasInteracted(true);
|
|
79
|
+
const error2 = validateInput(newValue);
|
|
80
|
+
setValidationError(error2);
|
|
81
|
+
if (onChange) {
|
|
82
|
+
onChange(e);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const displayError = error || hasInteracted && validationError;
|
|
86
|
+
const renderInput = () => {
|
|
87
|
+
const commonProps = {
|
|
88
|
+
name,
|
|
89
|
+
value: inputValue,
|
|
90
|
+
onChange: handleChange,
|
|
91
|
+
disabled,
|
|
92
|
+
placeholder: placeholder || (intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || "")
|
|
93
|
+
};
|
|
94
|
+
const inputStyle = {
|
|
95
|
+
width: "100%",
|
|
96
|
+
padding: "8px 12px",
|
|
97
|
+
border: `1px solid ${displayError ? "#d02b20" : "#dcdce4"}`,
|
|
98
|
+
borderRadius: "4px",
|
|
99
|
+
fontSize: "14px",
|
|
100
|
+
fontFamily: "inherit",
|
|
101
|
+
backgroundColor: disabled ? "#f6f6f9" : "#ffffff"
|
|
102
|
+
};
|
|
103
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
104
|
+
"input",
|
|
105
|
+
{
|
|
106
|
+
...commonProps,
|
|
107
|
+
type: "text",
|
|
108
|
+
style: inputStyle
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { col: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name, error: displayError, children: [
|
|
113
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Label, { children: [
|
|
114
|
+
intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || name,
|
|
115
|
+
required && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#d02b20", marginLeft: "4px" }, children: "*" })
|
|
116
|
+
] }),
|
|
117
|
+
renderInput(),
|
|
118
|
+
displayError && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, { children: displayError }),
|
|
119
|
+
description && (description.id || description.defaultMessage) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { children: description.id ? formatMessage(description) : description.defaultMessage }),
|
|
120
|
+
(fieldNote || fieldNoteFromAttribute) && /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
|
|
121
|
+
fontStyle: "italic",
|
|
122
|
+
color: "#666",
|
|
123
|
+
fontSize: "12px",
|
|
124
|
+
display: "block",
|
|
125
|
+
marginTop: "4px"
|
|
126
|
+
}, children: fieldNote || fieldNoteFromAttribute })
|
|
127
|
+
] }) });
|
|
128
|
+
};
|
|
129
|
+
exports.default = AdvancedInput;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useIntl } from "react-intl";
|
|
3
|
+
import { Box, Field, Flex } from "@strapi/design-system";
|
|
4
|
+
import { useState, useEffect } from "react";
|
|
5
|
+
const AdvancedRadio = ({
|
|
6
|
+
attribute = {},
|
|
7
|
+
description = { id: "", defaultMessage: "" },
|
|
8
|
+
disabled,
|
|
9
|
+
error,
|
|
10
|
+
intlLabel = { id: "", defaultMessage: "" },
|
|
11
|
+
labelAction,
|
|
12
|
+
name,
|
|
13
|
+
onChange,
|
|
14
|
+
required,
|
|
15
|
+
value
|
|
16
|
+
}) => {
|
|
17
|
+
const { formatMessage } = useIntl();
|
|
18
|
+
const [selectedValues, setSelectedValues] = useState([]);
|
|
19
|
+
const [validationError, setValidationError] = useState(null);
|
|
20
|
+
const [hasInteracted, setHasInteracted] = useState(false);
|
|
21
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
22
|
+
const {
|
|
23
|
+
selectionType = "single",
|
|
24
|
+
layout = "vertical",
|
|
25
|
+
minChoices = 0,
|
|
26
|
+
maxChoices = 0,
|
|
27
|
+
defaultSelected = "",
|
|
28
|
+
radioOptions = "",
|
|
29
|
+
customErrorMessage = "",
|
|
30
|
+
fieldNote = ""
|
|
31
|
+
} = attribute.options || attribute;
|
|
32
|
+
const fieldNoteFromAttribute = attribute.options?.fieldNote || "";
|
|
33
|
+
const options = radioOptions.split("\n").filter((opt) => opt.trim()).map((opt) => {
|
|
34
|
+
const [value2, label] = opt.split("|");
|
|
35
|
+
return { value: value2?.trim() || "", label: label?.trim() || value2?.trim() || "" };
|
|
36
|
+
});
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
let initialValues = [];
|
|
39
|
+
if (value && Array.isArray(value)) {
|
|
40
|
+
initialValues = value;
|
|
41
|
+
} else if (value && typeof value === "string") {
|
|
42
|
+
initialValues = value.split(",").map((v) => v.trim()).filter((v) => v);
|
|
43
|
+
} else if (defaultSelected) {
|
|
44
|
+
initialValues = defaultSelected.split(",").map((v) => v.trim()).filter((v) => v);
|
|
45
|
+
}
|
|
46
|
+
setSelectedValues(initialValues);
|
|
47
|
+
const validationResult = validateSelection(initialValues);
|
|
48
|
+
setValidationError(validationResult);
|
|
49
|
+
if (onChange && initialValues.length > 0 && (!value || Array.isArray(value) && value.length === 0) && !isInitialized) {
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
onChange({
|
|
52
|
+
target: {
|
|
53
|
+
value: initialValues,
|
|
54
|
+
name,
|
|
55
|
+
id: name
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
setIsInitialized(true);
|
|
59
|
+
}, 0);
|
|
60
|
+
} else if (!isInitialized) {
|
|
61
|
+
setIsInitialized(true);
|
|
62
|
+
}
|
|
63
|
+
}, [value, defaultSelected, onChange, error]);
|
|
64
|
+
const validateSelection = (values) => {
|
|
65
|
+
const valArray = Array.isArray(values) ? values : [];
|
|
66
|
+
if (required && valArray.length === 0) {
|
|
67
|
+
return customErrorMessage || "This field is required";
|
|
68
|
+
}
|
|
69
|
+
if (valArray.length === 0) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
if (selectionType === "multiple") {
|
|
73
|
+
if (minChoices > 0 && valArray.length < minChoices) {
|
|
74
|
+
return customErrorMessage || `Please select at least ${minChoices} option${minChoices > 1 ? "s" : ""}`;
|
|
75
|
+
}
|
|
76
|
+
if (maxChoices > 0 && valArray.length > maxChoices) {
|
|
77
|
+
return customErrorMessage || `Please select at most ${maxChoices} option${maxChoices > 1 ? "s" : ""}`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
};
|
|
82
|
+
const handleRadioChange = (optionValue, isChecked) => {
|
|
83
|
+
let newValues;
|
|
84
|
+
if (selectionType === "single") {
|
|
85
|
+
newValues = isChecked ? [optionValue] : [];
|
|
86
|
+
} else {
|
|
87
|
+
if (isChecked) {
|
|
88
|
+
newValues = [...selectedValues, optionValue];
|
|
89
|
+
} else {
|
|
90
|
+
newValues = selectedValues.filter((val) => val !== optionValue);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
setSelectedValues(newValues);
|
|
94
|
+
setHasInteracted(true);
|
|
95
|
+
const error2 = validateSelection(newValues);
|
|
96
|
+
setValidationError(error2);
|
|
97
|
+
if (onChange) {
|
|
98
|
+
const event = {
|
|
99
|
+
target: {
|
|
100
|
+
name,
|
|
101
|
+
id: name,
|
|
102
|
+
value: newValues
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
onChange(event);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const displayError = error || hasInteracted && validationError;
|
|
109
|
+
const renderRadios = () => {
|
|
110
|
+
const radioStyle = {
|
|
111
|
+
display: "flex",
|
|
112
|
+
alignItems: "center",
|
|
113
|
+
gap: "8px",
|
|
114
|
+
padding: "4px 0"
|
|
115
|
+
};
|
|
116
|
+
const radioInputStyle = {
|
|
117
|
+
width: "16px",
|
|
118
|
+
height: "16px",
|
|
119
|
+
accentColor: "#4945ff",
|
|
120
|
+
margin: "0",
|
|
121
|
+
padding: "0",
|
|
122
|
+
opacity: "1",
|
|
123
|
+
visibility: "visible",
|
|
124
|
+
display: "block",
|
|
125
|
+
position: "relative",
|
|
126
|
+
zIndex: "1",
|
|
127
|
+
cursor: "pointer",
|
|
128
|
+
border: "2px solid #dcdce4",
|
|
129
|
+
borderRadius: "50%",
|
|
130
|
+
backgroundColor: "white",
|
|
131
|
+
transition: "all 0.2s ease"
|
|
132
|
+
};
|
|
133
|
+
const customRadioStyle = {
|
|
134
|
+
width: "16px",
|
|
135
|
+
height: "16px",
|
|
136
|
+
borderRadius: "50%",
|
|
137
|
+
borderWidth: "1px",
|
|
138
|
+
borderStyle: "solid",
|
|
139
|
+
backgroundColor: "#ffffff",
|
|
140
|
+
cursor: "pointer",
|
|
141
|
+
transition: "all 0.2s ease",
|
|
142
|
+
position: "relative",
|
|
143
|
+
display: "flex",
|
|
144
|
+
alignItems: "center",
|
|
145
|
+
justifyContent: "center"
|
|
146
|
+
};
|
|
147
|
+
const customRadioCheckedStyle = {
|
|
148
|
+
...customRadioStyle,
|
|
149
|
+
backgroundColor: "#ffffff",
|
|
150
|
+
borderColor: "#4945ff"
|
|
151
|
+
};
|
|
152
|
+
const customRadioDotStyle = {
|
|
153
|
+
width: "10px",
|
|
154
|
+
height: "10px",
|
|
155
|
+
borderRadius: "50%",
|
|
156
|
+
backgroundColor: "#4945ff"
|
|
157
|
+
};
|
|
158
|
+
const radioLabelStyle = {
|
|
159
|
+
fontSize: "14px",
|
|
160
|
+
fontFamily: "inherit",
|
|
161
|
+
cursor: "pointer",
|
|
162
|
+
userSelect: "none",
|
|
163
|
+
color: "#32324d",
|
|
164
|
+
fontWeight: "400",
|
|
165
|
+
lineHeight: "1.5",
|
|
166
|
+
marginLeft: "4px"
|
|
167
|
+
};
|
|
168
|
+
if (!options || options.length === 0) {
|
|
169
|
+
return /* @__PURE__ */ jsx("div", { style: { padding: "8px", color: "#666", fontStyle: "italic" }, children: formatMessage({
|
|
170
|
+
id: "advanced-fields.radio.no-options",
|
|
171
|
+
defaultMessage: "No options defined. Please configure this field in the content type settings."
|
|
172
|
+
}) });
|
|
173
|
+
}
|
|
174
|
+
if (layout === "horizontal") {
|
|
175
|
+
return /* @__PURE__ */ jsx(Flex, { wrap: "wrap", gap: 2, children: options.map((option) => /* @__PURE__ */ jsxs("div", { style: radioStyle, children: [
|
|
176
|
+
selectionType === "multiple" ? (
|
|
177
|
+
// Custom radio button appearance for multiple selection
|
|
178
|
+
/* @__PURE__ */ jsx(
|
|
179
|
+
"div",
|
|
180
|
+
{
|
|
181
|
+
style: selectedValues.includes(option.value) ? customRadioCheckedStyle : customRadioStyle,
|
|
182
|
+
onClick: () => handleRadioChange(option.value, !selectedValues.includes(option.value)),
|
|
183
|
+
children: selectedValues.includes(option.value) && /* @__PURE__ */ jsx("div", { style: customRadioDotStyle })
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
) : (
|
|
187
|
+
// Regular radio button for single selection
|
|
188
|
+
/* @__PURE__ */ jsx(
|
|
189
|
+
"input",
|
|
190
|
+
{
|
|
191
|
+
type: "radio",
|
|
192
|
+
id: `${name}-${option.value}`,
|
|
193
|
+
name,
|
|
194
|
+
checked: selectedValues.includes(option.value),
|
|
195
|
+
onChange: (e) => handleRadioChange(option.value, e.target.checked),
|
|
196
|
+
disabled,
|
|
197
|
+
style: radioInputStyle
|
|
198
|
+
}
|
|
199
|
+
)
|
|
200
|
+
),
|
|
201
|
+
/* @__PURE__ */ jsx(
|
|
202
|
+
"label",
|
|
203
|
+
{
|
|
204
|
+
htmlFor: selectionType === "single" ? `${name}-${option.value}` : void 0,
|
|
205
|
+
style: radioLabelStyle,
|
|
206
|
+
onClick: selectionType === "multiple" ? () => handleRadioChange(option.value, !selectedValues.includes(option.value)) : void 0,
|
|
207
|
+
children: option.label
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
] }, option.value)) });
|
|
211
|
+
}
|
|
212
|
+
return /* @__PURE__ */ jsx("div", { children: options.map((option) => /* @__PURE__ */ jsxs("div", { style: radioStyle, children: [
|
|
213
|
+
selectionType === "multiple" ? (
|
|
214
|
+
// Custom radio button appearance for multiple selection
|
|
215
|
+
/* @__PURE__ */ jsx(
|
|
216
|
+
"div",
|
|
217
|
+
{
|
|
218
|
+
style: selectedValues.includes(option.value) ? customRadioCheckedStyle : customRadioStyle,
|
|
219
|
+
onClick: () => handleRadioChange(option.value, !selectedValues.includes(option.value)),
|
|
220
|
+
children: selectedValues.includes(option.value) && /* @__PURE__ */ jsx("div", { style: customRadioDotStyle })
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
) : (
|
|
224
|
+
// Regular radio button for single selection
|
|
225
|
+
/* @__PURE__ */ jsx(
|
|
226
|
+
"input",
|
|
227
|
+
{
|
|
228
|
+
type: "radio",
|
|
229
|
+
id: `${name}-${option.value}`,
|
|
230
|
+
name,
|
|
231
|
+
checked: selectedValues.includes(option.value),
|
|
232
|
+
onChange: (e) => handleRadioChange(option.value, e.target.checked),
|
|
233
|
+
disabled,
|
|
234
|
+
style: radioInputStyle
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
),
|
|
238
|
+
/* @__PURE__ */ jsx(
|
|
239
|
+
"label",
|
|
240
|
+
{
|
|
241
|
+
htmlFor: selectionType === "single" ? `${name}-${option.value}` : void 0,
|
|
242
|
+
style: radioLabelStyle,
|
|
243
|
+
onClick: selectionType === "multiple" ? () => handleRadioChange(option.value, !selectedValues.includes(option.value)) : void 0,
|
|
244
|
+
children: option.label
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
] }, option.value)) });
|
|
248
|
+
};
|
|
249
|
+
return /* @__PURE__ */ jsx(Box, { col: 6, children: /* @__PURE__ */ jsxs(Field.Root, { name, error: displayError, children: [
|
|
250
|
+
/* @__PURE__ */ jsxs(Field.Label, { children: [
|
|
251
|
+
intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || name,
|
|
252
|
+
required && /* @__PURE__ */ jsx("span", { style: { color: "#d02b20", marginLeft: "4px" }, children: "*" })
|
|
253
|
+
] }),
|
|
254
|
+
renderRadios(),
|
|
255
|
+
displayError && /* @__PURE__ */ jsx(Field.Error, { children: displayError }),
|
|
256
|
+
description && (description.id || description.defaultMessage) && /* @__PURE__ */ jsx(Field.Hint, { children: description.id ? formatMessage(description) : description.defaultMessage }),
|
|
257
|
+
(fieldNote || fieldNoteFromAttribute) && /* @__PURE__ */ jsx("span", { style: {
|
|
258
|
+
fontStyle: "italic",
|
|
259
|
+
color: "#666",
|
|
260
|
+
fontSize: "12px",
|
|
261
|
+
display: "block",
|
|
262
|
+
marginTop: "4px"
|
|
263
|
+
}, children: fieldNote || fieldNoteFromAttribute })
|
|
264
|
+
] }) });
|
|
265
|
+
};
|
|
266
|
+
export {
|
|
267
|
+
AdvancedRadio as default
|
|
268
|
+
};
|