strapi-plugin-tags-custom-field 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 CHANGED
@@ -25,13 +25,13 @@ Restart your Strapi server after installation.
25
25
  - `maxTags` (default: `20`): maximum number of tags.
26
26
  - `maxTagLength` (default: `40`): maximum characters per tag.
27
27
  - `allowDuplicates` (default: `false`): allow repeated tags.
28
- - `separator` (default: `,`): character used to split typed/pasted values.
28
+ - `separator` (optional): character used to split typed/pasted values.
29
29
  - `normalizeCase` (default: `none`): `none`, `lowercase`, or `UPPERCASE`.
30
30
 
31
31
  ## Input behavior
32
32
 
33
33
  - `Enter` adds the current tag.
34
- - The configured separator also adds the current tag.
34
+ - If configured, the separator also adds the current tag.
35
35
  - Paste supports multiple tags (newline or configured separator).
36
36
 
37
37
  ## Data format
@@ -49,7 +49,7 @@ Example:
49
49
  ## Compatibility
50
50
 
51
51
  - Strapi: `v5`
52
- - Node.js: `>=18`
52
+ - Node.js: `>=18 <25` (18 to 24)
53
53
 
54
54
  ## Local development (plugin repo)
55
55
 
@@ -23,9 +23,8 @@ function _interopNamespace(e) {
23
23
  return Object.freeze(n);
24
24
  }
25
25
  const React__namespace = /* @__PURE__ */ _interopNamespace(React);
26
- const DEFAULT_SEPARATOR = ",";
27
- const DEFAULT_MAX_TAGS = 20;
28
- const DEFAULT_MAX_TAG_LENGTH = 40;
26
+ const DEFAULT_MAX_TAGS = 50;
27
+ const DEFAULT_MAX_TAG_LENGTH = 100;
29
28
  const parseTagsValue = (value) => {
30
29
  if (Array.isArray(value)) {
31
30
  return value.map((item) => String(item).trim()).filter((item) => item.length > 0);
@@ -84,14 +83,14 @@ const parsePositiveInt = (value, fallbackValue) => {
84
83
  };
85
84
  const getSplitRegex = (separator) => {
86
85
  const characters = Array.from(/* @__PURE__ */ new Set([separator, "\n", "\r"])).filter(
87
- (character) => character.length > 0
86
+ (character) => typeof character === "string" && character.length > 0
88
87
  );
89
88
  const escapedCharacters = characters.map((character) => character.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("");
90
89
  return new RegExp(`[${escapedCharacters}]+`);
91
90
  };
92
91
  const parseRawTags = (rawValue, separator, normalizeCase) => rawValue.split(getSplitRegex(separator)).map((tag) => normalizeTag(tag, normalizeCase)).filter((tag) => tag.length > 0);
93
92
  const hasSplitCharacters = (value, separator) => [separator, "\n", "\r"].some(
94
- (character) => character.length > 0 && value.includes(character)
93
+ (character) => typeof character === "string" && character.length > 0 && value.includes(character)
95
94
  );
96
95
  const TagsInput = React__namespace.forwardRef(
97
96
  ({
@@ -117,7 +116,7 @@ const TagsInput = React__namespace.forwardRef(
117
116
  }, [value]);
118
117
  const fieldType = attribute?.type ?? "json";
119
118
  const options = attribute?.options ?? {};
120
- const separator = typeof options.separator === "string" && options.separator.trim().length > 0 ? options.separator.trim().charAt(0) : DEFAULT_SEPARATOR;
119
+ const separator = typeof options.separator === "string" && options.separator.trim().length > 0 ? options.separator.trim().charAt(0) : void 0;
121
120
  const maxTags = parsePositiveInt(options.maxTags, DEFAULT_MAX_TAGS);
122
121
  const maxTagLength = parsePositiveInt(
123
122
  options.maxTagLength,
@@ -142,7 +141,7 @@ const TagsInput = React__namespace.forwardRef(
142
141
  const descriptionText = typeof description === "string" ? description : formatIntlMessage(description);
143
142
  const hintMessage = descriptionText && descriptionText.trim().length > 0 ? descriptionText.trim() : hint ?? formatMessage({
144
143
  id: "tags-input.hint",
145
- defaultMessage: "Press Enter or type the separator to add tags. Paste multiple tags at once."
144
+ defaultMessage: separator ? "Press Enter or type the separator to add tags. Paste multiple tags at once." : "Press Enter to add tags. Paste multiple tags at once."
146
145
  });
147
146
  const shownError = error || localError;
148
147
  const emitChange = React__namespace.useCallback(
@@ -228,7 +227,7 @@ const TagsInput = React__namespace.forwardRef(
228
227
  [emitChange]
229
228
  );
230
229
  const onKeyDown = (event) => {
231
- if (event.key === "Enter" || event.key === separator) {
230
+ if (event.key === "Enter" || separator && event.key === separator) {
232
231
  event.preventDefault();
233
232
  commitDraft();
234
233
  }
@@ -255,6 +254,7 @@ const TagsInput = React__namespace.forwardRef(
255
254
  error: shownError,
256
255
  children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
257
256
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: labelMessage }),
257
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
258
258
  tags.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
259
259
  designSystem.Box,
260
260
  {
@@ -307,13 +307,16 @@ const TagsInput = React__namespace.forwardRef(
307
307
  onBlur: commitDraft,
308
308
  onKeyDown,
309
309
  onPaste,
310
- placeholder: placeholder ?? formatMessage(
310
+ placeholder: placeholder ?? (separator ? formatMessage(
311
311
  {
312
- id: "tags-input.placeholder",
312
+ id: "tags-input.placeholder.with-separator",
313
313
  defaultMessage: "Type a tag and press Enter or {separator} to add it"
314
314
  },
315
315
  { separator }
316
- )
316
+ ) : formatMessage({
317
+ id: "tags-input.placeholder.without-separator",
318
+ defaultMessage: "Type a tag and press Enter to add it"
319
+ }))
317
320
  }
318
321
  ),
319
322
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 2, children: [
@@ -339,7 +342,6 @@ const TagsInput = React__namespace.forwardRef(
339
342
  }
340
343
  )
341
344
  ] }),
342
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
343
345
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
344
346
  ] })
345
347
  }
@@ -3,9 +3,8 @@ import * as React from "react";
3
3
  import { Field, Flex, Box, Tag, TextInput, Typography } from "@strapi/design-system";
4
4
  import { Cross } from "@strapi/icons";
5
5
  import { useIntl } from "react-intl";
6
- const DEFAULT_SEPARATOR = ",";
7
- const DEFAULT_MAX_TAGS = 20;
8
- const DEFAULT_MAX_TAG_LENGTH = 40;
6
+ const DEFAULT_MAX_TAGS = 50;
7
+ const DEFAULT_MAX_TAG_LENGTH = 100;
9
8
  const parseTagsValue = (value) => {
10
9
  if (Array.isArray(value)) {
11
10
  return value.map((item) => String(item).trim()).filter((item) => item.length > 0);
@@ -64,14 +63,14 @@ const parsePositiveInt = (value, fallbackValue) => {
64
63
  };
65
64
  const getSplitRegex = (separator) => {
66
65
  const characters = Array.from(/* @__PURE__ */ new Set([separator, "\n", "\r"])).filter(
67
- (character) => character.length > 0
66
+ (character) => typeof character === "string" && character.length > 0
68
67
  );
69
68
  const escapedCharacters = characters.map((character) => character.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("");
70
69
  return new RegExp(`[${escapedCharacters}]+`);
71
70
  };
72
71
  const parseRawTags = (rawValue, separator, normalizeCase) => rawValue.split(getSplitRegex(separator)).map((tag) => normalizeTag(tag, normalizeCase)).filter((tag) => tag.length > 0);
73
72
  const hasSplitCharacters = (value, separator) => [separator, "\n", "\r"].some(
74
- (character) => character.length > 0 && value.includes(character)
73
+ (character) => typeof character === "string" && character.length > 0 && value.includes(character)
75
74
  );
76
75
  const TagsInput = React.forwardRef(
77
76
  ({
@@ -97,7 +96,7 @@ const TagsInput = React.forwardRef(
97
96
  }, [value]);
98
97
  const fieldType = attribute?.type ?? "json";
99
98
  const options = attribute?.options ?? {};
100
- const separator = typeof options.separator === "string" && options.separator.trim().length > 0 ? options.separator.trim().charAt(0) : DEFAULT_SEPARATOR;
99
+ const separator = typeof options.separator === "string" && options.separator.trim().length > 0 ? options.separator.trim().charAt(0) : void 0;
101
100
  const maxTags = parsePositiveInt(options.maxTags, DEFAULT_MAX_TAGS);
102
101
  const maxTagLength = parsePositiveInt(
103
102
  options.maxTagLength,
@@ -122,7 +121,7 @@ const TagsInput = React.forwardRef(
122
121
  const descriptionText = typeof description === "string" ? description : formatIntlMessage(description);
123
122
  const hintMessage = descriptionText && descriptionText.trim().length > 0 ? descriptionText.trim() : hint ?? formatMessage({
124
123
  id: "tags-input.hint",
125
- defaultMessage: "Press Enter or type the separator to add tags. Paste multiple tags at once."
124
+ defaultMessage: separator ? "Press Enter or type the separator to add tags. Paste multiple tags at once." : "Press Enter to add tags. Paste multiple tags at once."
126
125
  });
127
126
  const shownError = error || localError;
128
127
  const emitChange = React.useCallback(
@@ -208,7 +207,7 @@ const TagsInput = React.forwardRef(
208
207
  [emitChange]
209
208
  );
210
209
  const onKeyDown = (event) => {
211
- if (event.key === "Enter" || event.key === separator) {
210
+ if (event.key === "Enter" || separator && event.key === separator) {
212
211
  event.preventDefault();
213
212
  commitDraft();
214
213
  }
@@ -235,6 +234,7 @@ const TagsInput = React.forwardRef(
235
234
  error: shownError,
236
235
  children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
237
236
  /* @__PURE__ */ jsx(Field.Label, { children: labelMessage }),
237
+ /* @__PURE__ */ jsx(Field.Hint, {}),
238
238
  tags.length > 0 ? /* @__PURE__ */ jsx(
239
239
  Box,
240
240
  {
@@ -287,13 +287,16 @@ const TagsInput = React.forwardRef(
287
287
  onBlur: commitDraft,
288
288
  onKeyDown,
289
289
  onPaste,
290
- placeholder: placeholder ?? formatMessage(
290
+ placeholder: placeholder ?? (separator ? formatMessage(
291
291
  {
292
- id: "tags-input.placeholder",
292
+ id: "tags-input.placeholder.with-separator",
293
293
  defaultMessage: "Type a tag and press Enter or {separator} to add it"
294
294
  },
295
295
  { separator }
296
- )
296
+ ) : formatMessage({
297
+ id: "tags-input.placeholder.without-separator",
298
+ defaultMessage: "Type a tag and press Enter to add it"
299
+ }))
297
300
  }
298
301
  ),
299
302
  /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 2, children: [
@@ -319,7 +322,6 @@ const TagsInput = React.forwardRef(
319
322
  }
320
323
  )
321
324
  ] }),
322
- /* @__PURE__ */ jsx(Field.Hint, {}),
323
325
  /* @__PURE__ */ jsx(Field.Error, {})
324
326
  ] })
325
327
  }
@@ -6,9 +6,9 @@ const en = {
6
6
  "field.tags.description": "Store tags as a JSON array",
7
7
  "field.tags.options.behavior.section": "Tag behavior",
8
8
  "field.tags.options.maxTags.label": "Maximum tags",
9
- "field.tags.options.maxTags.description": "Maximum number of tags allowed",
9
+ "field.tags.options.maxTags.description": "Maximum number of tags allowed (default 50)",
10
10
  "field.tags.options.maxTagLength.label": "Maximum tag length",
11
- "field.tags.options.maxTagLength.description": "Maximum number of characters per tag",
11
+ "field.tags.options.maxTagLength.description": "Maximum number of characters per tag (default 100)",
12
12
  "field.tags.options.allowDuplicates.label": "Allow duplicates",
13
13
  "field.tags.options.allowDuplicates.description": "Allow repeated tags in the same value",
14
14
  "field.tags.options.input.section": "Input parsing",
@@ -4,9 +4,9 @@ const en = {
4
4
  "field.tags.description": "Store tags as a JSON array",
5
5
  "field.tags.options.behavior.section": "Tag behavior",
6
6
  "field.tags.options.maxTags.label": "Maximum tags",
7
- "field.tags.options.maxTags.description": "Maximum number of tags allowed",
7
+ "field.tags.options.maxTags.description": "Maximum number of tags allowed (default 50)",
8
8
  "field.tags.options.maxTagLength.label": "Maximum tag length",
9
- "field.tags.options.maxTagLength.description": "Maximum number of characters per tag",
9
+ "field.tags.options.maxTagLength.description": "Maximum number of characters per tag (default 100)",
10
10
  "field.tags.options.allowDuplicates.label": "Allow duplicates",
11
11
  "field.tags.options.allowDuplicates.description": "Allow repeated tags in the same value",
12
12
  "field.tags.options.input.section": "Input parsing",
@@ -66,7 +66,7 @@ const index = {
66
66
  },
67
67
  icon: PluginIcon,
68
68
  components: {
69
- Input: async () => Promise.resolve().then(() => require("./TagsInput-BfjqO_MO.js")).then((module2) => ({
69
+ Input: async () => Promise.resolve().then(() => require("./TagsInput-Bed2AKfo.js")).then((module2) => ({
70
70
  default: module2.TagsInput
71
71
  }))
72
72
  },
@@ -85,7 +85,7 @@ const index = {
85
85
  },
86
86
  description: {
87
87
  id: getTranslation("field.tags.options.maxTags.description"),
88
- defaultMessage: "Maximum number of tags allowed"
88
+ defaultMessage: "Maximum number of tags allowed (default 50)"
89
89
  },
90
90
  name: "options.maxTags",
91
91
  type: "number",
@@ -100,7 +100,7 @@ const index = {
100
100
  id: getTranslation(
101
101
  "field.tags.options.maxTagLength.description"
102
102
  ),
103
- defaultMessage: "Maximum number of characters per tag"
103
+ defaultMessage: "Maximum number of characters per tag (default 100)"
104
104
  },
105
105
  name: "options.maxTagLength",
106
106
  type: "number",
@@ -140,7 +140,7 @@ const index = {
140
140
  },
141
141
  name: "options.separator",
142
142
  type: "text",
143
- value: ","
143
+ value: ""
144
144
  },
145
145
  {
146
146
  intlLabel: {
@@ -209,7 +209,7 @@ const index = {
209
209
  return Promise.all(
210
210
  locales.map(async (locale) => {
211
211
  try {
212
- const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-C_FVtF2u.js")) }), `./translations/${locale}.json`, 3);
212
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-B10Zs0Lz.js")) }), `./translations/${locale}.json`, 3);
213
213
  return { data, locale };
214
214
  } catch {
215
215
  return { data: {}, locale };
@@ -64,7 +64,7 @@ const index = {
64
64
  },
65
65
  icon: PluginIcon,
66
66
  components: {
67
- Input: async () => import("./TagsInput-CzXiRuEv.mjs").then((module) => ({
67
+ Input: async () => import("./TagsInput-C96IrAdH.mjs").then((module) => ({
68
68
  default: module.TagsInput
69
69
  }))
70
70
  },
@@ -83,7 +83,7 @@ const index = {
83
83
  },
84
84
  description: {
85
85
  id: getTranslation("field.tags.options.maxTags.description"),
86
- defaultMessage: "Maximum number of tags allowed"
86
+ defaultMessage: "Maximum number of tags allowed (default 50)"
87
87
  },
88
88
  name: "options.maxTags",
89
89
  type: "number",
@@ -98,7 +98,7 @@ const index = {
98
98
  id: getTranslation(
99
99
  "field.tags.options.maxTagLength.description"
100
100
  ),
101
- defaultMessage: "Maximum number of characters per tag"
101
+ defaultMessage: "Maximum number of characters per tag (default 100)"
102
102
  },
103
103
  name: "options.maxTagLength",
104
104
  type: "number",
@@ -138,7 +138,7 @@ const index = {
138
138
  },
139
139
  name: "options.separator",
140
140
  type: "text",
141
- value: ","
141
+ value: ""
142
142
  },
143
143
  {
144
144
  intlLabel: {
@@ -207,7 +207,7 @@ const index = {
207
207
  return Promise.all(
208
208
  locales.map(async (locale) => {
209
209
  try {
210
- const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-BhiXf-Vq.mjs") }), `./translations/${locale}.json`, 3);
210
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-vnGnUwx5.mjs") }), `./translations/${locale}.json`, 3);
211
211
  return { data, locale };
212
212
  } catch {
213
213
  return { data: {}, locale };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-tags-custom-field",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Custom field plugin for Strapi 5 to manage tags (array of strings) stored as JSON array.",
5
5
  "keywords": [
6
6
  "strapi",
@@ -74,7 +74,7 @@
74
74
  "description": "Custom field for tags (array of strings) stored as JSON array"
75
75
  },
76
76
  "engines": {
77
- "node": ">=18.0.0"
77
+ "node": ">=18 <25"
78
78
  },
79
79
  "repository": {
80
80
  "type": "git",