@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.
@@ -0,0 +1,282 @@
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 AdvancedCheckbox = ({
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 {
21
+ checkboxType = "multiple",
22
+ layout = "vertical",
23
+ minChoices = 0,
24
+ maxChoices = 0,
25
+ defaultSelected = "",
26
+ checkboxOptions = "",
27
+ customErrorMessage = "",
28
+ fieldNote = ""
29
+ } = attribute.options || attribute;
30
+ const fieldNoteFromAttribute = attribute.options?.fieldNote || "";
31
+ const getInitialValues = () => {
32
+ if (checkboxType === "single") {
33
+ if (value && Array.isArray(value) && value.length > 0) {
34
+ return value;
35
+ } else if (value && typeof value === "string" && value.trim()) {
36
+ return [value.trim()];
37
+ } else if (defaultSelected && typeof defaultSelected === "string" && defaultSelected.trim()) {
38
+ return [defaultSelected.trim()];
39
+ } else if (defaultSelected && Array.isArray(defaultSelected) && defaultSelected.length > 0) {
40
+ return defaultSelected;
41
+ }
42
+ return [];
43
+ } else {
44
+ if (value && Array.isArray(value)) {
45
+ return value;
46
+ } else if (value && typeof value === "string") {
47
+ return value.split(",").map((v) => v.trim()).filter((v) => v);
48
+ } else if (defaultSelected) {
49
+ return defaultSelected.split(",").map((v) => v.trim()).filter((v) => v);
50
+ }
51
+ return [];
52
+ }
53
+ };
54
+ const [fieldValue, setFieldValue] = react.useState(getInitialValues);
55
+ const [validationError, setValidationError] = react.useState(null);
56
+ const [hasInteracted, setHasInteracted] = react.useState(false);
57
+ const [isInitialized, setIsInitialized] = react.useState(false);
58
+ const options = checkboxOptions.split("\n").filter((opt) => opt.trim()).map((opt) => {
59
+ const [value2, label] = opt.split("|");
60
+ return { value: value2?.trim() || "", label: label?.trim() || value2?.trim() || "" };
61
+ });
62
+ react.useEffect(() => {
63
+ const initialValues = getInitialValues();
64
+ setFieldValue(initialValues);
65
+ const validationResult = validateSelection(initialValues);
66
+ setValidationError(validationResult);
67
+ if (onChange && initialValues.length > 0 && (!value || Array.isArray(value) && value.length === 0) && !isInitialized) {
68
+ setTimeout(() => {
69
+ onChange({
70
+ target: {
71
+ value: initialValues,
72
+ name,
73
+ id: name
74
+ }
75
+ });
76
+ setIsInitialized(true);
77
+ }, 0);
78
+ } else if (!isInitialized) {
79
+ setIsInitialized(true);
80
+ }
81
+ }, []);
82
+ react.useEffect(() => {
83
+ if (value && Array.isArray(value) && value.length > 0) {
84
+ setFieldValue(value);
85
+ const validationResult = validateSelection(value);
86
+ setValidationError(validationResult);
87
+ }
88
+ }, [value]);
89
+ const validateSelection = (val) => {
90
+ const values = Array.isArray(val) ? val : [];
91
+ if (required && values.length === 0) {
92
+ return customErrorMessage || "This field is required";
93
+ }
94
+ if (values.length === 0) {
95
+ return null;
96
+ }
97
+ if (checkboxType === "multiple") {
98
+ if (minChoices > 0 && values.length < minChoices) {
99
+ return customErrorMessage || `Please select at least ${minChoices} option${minChoices > 1 ? "s" : ""}`;
100
+ }
101
+ if (maxChoices > 0 && values.length > maxChoices) {
102
+ return customErrorMessage || `Please select at most ${maxChoices} option${maxChoices > 1 ? "s" : ""}`;
103
+ }
104
+ }
105
+ return null;
106
+ };
107
+ const handleCheckboxChange = (optionValue, isChecked) => {
108
+ let newValue;
109
+ if (checkboxType === "single") {
110
+ if (isChecked) {
111
+ newValue = [optionValue];
112
+ } else {
113
+ newValue = [];
114
+ }
115
+ } else {
116
+ if (isChecked) {
117
+ newValue = [...fieldValue, optionValue];
118
+ } else {
119
+ newValue = fieldValue.filter((val) => val !== optionValue);
120
+ }
121
+ }
122
+ setFieldValue(newValue);
123
+ setHasInteracted(true);
124
+ const error2 = validateSelection(newValue);
125
+ setValidationError(error2);
126
+ if (onChange) {
127
+ onChange({
128
+ target: {
129
+ value: newValue,
130
+ name,
131
+ id: name
132
+ }
133
+ });
134
+ }
135
+ };
136
+ const displayError = error || hasInteracted && validationError;
137
+ const renderCheckboxes = () => {
138
+ const checkboxStyle = {
139
+ display: "flex",
140
+ alignItems: "center",
141
+ gap: "8px",
142
+ marginBottom: "8px"
143
+ };
144
+ const checkboxInputStyle = {
145
+ width: "16px",
146
+ height: "16px",
147
+ accentColor: "#4945ff",
148
+ margin: "0",
149
+ padding: "0",
150
+ opacity: "1",
151
+ visibility: "visible",
152
+ display: "block",
153
+ position: "relative",
154
+ zIndex: "1"
155
+ };
156
+ const checkboxLabelStyle = {
157
+ fontSize: "14px",
158
+ fontFamily: "inherit",
159
+ cursor: "pointer",
160
+ userSelect: "none"
161
+ };
162
+ if (checkboxType === "single") {
163
+ if (!options || options.length === 0) {
164
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "8px", color: "#666", fontStyle: "italic" }, children: formatMessage({
165
+ id: "advanced-fields.checkbox.options.checkboxOptions.description",
166
+ defaultMessage: "Define available options for multiple checkboxes (one per line: value|label)"
167
+ }) });
168
+ }
169
+ if (layout === "horizontal") {
170
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { wrap: "wrap", gap: 2, children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: checkboxStyle, children: [
171
+ /* @__PURE__ */ jsxRuntime.jsx(
172
+ "input",
173
+ {
174
+ type: "checkbox",
175
+ id: `${name}-${option.value}`,
176
+ checked: fieldValue.includes(option.value),
177
+ onChange: (e) => handleCheckboxChange(option.value, e.target.checked),
178
+ disabled,
179
+ style: checkboxInputStyle
180
+ }
181
+ ),
182
+ /* @__PURE__ */ jsxRuntime.jsx(
183
+ "label",
184
+ {
185
+ htmlFor: `${name}-${option.value}`,
186
+ style: checkboxLabelStyle,
187
+ children: option.label
188
+ }
189
+ )
190
+ ] }, option.value)) });
191
+ }
192
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: checkboxStyle, children: [
193
+ /* @__PURE__ */ jsxRuntime.jsx(
194
+ "input",
195
+ {
196
+ type: "checkbox",
197
+ id: `${name}-${option.value}`,
198
+ checked: fieldValue.includes(option.value),
199
+ onChange: (e) => handleCheckboxChange(option.value, e.target.checked),
200
+ disabled,
201
+ style: checkboxInputStyle
202
+ }
203
+ ),
204
+ /* @__PURE__ */ jsxRuntime.jsx(
205
+ "label",
206
+ {
207
+ htmlFor: `${name}-${option.value}`,
208
+ style: checkboxLabelStyle,
209
+ children: option.label
210
+ }
211
+ )
212
+ ] }, option.value)) });
213
+ }
214
+ if (!options || options.length === 0) {
215
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "8px", color: "#666", fontStyle: "italic" }, children: formatMessage({
216
+ id: "advanced-fields.checkbox.options.checkboxOptions.description",
217
+ defaultMessage: "Define available options for multiple checkboxes (one per line: value|label)"
218
+ }) });
219
+ }
220
+ if (layout === "horizontal") {
221
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { wrap: "wrap", gap: 2, children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: checkboxStyle, children: [
222
+ /* @__PURE__ */ jsxRuntime.jsx(
223
+ "input",
224
+ {
225
+ type: "checkbox",
226
+ id: `${name}-${option.value}`,
227
+ checked: fieldValue.includes(option.value),
228
+ onChange: (e) => handleCheckboxChange(option.value, e.target.checked),
229
+ disabled,
230
+ style: checkboxInputStyle
231
+ }
232
+ ),
233
+ /* @__PURE__ */ jsxRuntime.jsx(
234
+ "label",
235
+ {
236
+ htmlFor: `${name}-${option.value}`,
237
+ style: checkboxLabelStyle,
238
+ children: option.label
239
+ }
240
+ )
241
+ ] }, option.value)) });
242
+ }
243
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: checkboxStyle, children: [
244
+ /* @__PURE__ */ jsxRuntime.jsx(
245
+ "input",
246
+ {
247
+ type: "checkbox",
248
+ id: `${name}-${option.value}`,
249
+ checked: fieldValue.includes(option.value),
250
+ onChange: (e) => handleCheckboxChange(option.value, e.target.checked),
251
+ disabled,
252
+ style: checkboxInputStyle
253
+ }
254
+ ),
255
+ /* @__PURE__ */ jsxRuntime.jsx(
256
+ "label",
257
+ {
258
+ htmlFor: `${name}-${option.value}`,
259
+ style: checkboxLabelStyle,
260
+ children: option.label
261
+ }
262
+ )
263
+ ] }, option.value)) });
264
+ };
265
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { col: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name, error: displayError, children: [
266
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Label, { children: [
267
+ intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || name,
268
+ required && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#d02b20", marginLeft: "4px" }, children: "*" })
269
+ ] }),
270
+ renderCheckboxes(),
271
+ displayError && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, { children: displayError }),
272
+ description && (description.id || description.defaultMessage) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { children: description.id ? formatMessage(description) : description.defaultMessage }),
273
+ (fieldNote || fieldNoteFromAttribute) && /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
274
+ fontStyle: "italic",
275
+ color: "#666",
276
+ fontSize: "12px",
277
+ display: "block",
278
+ marginTop: "4px"
279
+ }, children: fieldNote || fieldNoteFromAttribute })
280
+ ] }) });
281
+ };
282
+ exports.default = AdvancedCheckbox;
@@ -1,10 +1,7 @@
1
- "use client";
2
-
1
+ import { jsx, jsxs } from "react/jsx-runtime";
3
2
  import { useIntl } from "react-intl";
4
- import { Field, Box } from "@strapi/design-system";
5
- import { Cross } from "@strapi/icons";
3
+ import { Box, Field } from "@strapi/design-system";
6
4
  import { useState, useEffect } from "react";
7
-
8
5
  const AdvancedInput = ({
9
6
  attribute = {},
10
7
  description = { id: "", defaultMessage: "" },
@@ -15,13 +12,12 @@ const AdvancedInput = ({
15
12
  name,
16
13
  onChange,
17
14
  required,
18
- value,
15
+ value
19
16
  }) => {
20
17
  const { formatMessage } = useIntl();
21
18
  const [inputValue, setInputValue] = useState(value || "");
22
19
  const [validationError, setValidationError] = useState(null);
23
20
  const [hasInteracted, setHasInteracted] = useState(false);
24
-
25
21
  const {
26
22
  minLength = 0,
27
23
  maxLength = 0,
@@ -29,60 +25,40 @@ const AdvancedInput = ({
29
25
  max = 0,
30
26
  step = 1,
31
27
  rows = 4,
32
- options = {},
28
+ options = {}
33
29
  } = attribute;
34
-
35
- // Extract options with defaults
36
30
  const {
37
- placeholder = '',
38
- defaultValue = '',
39
- customErrorMessage = '',
40
- regex = '',
41
- fieldNote = '',
31
+ placeholder = "",
32
+ defaultValue = "",
33
+ customErrorMessage = "",
34
+ regex = "",
35
+ fieldNote = ""
42
36
  } = options;
43
-
44
- // Also check attribute.options for fieldNote
45
- const fieldNoteFromAttribute = attribute.options?.fieldNote || '';
46
-
47
-
48
- // Initialize input value
37
+ const fieldNoteFromAttribute = attribute.options?.fieldNote || "";
49
38
  useEffect(() => {
50
- const initialValue = value === undefined ? defaultValue : value;
39
+ const initialValue = value === void 0 ? defaultValue : value;
51
40
  setInputValue(initialValue);
52
-
53
- // Don't validate on initial load unless there's an error from Strapi
54
41
  if (error) {
55
42
  setValidationError(error);
56
43
  }
57
-
58
44
  if (onChange) {
59
45
  onChange({ target: { value: initialValue } });
60
46
  }
61
47
  }, [value, defaultValue, onChange, error]);
62
-
63
- // Validation function - this should match server-side validation
64
48
  const validateInput = (val) => {
65
- // Check required validation first
66
49
  if (required && (!val || val.toString().trim().length === 0)) {
67
50
  return customErrorMessage || "This field is required";
68
51
  }
69
-
70
- // If no value, no additional validation needed
71
52
  if (!val || val.toString().trim().length === 0) {
72
53
  return null;
73
54
  }
74
-
75
55
  const stringValue = val.toString().trim();
76
-
77
- // Check min/max length validation
78
56
  if (minLength > 0 && stringValue.length < minLength) {
79
57
  return customErrorMessage || `Minimum length is ${minLength} characters`;
80
58
  }
81
59
  if (maxLength > 0 && stringValue.length > maxLength) {
82
60
  return customErrorMessage || `Maximum length is ${maxLength} characters`;
83
61
  }
84
-
85
- // Check regex validation if provided
86
62
  if (regex && stringValue) {
87
63
  try {
88
64
  const regexPattern = new RegExp(regex);
@@ -90,39 +66,29 @@ const AdvancedInput = ({
90
66
  return customErrorMessage || "Invalid format";
91
67
  }
92
68
  } catch (e) {
93
- // Invalid regex pattern, skip validation
94
69
  }
95
70
  }
96
-
97
71
  return null;
98
72
  };
99
-
100
73
  const handleChange = (e) => {
101
74
  const newValue = e.target.value;
102
75
  setInputValue(newValue);
103
76
  setHasInteracted(true);
104
-
105
- // Validate input only after user interaction
106
- const error = validateInput(newValue);
107
- setValidationError(error);
108
-
77
+ const error2 = validateInput(newValue);
78
+ setValidationError(error2);
109
79
  if (onChange) {
110
80
  onChange(e);
111
81
  }
112
82
  };
113
-
114
- // Show validation error - prioritize Strapi's error, then our validation only after user interaction
115
- const displayError = error || (hasInteracted && validationError);
116
-
83
+ const displayError = error || hasInteracted && validationError;
117
84
  const renderInput = () => {
118
85
  const commonProps = {
119
86
  name,
120
87
  value: inputValue,
121
88
  onChange: handleChange,
122
89
  disabled,
123
- placeholder: placeholder || (intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || ""),
90
+ placeholder: placeholder || (intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || "")
124
91
  };
125
-
126
92
  const inputStyle = {
127
93
  width: "100%",
128
94
  padding: "8px 12px",
@@ -130,50 +96,34 @@ const AdvancedInput = ({
130
96
  borderRadius: "4px",
131
97
  fontSize: "14px",
132
98
  fontFamily: "inherit",
133
- backgroundColor: disabled ? "#f6f6f9" : "#ffffff",
99
+ backgroundColor: disabled ? "#f6f6f9" : "#ffffff"
134
100
  };
135
-
136
- return (
137
- <input
138
- {...commonProps}
139
- type="text"
140
- style={inputStyle}
141
- />
101
+ return /* @__PURE__ */ jsx(
102
+ "input",
103
+ {
104
+ ...commonProps,
105
+ type: "text",
106
+ style: inputStyle
107
+ }
142
108
  );
143
109
  };
144
-
145
- return (
146
- <Box col={6}>
147
- <Field.Root name={name} error={displayError}>
148
- <Field.Label>
149
- {intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || name}
150
- {required && <span style={{ color: "#d02b20", marginLeft: "4px" }}>*</span>}
151
- </Field.Label>
152
- {renderInput()}
153
- {displayError && (
154
- <Field.Error>
155
- {displayError}
156
- </Field.Error>
157
- )}
158
- {description && (description.id || description.defaultMessage) && (
159
- <Field.Hint>
160
- {description.id ? formatMessage(description) : description.defaultMessage}
161
- </Field.Hint>
162
- )}
163
- {(fieldNote || fieldNoteFromAttribute) && (
164
- <span style={{
165
- fontStyle: 'italic',
166
- color: '#666',
167
- fontSize: '12px',
168
- display: 'block',
169
- marginTop: '4px'
170
- }}>
171
- {fieldNote || fieldNoteFromAttribute}
172
- </span>
173
- )}
174
- </Field.Root>
175
- </Box>
176
- );
110
+ return /* @__PURE__ */ jsx(Box, { col: 6, children: /* @__PURE__ */ jsxs(Field.Root, { name, error: displayError, children: [
111
+ /* @__PURE__ */ jsxs(Field.Label, { children: [
112
+ intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || name,
113
+ required && /* @__PURE__ */ jsx("span", { style: { color: "#d02b20", marginLeft: "4px" }, children: "*" })
114
+ ] }),
115
+ renderInput(),
116
+ displayError && /* @__PURE__ */ jsx(Field.Error, { children: displayError }),
117
+ description && (description.id || description.defaultMessage) && /* @__PURE__ */ jsx(Field.Hint, { children: description.id ? formatMessage(description) : description.defaultMessage }),
118
+ (fieldNote || fieldNoteFromAttribute) && /* @__PURE__ */ jsx("span", { style: {
119
+ fontStyle: "italic",
120
+ color: "#666",
121
+ fontSize: "12px",
122
+ display: "block",
123
+ marginTop: "4px"
124
+ }, children: fieldNote || fieldNoteFromAttribute })
125
+ ] }) });
126
+ };
127
+ export {
128
+ AdvancedInput as default
177
129
  };
178
-
179
- export default AdvancedInput;