@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,268 @@
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 AdvancedRadio = ({
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 [selectedValues, setSelectedValues] = react.useState([]);
21
+ const [validationError, setValidationError] = react.useState(null);
22
+ const [hasInteracted, setHasInteracted] = react.useState(false);
23
+ const [isInitialized, setIsInitialized] = react.useState(false);
24
+ const {
25
+ selectionType = "single",
26
+ layout = "vertical",
27
+ minChoices = 0,
28
+ maxChoices = 0,
29
+ defaultSelected = "",
30
+ radioOptions = "",
31
+ customErrorMessage = "",
32
+ fieldNote = ""
33
+ } = attribute.options || attribute;
34
+ const fieldNoteFromAttribute = attribute.options?.fieldNote || "";
35
+ const options = radioOptions.split("\n").filter((opt) => opt.trim()).map((opt) => {
36
+ const [value2, label] = opt.split("|");
37
+ return { value: value2?.trim() || "", label: label?.trim() || value2?.trim() || "" };
38
+ });
39
+ react.useEffect(() => {
40
+ let initialValues = [];
41
+ if (value && Array.isArray(value)) {
42
+ initialValues = value;
43
+ } else if (value && typeof value === "string") {
44
+ initialValues = value.split(",").map((v) => v.trim()).filter((v) => v);
45
+ } else if (defaultSelected) {
46
+ initialValues = defaultSelected.split(",").map((v) => v.trim()).filter((v) => v);
47
+ }
48
+ setSelectedValues(initialValues);
49
+ const validationResult = validateSelection(initialValues);
50
+ setValidationError(validationResult);
51
+ if (onChange && initialValues.length > 0 && (!value || Array.isArray(value) && value.length === 0) && !isInitialized) {
52
+ setTimeout(() => {
53
+ onChange({
54
+ target: {
55
+ value: initialValues,
56
+ name,
57
+ id: name
58
+ }
59
+ });
60
+ setIsInitialized(true);
61
+ }, 0);
62
+ } else if (!isInitialized) {
63
+ setIsInitialized(true);
64
+ }
65
+ }, [value, defaultSelected, onChange, error]);
66
+ const validateSelection = (values) => {
67
+ const valArray = Array.isArray(values) ? values : [];
68
+ if (required && valArray.length === 0) {
69
+ return customErrorMessage || "This field is required";
70
+ }
71
+ if (valArray.length === 0) {
72
+ return null;
73
+ }
74
+ if (selectionType === "multiple") {
75
+ if (minChoices > 0 && valArray.length < minChoices) {
76
+ return customErrorMessage || `Please select at least ${minChoices} option${minChoices > 1 ? "s" : ""}`;
77
+ }
78
+ if (maxChoices > 0 && valArray.length > maxChoices) {
79
+ return customErrorMessage || `Please select at most ${maxChoices} option${maxChoices > 1 ? "s" : ""}`;
80
+ }
81
+ }
82
+ return null;
83
+ };
84
+ const handleRadioChange = (optionValue, isChecked) => {
85
+ let newValues;
86
+ if (selectionType === "single") {
87
+ newValues = isChecked ? [optionValue] : [];
88
+ } else {
89
+ if (isChecked) {
90
+ newValues = [...selectedValues, optionValue];
91
+ } else {
92
+ newValues = selectedValues.filter((val) => val !== optionValue);
93
+ }
94
+ }
95
+ setSelectedValues(newValues);
96
+ setHasInteracted(true);
97
+ const error2 = validateSelection(newValues);
98
+ setValidationError(error2);
99
+ if (onChange) {
100
+ const event = {
101
+ target: {
102
+ name,
103
+ id: name,
104
+ value: newValues
105
+ }
106
+ };
107
+ onChange(event);
108
+ }
109
+ };
110
+ const displayError = error || hasInteracted && validationError;
111
+ const renderRadios = () => {
112
+ const radioStyle = {
113
+ display: "flex",
114
+ alignItems: "center",
115
+ gap: "8px",
116
+ padding: "4px 0"
117
+ };
118
+ const radioInputStyle = {
119
+ width: "16px",
120
+ height: "16px",
121
+ accentColor: "#4945ff",
122
+ margin: "0",
123
+ padding: "0",
124
+ opacity: "1",
125
+ visibility: "visible",
126
+ display: "block",
127
+ position: "relative",
128
+ zIndex: "1",
129
+ cursor: "pointer",
130
+ border: "2px solid #dcdce4",
131
+ borderRadius: "50%",
132
+ backgroundColor: "white",
133
+ transition: "all 0.2s ease"
134
+ };
135
+ const customRadioStyle = {
136
+ width: "16px",
137
+ height: "16px",
138
+ borderRadius: "50%",
139
+ borderWidth: "1px",
140
+ borderStyle: "solid",
141
+ backgroundColor: "#ffffff",
142
+ cursor: "pointer",
143
+ transition: "all 0.2s ease",
144
+ position: "relative",
145
+ display: "flex",
146
+ alignItems: "center",
147
+ justifyContent: "center"
148
+ };
149
+ const customRadioCheckedStyle = {
150
+ ...customRadioStyle,
151
+ backgroundColor: "#ffffff",
152
+ borderColor: "#4945ff"
153
+ };
154
+ const customRadioDotStyle = {
155
+ width: "10px",
156
+ height: "10px",
157
+ borderRadius: "50%",
158
+ backgroundColor: "#4945ff"
159
+ };
160
+ const radioLabelStyle = {
161
+ fontSize: "14px",
162
+ fontFamily: "inherit",
163
+ cursor: "pointer",
164
+ userSelect: "none",
165
+ color: "#32324d",
166
+ fontWeight: "400",
167
+ lineHeight: "1.5",
168
+ marginLeft: "4px"
169
+ };
170
+ if (!options || options.length === 0) {
171
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "8px", color: "#666", fontStyle: "italic" }, children: formatMessage({
172
+ id: "advanced-fields.radio.no-options",
173
+ defaultMessage: "No options defined. Please configure this field in the content type settings."
174
+ }) });
175
+ }
176
+ if (layout === "horizontal") {
177
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { wrap: "wrap", gap: 2, children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: radioStyle, children: [
178
+ selectionType === "multiple" ? (
179
+ // Custom radio button appearance for multiple selection
180
+ /* @__PURE__ */ jsxRuntime.jsx(
181
+ "div",
182
+ {
183
+ style: selectedValues.includes(option.value) ? customRadioCheckedStyle : customRadioStyle,
184
+ onClick: () => handleRadioChange(option.value, !selectedValues.includes(option.value)),
185
+ children: selectedValues.includes(option.value) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: customRadioDotStyle })
186
+ }
187
+ )
188
+ ) : (
189
+ // Regular radio button for single selection
190
+ /* @__PURE__ */ jsxRuntime.jsx(
191
+ "input",
192
+ {
193
+ type: "radio",
194
+ id: `${name}-${option.value}`,
195
+ name,
196
+ checked: selectedValues.includes(option.value),
197
+ onChange: (e) => handleRadioChange(option.value, e.target.checked),
198
+ disabled,
199
+ style: radioInputStyle
200
+ }
201
+ )
202
+ ),
203
+ /* @__PURE__ */ jsxRuntime.jsx(
204
+ "label",
205
+ {
206
+ htmlFor: selectionType === "single" ? `${name}-${option.value}` : void 0,
207
+ style: radioLabelStyle,
208
+ onClick: selectionType === "multiple" ? () => handleRadioChange(option.value, !selectedValues.includes(option.value)) : void 0,
209
+ children: option.label
210
+ }
211
+ )
212
+ ] }, option.value)) });
213
+ }
214
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: radioStyle, children: [
215
+ selectionType === "multiple" ? (
216
+ // Custom radio button appearance for multiple selection
217
+ /* @__PURE__ */ jsxRuntime.jsx(
218
+ "div",
219
+ {
220
+ style: selectedValues.includes(option.value) ? customRadioCheckedStyle : customRadioStyle,
221
+ onClick: () => handleRadioChange(option.value, !selectedValues.includes(option.value)),
222
+ children: selectedValues.includes(option.value) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: customRadioDotStyle })
223
+ }
224
+ )
225
+ ) : (
226
+ // Regular radio button for single selection
227
+ /* @__PURE__ */ jsxRuntime.jsx(
228
+ "input",
229
+ {
230
+ type: "radio",
231
+ id: `${name}-${option.value}`,
232
+ name,
233
+ checked: selectedValues.includes(option.value),
234
+ onChange: (e) => handleRadioChange(option.value, e.target.checked),
235
+ disabled,
236
+ style: radioInputStyle
237
+ }
238
+ )
239
+ ),
240
+ /* @__PURE__ */ jsxRuntime.jsx(
241
+ "label",
242
+ {
243
+ htmlFor: selectionType === "single" ? `${name}-${option.value}` : void 0,
244
+ style: radioLabelStyle,
245
+ onClick: selectionType === "multiple" ? () => handleRadioChange(option.value, !selectedValues.includes(option.value)) : void 0,
246
+ children: option.label
247
+ }
248
+ )
249
+ ] }, option.value)) });
250
+ };
251
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { col: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name, error: displayError, children: [
252
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Label, { children: [
253
+ intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || name,
254
+ required && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#d02b20", marginLeft: "4px" }, children: "*" })
255
+ ] }),
256
+ renderRadios(),
257
+ displayError && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, { children: displayError }),
258
+ description && (description.id || description.defaultMessage) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { children: description.id ? formatMessage(description) : description.defaultMessage }),
259
+ (fieldNote || fieldNoteFromAttribute) && /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
260
+ fontStyle: "italic",
261
+ color: "#666",
262
+ fontSize: "12px",
263
+ display: "block",
264
+ marginTop: "4px"
265
+ }, children: fieldNote || fieldNoteFromAttribute })
266
+ ] }) });
267
+ };
268
+ exports.default = AdvancedRadio;
@@ -0,0 +1,282 @@
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 AdvancedCheckbox = ({
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
+ checkboxType = "multiple",
20
+ layout = "vertical",
21
+ minChoices = 0,
22
+ maxChoices = 0,
23
+ defaultSelected = "",
24
+ checkboxOptions = "",
25
+ customErrorMessage = "",
26
+ fieldNote = ""
27
+ } = attribute.options || attribute;
28
+ const fieldNoteFromAttribute = attribute.options?.fieldNote || "";
29
+ const getInitialValues = () => {
30
+ if (checkboxType === "single") {
31
+ if (value && Array.isArray(value) && value.length > 0) {
32
+ return value;
33
+ } else if (value && typeof value === "string" && value.trim()) {
34
+ return [value.trim()];
35
+ } else if (defaultSelected && typeof defaultSelected === "string" && defaultSelected.trim()) {
36
+ return [defaultSelected.trim()];
37
+ } else if (defaultSelected && Array.isArray(defaultSelected) && defaultSelected.length > 0) {
38
+ return defaultSelected;
39
+ }
40
+ return [];
41
+ } else {
42
+ if (value && Array.isArray(value)) {
43
+ return value;
44
+ } else if (value && typeof value === "string") {
45
+ return value.split(",").map((v) => v.trim()).filter((v) => v);
46
+ } else if (defaultSelected) {
47
+ return defaultSelected.split(",").map((v) => v.trim()).filter((v) => v);
48
+ }
49
+ return [];
50
+ }
51
+ };
52
+ const [fieldValue, setFieldValue] = useState(getInitialValues);
53
+ const [validationError, setValidationError] = useState(null);
54
+ const [hasInteracted, setHasInteracted] = useState(false);
55
+ const [isInitialized, setIsInitialized] = useState(false);
56
+ const options = checkboxOptions.split("\n").filter((opt) => opt.trim()).map((opt) => {
57
+ const [value2, label] = opt.split("|");
58
+ return { value: value2?.trim() || "", label: label?.trim() || value2?.trim() || "" };
59
+ });
60
+ useEffect(() => {
61
+ const initialValues = getInitialValues();
62
+ setFieldValue(initialValues);
63
+ const validationResult = validateSelection(initialValues);
64
+ setValidationError(validationResult);
65
+ if (onChange && initialValues.length > 0 && (!value || Array.isArray(value) && value.length === 0) && !isInitialized) {
66
+ setTimeout(() => {
67
+ onChange({
68
+ target: {
69
+ value: initialValues,
70
+ name,
71
+ id: name
72
+ }
73
+ });
74
+ setIsInitialized(true);
75
+ }, 0);
76
+ } else if (!isInitialized) {
77
+ setIsInitialized(true);
78
+ }
79
+ }, []);
80
+ useEffect(() => {
81
+ if (value && Array.isArray(value) && value.length > 0) {
82
+ setFieldValue(value);
83
+ const validationResult = validateSelection(value);
84
+ setValidationError(validationResult);
85
+ }
86
+ }, [value]);
87
+ const validateSelection = (val) => {
88
+ const values = Array.isArray(val) ? val : [];
89
+ if (required && values.length === 0) {
90
+ return customErrorMessage || "This field is required";
91
+ }
92
+ if (values.length === 0) {
93
+ return null;
94
+ }
95
+ if (checkboxType === "multiple") {
96
+ if (minChoices > 0 && values.length < minChoices) {
97
+ return customErrorMessage || `Please select at least ${minChoices} option${minChoices > 1 ? "s" : ""}`;
98
+ }
99
+ if (maxChoices > 0 && values.length > maxChoices) {
100
+ return customErrorMessage || `Please select at most ${maxChoices} option${maxChoices > 1 ? "s" : ""}`;
101
+ }
102
+ }
103
+ return null;
104
+ };
105
+ const handleCheckboxChange = (optionValue, isChecked) => {
106
+ let newValue;
107
+ if (checkboxType === "single") {
108
+ if (isChecked) {
109
+ newValue = [optionValue];
110
+ } else {
111
+ newValue = [];
112
+ }
113
+ } else {
114
+ if (isChecked) {
115
+ newValue = [...fieldValue, optionValue];
116
+ } else {
117
+ newValue = fieldValue.filter((val) => val !== optionValue);
118
+ }
119
+ }
120
+ setFieldValue(newValue);
121
+ setHasInteracted(true);
122
+ const error2 = validateSelection(newValue);
123
+ setValidationError(error2);
124
+ if (onChange) {
125
+ onChange({
126
+ target: {
127
+ value: newValue,
128
+ name,
129
+ id: name
130
+ }
131
+ });
132
+ }
133
+ };
134
+ const displayError = error || hasInteracted && validationError;
135
+ const renderCheckboxes = () => {
136
+ const checkboxStyle = {
137
+ display: "flex",
138
+ alignItems: "center",
139
+ gap: "8px",
140
+ marginBottom: "8px"
141
+ };
142
+ const checkboxInputStyle = {
143
+ width: "16px",
144
+ height: "16px",
145
+ accentColor: "#4945ff",
146
+ margin: "0",
147
+ padding: "0",
148
+ opacity: "1",
149
+ visibility: "visible",
150
+ display: "block",
151
+ position: "relative",
152
+ zIndex: "1"
153
+ };
154
+ const checkboxLabelStyle = {
155
+ fontSize: "14px",
156
+ fontFamily: "inherit",
157
+ cursor: "pointer",
158
+ userSelect: "none"
159
+ };
160
+ if (checkboxType === "single") {
161
+ if (!options || options.length === 0) {
162
+ return /* @__PURE__ */ jsx("div", { style: { padding: "8px", color: "#666", fontStyle: "italic" }, children: formatMessage({
163
+ id: "advanced-fields.checkbox.options.checkboxOptions.description",
164
+ defaultMessage: "Define available options for multiple checkboxes (one per line: value|label)"
165
+ }) });
166
+ }
167
+ if (layout === "horizontal") {
168
+ return /* @__PURE__ */ jsx(Flex, { wrap: "wrap", gap: 2, children: options.map((option) => /* @__PURE__ */ jsxs("div", { style: checkboxStyle, children: [
169
+ /* @__PURE__ */ jsx(
170
+ "input",
171
+ {
172
+ type: "checkbox",
173
+ id: `${name}-${option.value}`,
174
+ checked: fieldValue.includes(option.value),
175
+ onChange: (e) => handleCheckboxChange(option.value, e.target.checked),
176
+ disabled,
177
+ style: checkboxInputStyle
178
+ }
179
+ ),
180
+ /* @__PURE__ */ jsx(
181
+ "label",
182
+ {
183
+ htmlFor: `${name}-${option.value}`,
184
+ style: checkboxLabelStyle,
185
+ children: option.label
186
+ }
187
+ )
188
+ ] }, option.value)) });
189
+ }
190
+ return /* @__PURE__ */ jsx("div", { children: options.map((option) => /* @__PURE__ */ jsxs("div", { style: checkboxStyle, children: [
191
+ /* @__PURE__ */ jsx(
192
+ "input",
193
+ {
194
+ type: "checkbox",
195
+ id: `${name}-${option.value}`,
196
+ checked: fieldValue.includes(option.value),
197
+ onChange: (e) => handleCheckboxChange(option.value, e.target.checked),
198
+ disabled,
199
+ style: checkboxInputStyle
200
+ }
201
+ ),
202
+ /* @__PURE__ */ jsx(
203
+ "label",
204
+ {
205
+ htmlFor: `${name}-${option.value}`,
206
+ style: checkboxLabelStyle,
207
+ children: option.label
208
+ }
209
+ )
210
+ ] }, option.value)) });
211
+ }
212
+ if (!options || options.length === 0) {
213
+ return /* @__PURE__ */ jsx("div", { style: { padding: "8px", color: "#666", fontStyle: "italic" }, children: formatMessage({
214
+ id: "advanced-fields.checkbox.options.checkboxOptions.description",
215
+ defaultMessage: "Define available options for multiple checkboxes (one per line: value|label)"
216
+ }) });
217
+ }
218
+ if (layout === "horizontal") {
219
+ return /* @__PURE__ */ jsx(Flex, { wrap: "wrap", gap: 2, children: options.map((option) => /* @__PURE__ */ jsxs("div", { style: checkboxStyle, children: [
220
+ /* @__PURE__ */ jsx(
221
+ "input",
222
+ {
223
+ type: "checkbox",
224
+ id: `${name}-${option.value}`,
225
+ checked: fieldValue.includes(option.value),
226
+ onChange: (e) => handleCheckboxChange(option.value, e.target.checked),
227
+ disabled,
228
+ style: checkboxInputStyle
229
+ }
230
+ ),
231
+ /* @__PURE__ */ jsx(
232
+ "label",
233
+ {
234
+ htmlFor: `${name}-${option.value}`,
235
+ style: checkboxLabelStyle,
236
+ children: option.label
237
+ }
238
+ )
239
+ ] }, option.value)) });
240
+ }
241
+ return /* @__PURE__ */ jsx("div", { children: options.map((option) => /* @__PURE__ */ jsxs("div", { style: checkboxStyle, children: [
242
+ /* @__PURE__ */ jsx(
243
+ "input",
244
+ {
245
+ type: "checkbox",
246
+ id: `${name}-${option.value}`,
247
+ checked: fieldValue.includes(option.value),
248
+ onChange: (e) => handleCheckboxChange(option.value, e.target.checked),
249
+ disabled,
250
+ style: checkboxInputStyle
251
+ }
252
+ ),
253
+ /* @__PURE__ */ jsx(
254
+ "label",
255
+ {
256
+ htmlFor: `${name}-${option.value}`,
257
+ style: checkboxLabelStyle,
258
+ children: option.label
259
+ }
260
+ )
261
+ ] }, option.value)) });
262
+ };
263
+ return /* @__PURE__ */ jsx(Box, { col: 6, children: /* @__PURE__ */ jsxs(Field.Root, { name, error: displayError, children: [
264
+ /* @__PURE__ */ jsxs(Field.Label, { children: [
265
+ intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || name,
266
+ required && /* @__PURE__ */ jsx("span", { style: { color: "#d02b20", marginLeft: "4px" }, children: "*" })
267
+ ] }),
268
+ renderCheckboxes(),
269
+ displayError && /* @__PURE__ */ jsx(Field.Error, { children: displayError }),
270
+ description && (description.id || description.defaultMessage) && /* @__PURE__ */ jsx(Field.Hint, { children: description.id ? formatMessage(description) : description.defaultMessage }),
271
+ (fieldNote || fieldNoteFromAttribute) && /* @__PURE__ */ jsx("span", { style: {
272
+ fontStyle: "italic",
273
+ color: "#666",
274
+ fontSize: "12px",
275
+ display: "block",
276
+ marginTop: "4px"
277
+ }, children: fieldNote || fieldNoteFromAttribute })
278
+ ] }) });
279
+ };
280
+ export {
281
+ AdvancedCheckbox as default
282
+ };