@webbycrown/advanced-fields 1.0.1 → 1.0.3

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/index.js CHANGED
@@ -28,7 +28,7 @@ const customFields = [
28
28
  return options.customErrorMessage || 'Value does not match required format';
29
29
  }
30
30
  } catch (e) {
31
- console.warn('Invalid regex pattern:', options.regex);
31
+ // Invalid regex pattern - silently ignore
32
32
  }
33
33
  }
34
34
 
@@ -63,15 +63,6 @@ const customFields = [
63
63
  name: 'checkbox',
64
64
  type: 'json',
65
65
  validate: (value, { required, options = {} }) => {
66
- console.log('[advanced-fields] Checkbox validation:', {
67
- value,
68
- required,
69
- options,
70
- valueType: typeof value,
71
- isArray: Array.isArray(value),
72
- valueLength: Array.isArray(value) ? value.length : 'N/A',
73
- checkboxType: options.checkboxType
74
- });
75
66
 
76
67
  // Basic required validation
77
68
  if (required) {
@@ -80,14 +71,12 @@ const customFields = [
80
71
  (typeof value === 'string' && value.trim() === '') ||
81
72
  value === null ||
82
73
  value === undefined) {
83
- console.log('[advanced-fields] Checkbox validation failed: required field is empty');
84
74
  return options.customErrorMessage || 'This field is required';
85
75
  }
86
76
  }
87
77
 
88
78
  // If no value and not required, no additional validation needed
89
79
  if (!required && (!value || (Array.isArray(value) && value.length === 0))) {
90
- console.log('[advanced-fields] Checkbox validation passed (no value, not required)');
91
80
  return null;
92
81
  }
93
82
 
@@ -101,7 +90,6 @@ const customFields = [
101
90
  }
102
91
  }
103
92
 
104
- console.log('[advanced-fields] Checkbox validation passed');
105
93
  return null;
106
94
  }
107
95
  },
@@ -137,18 +125,14 @@ const customFields = [
137
125
 
138
126
  module.exports = {
139
127
  register({ strapi }) {
140
- console.log(`[${PLUGIN_ID}] Registering custom fields...`);
141
-
142
128
  // Ensure custom fields registry is available
143
129
  if (!strapi.customFields) {
144
- console.error(`[${PLUGIN_ID}] Custom fields registry not available`);
145
130
  return;
146
131
  }
147
132
 
148
133
  // Register custom fields with proper error handling
149
134
  customFields.forEach((field) => {
150
135
  try {
151
- console.log(`[${PLUGIN_ID}] Registering field: ${field.name} (type: ${field.type})`);
152
136
 
153
137
  const fieldConfig = {
154
138
  name: field.name,
@@ -159,44 +143,16 @@ module.exports = {
159
143
  // Add validation function if it exists
160
144
  if (field.validate) {
161
145
  fieldConfig.validate = field.validate;
162
- console.log(`[${PLUGIN_ID}] Added validation function for field: ${field.name}`);
163
146
  }
164
147
 
165
148
  strapi.customFields.register(fieldConfig);
166
- console.log(`[${PLUGIN_ID}] Successfully registered field: ${field.name}`);
167
149
  } catch (error) {
168
- console.error(`[${PLUGIN_ID}] Failed to register field ${field.name}:`, error.message);
169
- console.error(`[${PLUGIN_ID}] Error details:`, error);
150
+ // Field registration failed - silently continue
170
151
  }
171
152
  });
172
-
173
- console.log(`[${PLUGIN_ID}] All custom fields registration completed`);
174
153
  },
175
154
 
176
155
  bootstrap({ strapi }) {
177
- console.log(`[${PLUGIN_ID}] Bootstrap starting...`);
178
-
179
- // Wait a bit for registration to complete
180
- setTimeout(() => {
181
- // Verify all fields are registered only if customFields.get is available
182
- if (strapi.customFields && typeof strapi.customFields.get === 'function') {
183
- customFields.forEach((field) => {
184
- try {
185
- const registeredField = strapi.customFields.get(`${PLUGIN_ID}.${field.name}`);
186
- if (registeredField) {
187
- console.log(`[${PLUGIN_ID}] ✅ Field verified: ${field.name}`);
188
- } else {
189
- console.error(`[${PLUGIN_ID}] ❌ Field not found in registry: ${field.name}`);
190
- }
191
- } catch (error) {
192
- console.error(`[${PLUGIN_ID}] Error verifying field ${field.name}:`, error.message);
193
- }
194
- });
195
- } else {
196
- console.log(`[${PLUGIN_ID}] Skipping field verification (customFields.get not available)`);
197
- }
198
- }, 100);
199
-
200
- console.log(`[${PLUGIN_ID}] Bootstrap completed`);
156
+ // Bootstrap completed
201
157
  },
202
158
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webbycrown/advanced-fields",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "keywords": [
5
5
  "strapi",
6
6
  "plugin",
@@ -25,8 +25,7 @@
25
25
  "./strapi-server": "./strapi-server.js"
26
26
  },
27
27
  "files": [
28
- "dist",
29
- "admin",
28
+ "dist/",
30
29
  "strapi-server.js",
31
30
  "strapi-server.mjs",
32
31
  "index.js",
@@ -34,8 +33,10 @@
34
33
  "LICENSE"
35
34
  ],
36
35
  "scripts": {
37
- "build": "npm run build:admin",
36
+ "build": "npm run build:admin && npm run copy:translations",
38
37
  "build:admin": "strapi-plugin build",
38
+ "copy:translations": "cp -r admin/src/translations dist/admin/",
39
+ "prepublishOnly": "npm run build",
39
40
  "watch": "strapi-plugin watch",
40
41
  "watch:link": "strapi-plugin watch:link",
41
42
  "verify": "strapi-plugin verify"
@@ -68,7 +69,7 @@
68
69
  "displayName": "Advanced Fields",
69
70
  "description": "Professional custom fields for Strapi: Advanced Input with validation, Advanced Checkbox (single/multiple), and Advanced Radio (single/multiple) with dynamic options and comprehensive configuration"
70
71
  },
71
- "description": "🚀 Professional custom fields for Strapi CMS. Includes Advanced Input with comprehensive validation, Advanced Checkbox supporting both single and multiple selections, and Advanced Radio with dynamic options. Perfect for building user-friendly content management interfaces.",
72
+ "description": "Professional custom fields for Strapi: Advanced Input with validation, Advanced Checkbox (single/multiple), and Advanced Radio (single/multiple) with dynamic options and comprehensive configuration",
72
73
  "license": "MIT",
73
74
  "author": {
74
75
  "name": "WebbyCrown",
@@ -1,10 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es6",
4
- "jsx": "react",
5
- "module": "esnext",
6
- "allowSyntheticDefaultImports": true,
7
- "esModuleInterop": true
8
- },
9
- "include": ["./src/**/*.js", "./src/**/*.jsx"]
10
- }
@@ -1,397 +0,0 @@
1
- "use client";
2
-
3
- import { useIntl } from "react-intl";
4
- import { Field, Box, Flex, Typography } from "@strapi/design-system";
5
- import { useState, useEffect } from "react";
6
-
7
- const CheckboxInput = ({
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 } = useIntl();
20
-
21
- const {
22
- checkboxType = "multiple",
23
- layout = "vertical",
24
- minChoices = 0,
25
- maxChoices = 0,
26
- defaultSelected = "",
27
- checkboxOptions = "",
28
- customErrorMessage = "",
29
- } = attribute.options || attribute;
30
-
31
- // Debug logging for checkboxType detection
32
- // console.log('AdvancedCheckbox options detection:', {
33
- // attribute,
34
- // attributeOptions: attribute.options,
35
- // checkboxType,
36
- // checkboxOptions,
37
- // allOptions: attribute.options || attribute
38
- // });
39
-
40
- // Initialize with default values if available
41
- const getInitialValues = () => {
42
- if (checkboxType === "single") {
43
- // For single checkbox mode, return array with one value (like radio button)
44
- if (value && Array.isArray(value) && value.length > 0) {
45
- return value;
46
- } else if (value && typeof value === "string" && value.trim()) {
47
- return [value.trim()];
48
- } else if (defaultSelected && typeof defaultSelected === "string" && defaultSelected.trim()) {
49
- return [defaultSelected.trim()];
50
- } else if (defaultSelected && Array.isArray(defaultSelected) && defaultSelected.length > 0) {
51
- return defaultSelected;
52
- }
53
- return [];
54
- } else {
55
- // For multiple checkboxes, return array of selected values
56
- if (value && Array.isArray(value)) {
57
- return value;
58
- } else if (value && typeof value === "string") {
59
- return value.split(",").map(v => v.trim()).filter(v => v);
60
- } else if (defaultSelected) {
61
- return defaultSelected.split(",").map(v => v.trim()).filter(v => v);
62
- }
63
- return [];
64
- }
65
- };
66
-
67
- const [fieldValue, setFieldValue] = useState(getInitialValues);
68
- const [validationError, setValidationError] = useState(null);
69
- const [hasInteracted, setHasInteracted] = useState(false);
70
- const [isInitialized, setIsInitialized] = useState(false);
71
-
72
- // Parse checkbox options
73
- const options = checkboxOptions
74
- .split("\n")
75
- .filter((opt) => opt.trim())
76
- .map((opt) => {
77
- const [value, label] = opt.split("|");
78
- return { value: value?.trim() || "", label: label?.trim() || value?.trim() || "" };
79
- });
80
-
81
- // Initialize selected values - only run once on mount
82
- useEffect(() => {
83
- const initialValues = getInitialValues();
84
-
85
- console.log('AdvancedCheckbox initialization:', {
86
- value,
87
- defaultSelected,
88
- initialValues,
89
- required,
90
- error
91
- });
92
-
93
- setFieldValue(initialValues);
94
-
95
- // Validate the initial values
96
- const validationResult = validateSelection(initialValues);
97
- setValidationError(validationResult);
98
-
99
- // Only trigger onChange if we have initial values and it's different from current value
100
- // AND only if the value prop is not already set (to avoid overriding during publish)
101
- // AND only if this is the first initialization
102
- if (onChange && initialValues.length > 0 && (!value || (Array.isArray(value) && value.length === 0)) && !isInitialized) {
103
- // Use setTimeout to ensure this happens after the component is fully mounted
104
- setTimeout(() => {
105
- onChange({
106
- target: {
107
- value: initialValues,
108
- name: name,
109
- id: name
110
- }
111
- });
112
- setIsInitialized(true);
113
- }, 0);
114
- } else if (!isInitialized) {
115
- setIsInitialized(true);
116
- }
117
- }, []); // Remove dependencies to only run once on mount
118
-
119
- // Handle external value changes (like when loading existing data)
120
- useEffect(() => {
121
- // Only update if the value prop has changed and it's not empty
122
- // This handles cases like loading existing content
123
- if (value && Array.isArray(value) && value.length > 0) {
124
- setFieldValue(value);
125
- const validationResult = validateSelection(value);
126
- setValidationError(validationResult);
127
- }
128
- }, [value]);
129
-
130
- // Validation function - this should match server-side validation
131
- const validateSelection = (val) => {
132
- const values = Array.isArray(val) ? val : [];
133
-
134
- // Check required validation first
135
- if (required && values.length === 0) {
136
- return customErrorMessage || 'This field is required';
137
- }
138
-
139
- // If field is empty and not required, no validation error
140
- if (values.length === 0) {
141
- return null;
142
- }
143
-
144
- // Check min/max choices validation (only for multiple mode)
145
- if (checkboxType === "multiple") {
146
- if (minChoices > 0 && values.length < minChoices) {
147
- return customErrorMessage || `Please select at least ${minChoices} option${minChoices > 1 ? 's' : ''}`;
148
- }
149
- if (maxChoices > 0 && values.length > maxChoices) {
150
- return customErrorMessage || `Please select at most ${maxChoices} option${maxChoices > 1 ? 's' : ''}`;
151
- }
152
- }
153
-
154
- return null;
155
- };
156
-
157
- const handleCheckboxChange = (optionValue, isChecked) => {
158
- let newValue;
159
-
160
- if (checkboxType === "single") {
161
- // For single checkbox mode, work like radio button - only one option can be selected
162
- if (isChecked) {
163
- // If checking an option, set it as the only selected value
164
- newValue = [optionValue];
165
- } else {
166
- // If unchecking, clear the selection
167
- newValue = [];
168
- }
169
- } else {
170
- // For multiple checkboxes, handle array of values
171
- if (isChecked) {
172
- newValue = [...fieldValue, optionValue];
173
- } else {
174
- newValue = fieldValue.filter(val => val !== optionValue);
175
- }
176
- }
177
-
178
- setFieldValue(newValue);
179
- setHasInteracted(true);
180
-
181
- // Validate selection only after user interaction
182
- const error = validateSelection(newValue);
183
- setValidationError(error);
184
-
185
- console.log('AdvancedCheckbox handleCheckboxChange:', {
186
- optionValue,
187
- isChecked,
188
- newValue,
189
- error,
190
- hasInteracted: true,
191
- checkboxType,
192
- isInitialized
193
- });
194
-
195
- // Always trigger onChange for user interactions
196
- if (onChange) {
197
- console.log(onChange)
198
- onChange({
199
- target: {
200
- value: newValue,
201
- name: name,
202
- id: name
203
- }
204
- });
205
- }
206
- };
207
-
208
- // Show validation error - prioritize Strapi's error, then our validation only after user interaction
209
- const displayError = error || (hasInteracted && validationError);
210
- console.log('displayError', displayError);
211
- const renderCheckboxes = () => {
212
- // Debug logging
213
- // console.log('AdvancedCheckbox renderCheckboxes:', {
214
- // checkboxType,
215
- // checkboxOptions,
216
- // options,
217
- // attribute,
218
- // attributeOptions: attribute.options,
219
- // fieldValue
220
- // });
221
-
222
- const checkboxStyle = {
223
- display: "flex",
224
- alignItems: "center",
225
- gap: "8px",
226
- marginBottom: "8px",
227
- };
228
-
229
- const checkboxInputStyle = {
230
- width: "16px",
231
- height: "16px",
232
- accentColor: "#4945ff",
233
- margin: "0",
234
- padding: "0",
235
- opacity: "1",
236
- visibility: "visible",
237
- display: "block",
238
- position: "relative",
239
- zIndex: "1",
240
- };
241
-
242
- const checkboxLabelStyle = {
243
- fontSize: "14px",
244
- fontFamily: "inherit",
245
- cursor: "pointer",
246
- userSelect: "none",
247
- };
248
-
249
- // Single checkbox mode - show multiple options but only one selectable (like radio buttons)
250
- if (checkboxType === "single") {
251
- // If no options are defined for single mode, show a message
252
- if (!options || options.length === 0) {
253
- return (
254
- <div style={{ padding: "8px", color: "#666", fontStyle: "italic" }}>
255
- {formatMessage({
256
- id: 'advanced-fields.checkbox.options.checkboxOptions.description',
257
- defaultMessage: 'Define available options for multiple checkboxes (one per line: value|label)'
258
- })}
259
- </div>
260
- );
261
- }
262
-
263
- if (layout === "horizontal") {
264
- return (
265
- <Flex wrap="wrap" gap={2}>
266
- {options.map((option) => (
267
- <div key={option.value} style={checkboxStyle}>
268
- <input
269
- type="checkbox"
270
- id={`${name}-${option.value}`}
271
- checked={fieldValue.includes(option.value)}
272
- onChange={(e) => handleCheckboxChange(option.value, e.target.checked)}
273
- disabled={disabled}
274
- style={checkboxInputStyle}
275
- />
276
- <label
277
- htmlFor={`${name}-${option.value}`}
278
- style={checkboxLabelStyle}
279
- >
280
- {option.label}
281
- </label>
282
- </div>
283
- ))}
284
- </Flex>
285
- );
286
- }
287
-
288
- return (
289
- <div>
290
- {options.map((option) => (
291
- <div key={option.value} style={checkboxStyle}>
292
- <input
293
- type="checkbox"
294
- id={`${name}-${option.value}`}
295
- checked={fieldValue.includes(option.value)}
296
- onChange={(e) => handleCheckboxChange(option.value, e.target.checked)}
297
- disabled={disabled}
298
- style={checkboxInputStyle}
299
- />
300
- <label
301
- htmlFor={`${name}-${option.value}`}
302
- style={checkboxLabelStyle}
303
- >
304
- {option.label}
305
- </label>
306
- </div>
307
- ))}
308
- </div>
309
- );
310
- }
311
-
312
- // Multiple checkbox mode
313
- // If no options are defined, show a message
314
- if (!options || options.length === 0) {
315
- return (
316
- <div style={{ padding: "8px", color: "#666", fontStyle: "italic" }}>
317
- {formatMessage({
318
- id: 'advanced-fields.checkbox.options.checkboxOptions.description',
319
- defaultMessage: 'Define available options for multiple checkboxes (one per line: value|label)'
320
- })}
321
- </div>
322
- );
323
- }
324
-
325
- if (layout === "horizontal") {
326
- return (
327
- <Flex wrap="wrap" gap={2}>
328
- {options.map((option) => (
329
- <div key={option.value} style={checkboxStyle}>
330
- <input
331
- type="checkbox"
332
- id={`${name}-${option.value}`}
333
- checked={fieldValue.includes(option.value)}
334
- onChange={(e) => handleCheckboxChange(option.value, e.target.checked)}
335
- disabled={disabled}
336
- style={checkboxInputStyle}
337
- />
338
- <label
339
- htmlFor={`${name}-${option.value}`}
340
- style={checkboxLabelStyle}
341
- >
342
- {option.label}
343
- </label>
344
- </div>
345
- ))}
346
- </Flex>
347
- );
348
- }
349
-
350
- return (
351
- <div>
352
- {options.map((option) => (
353
- <div key={option.value} style={checkboxStyle}>
354
- <input
355
- type="checkbox"
356
- id={`${name}-${option.value}`}
357
- checked={fieldValue.includes(option.value)}
358
- onChange={(e) => handleCheckboxChange(option.value, e.target.checked)}
359
- disabled={disabled}
360
- style={checkboxInputStyle}
361
- />
362
- <label
363
- htmlFor={`${name}-${option.value}`}
364
- style={checkboxLabelStyle}
365
- >
366
- {option.label}
367
- </label>
368
- </div>
369
- ))}
370
- </div>
371
- );
372
- };
373
-
374
- return (
375
- <Box col={6}>
376
- <Field.Root name={name} error={displayError}>
377
- <Field.Label>
378
- {intlLabel.id ? formatMessage(intlLabel) : intlLabel.defaultMessage || name}
379
- {required && <span style={{ color: "#d02b20", marginLeft: "4px" }}>*</span>}
380
- </Field.Label>
381
- {renderCheckboxes()}
382
- {displayError && (
383
- <Field.Error>
384
- {displayError}
385
- </Field.Error>
386
- )}
387
- {description && (description.id || description.defaultMessage) && (
388
- <Field.Hint>
389
- {description.id ? formatMessage(description) : description.defaultMessage}
390
- </Field.Hint>
391
- )}
392
- </Field.Root>
393
- </Box>
394
- );
395
- };
396
-
397
- export default CheckboxInput;