@webbycrown/advanced-fields 1.0.6 → 1.0.9

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.
@@ -0,0 +1,212 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { useIntl } from "react-intl";
3
+ import { Box, Field, MultiSelect, MultiSelectOption } from "@strapi/design-system";
4
+ import { useState, useEffect } from "react";
5
+ const AdvancedEnumeration = ({
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 {
19
+ enumOptions = "",
20
+ minChoices = 0,
21
+ maxChoices = 0,
22
+ defaultSelected = "",
23
+ customErrorMessage = "",
24
+ fieldNote = ""
25
+ } = attribute.options || attribute;
26
+ const fieldNoteFromAttribute = attribute.options?.fieldNote || "";
27
+ const options = enumOptions.split("\n").filter((opt) => opt.trim()).map((opt) => {
28
+ const parts = opt.split("|");
29
+ const optionValue = parts[0]?.trim() || "";
30
+ const label = parts[1]?.trim() || optionValue;
31
+ return { value: optionValue, label };
32
+ }).filter((opt) => opt.value);
33
+ const getInitialValues = () => {
34
+ if (value && Array.isArray(value) && value.length > 0) {
35
+ return value.map(String);
36
+ } else if (value && typeof value === "string" && value.trim()) {
37
+ return value.split(",").map((v) => v.trim()).filter((v) => v);
38
+ } else if (defaultSelected && typeof defaultSelected === "string" && defaultSelected.trim()) {
39
+ return defaultSelected.split("\n").map((v) => v.trim()).filter((v) => v);
40
+ }
41
+ return [];
42
+ };
43
+ const [fieldValue, setFieldValue] = useState(getInitialValues);
44
+ const [validationError, setValidationError] = useState(null);
45
+ const [hasInteracted, setHasInteracted] = useState(false);
46
+ const [isInitialized, setIsInitialized] = useState(false);
47
+ const validateSelection = (val) => {
48
+ const values = Array.isArray(val) ? val : [];
49
+ if (required && values.length === 0) {
50
+ return customErrorMessage || "This field is required";
51
+ }
52
+ if (values.length === 0) return null;
53
+ if (minChoices > 0 && values.length < Number(minChoices)) {
54
+ return customErrorMessage || `Please select at least ${minChoices} option${Number(minChoices) > 1 ? "s" : ""}`;
55
+ }
56
+ if (maxChoices > 0 && values.length > Number(maxChoices)) {
57
+ return customErrorMessage || `Please select at most ${maxChoices} option${Number(maxChoices) > 1 ? "s" : ""}`;
58
+ }
59
+ return null;
60
+ };
61
+ useEffect(() => {
62
+ const initialValues = getInitialValues();
63
+ setFieldValue(initialValues);
64
+ const validationResult = validateSelection(initialValues);
65
+ setValidationError(validationResult);
66
+ if (onChange && initialValues.length > 0 && (!value || Array.isArray(value) && value.length === 0) && !isInitialized) {
67
+ setTimeout(() => {
68
+ onChange({
69
+ target: {
70
+ value: initialValues,
71
+ name,
72
+ id: name
73
+ }
74
+ });
75
+ setIsInitialized(true);
76
+ }, 0);
77
+ } else if (!isInitialized) {
78
+ setIsInitialized(true);
79
+ }
80
+ }, [value, defaultSelected, onChange, error]);
81
+ useEffect(() => {
82
+ if (value && Array.isArray(value) && value.length > 0) {
83
+ setFieldValue(value.map(String));
84
+ const validationResult = validateSelection(value);
85
+ setValidationError(validationResult);
86
+ }
87
+ }, [value]);
88
+ const handleChange = (newValues) => {
89
+ setFieldValue(newValues);
90
+ setHasInteracted(true);
91
+ const errMsg = validateSelection(newValues);
92
+ setValidationError(errMsg);
93
+ if (onChange) {
94
+ onChange({
95
+ target: {
96
+ value: newValues,
97
+ name,
98
+ id: name
99
+ }
100
+ });
101
+ }
102
+ };
103
+ const displayError = error || hasInteracted && validationError;
104
+ const buildConstraintHint = () => {
105
+ const min = Number(minChoices);
106
+ const max = Number(maxChoices);
107
+ if (min > 0 && max > 0) {
108
+ return `Select between ${min} and ${max} options`;
109
+ } else if (min > 0) {
110
+ return `Select at least ${min} option${min > 1 ? "s" : ""}`;
111
+ } else if (max > 0) {
112
+ return `Select at most ${max} option${max > 1 ? "s" : ""}`;
113
+ }
114
+ return null;
115
+ };
116
+ const constraintHint = buildConstraintHint();
117
+ const renderContent = () => {
118
+ if (!options || options.length === 0) {
119
+ return /* @__PURE__ */ jsx(
120
+ "div",
121
+ {
122
+ style: {
123
+ padding: "10px 12px",
124
+ color: "#8e8ea9",
125
+ fontStyle: "italic",
126
+ fontSize: "14px",
127
+ backgroundColor: "#f6f6f9",
128
+ borderRadius: "4px",
129
+ border: "1px dashed #dcdce4"
130
+ },
131
+ children: formatMessage({
132
+ id: "advanced-fields.enumeration.no-options",
133
+ defaultMessage: "No options defined. Please configure this field in the content type settings (one per line: value|label)."
134
+ })
135
+ }
136
+ );
137
+ }
138
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
139
+ /* @__PURE__ */ jsx(
140
+ MultiSelect,
141
+ {
142
+ id: name,
143
+ name,
144
+ placeholder: formatMessage({
145
+ id: "advanced-fields.enumeration.placeholder",
146
+ defaultMessage: "Select one or more options..."
147
+ }),
148
+ onChange: handleChange,
149
+ value: fieldValue,
150
+ disabled,
151
+ hasError: !!displayError,
152
+ withTags: true,
153
+ children: options.map((option) => /* @__PURE__ */ jsx(MultiSelectOption, { value: option.value, children: option.label }, option.value))
154
+ }
155
+ ),
156
+ fieldValue.length > 0 && /* @__PURE__ */ jsxs(
157
+ "div",
158
+ {
159
+ style: {
160
+ marginTop: "4px",
161
+ fontSize: "12px",
162
+ color: "#666687",
163
+ textAlign: "right"
164
+ },
165
+ children: [
166
+ fieldValue.length,
167
+ " of ",
168
+ options.length,
169
+ " selected",
170
+ Number(maxChoices) > 0 && ` (max ${maxChoices})`
171
+ ]
172
+ }
173
+ )
174
+ ] });
175
+ };
176
+ return /* @__PURE__ */ jsx(Box, { col: 6, children: /* @__PURE__ */ jsxs(Field.Root, { name, error: displayError, children: [
177
+ /* @__PURE__ */ jsxs(Field.Label, { children: [
178
+ intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || name,
179
+ required && /* @__PURE__ */ jsx("span", { style: { color: "#d02b20", marginLeft: "4px" }, children: "*" })
180
+ ] }),
181
+ renderContent(),
182
+ displayError && /* @__PURE__ */ jsx(Field.Error, { children: displayError }),
183
+ constraintHint && !displayError && /* @__PURE__ */ jsx(
184
+ "div",
185
+ {
186
+ style: {
187
+ fontSize: "12px",
188
+ color: "#666687",
189
+ marginTop: "4px"
190
+ },
191
+ children: constraintHint
192
+ }
193
+ ),
194
+ description && (description.id || description.defaultMessage) && /* @__PURE__ */ jsx(Field.Hint, { children: description.id ? formatMessage(description) : description.defaultMessage }),
195
+ (fieldNote || fieldNoteFromAttribute) && /* @__PURE__ */ jsx(
196
+ "span",
197
+ {
198
+ style: {
199
+ fontStyle: "italic",
200
+ color: "#666",
201
+ fontSize: "12px",
202
+ display: "block",
203
+ marginTop: "4px"
204
+ },
205
+ children: fieldNote || fieldNoteFromAttribute
206
+ }
207
+ )
208
+ ] }) });
209
+ };
210
+ export {
211
+ AdvancedEnumeration as default
212
+ };