@tunghtml/strapi-plugin-multiselect-checkbox 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.
@@ -0,0 +1,109 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Field, Flex, Typography, Box, Checkbox } from "@strapi/design-system";
3
+ import { useField } from "@strapi/strapi/admin";
4
+ import { useMemo } from "react";
5
+ import { useIntl } from "react-intl";
6
+ import styled from "styled-components";
7
+ const CapitalizedText = styled.p`
8
+ &::first-letter {
9
+ text-transform: uppercase;
10
+ }
11
+ `;
12
+ const MultiSelect = ({
13
+ hint,
14
+ label,
15
+ name,
16
+ intlLabel,
17
+ required,
18
+ attribute,
19
+ description,
20
+ placeholder,
21
+ disabled
22
+ }) => {
23
+ const { formatMessage } = useIntl();
24
+ const { onChange, value, error } = useField(name);
25
+ const possibleOptions = useMemo(() => {
26
+ return (attribute["options"] || []).map((option) => {
27
+ const [label2, value2] = [...option.split(/:(.*)/s), option];
28
+ if (!label2 || !value2) return null;
29
+ return { label: label2, value: value2 };
30
+ }).filter(Boolean);
31
+ }, [attribute]);
32
+ const sanitizedValue = useMemo(() => {
33
+ let parsedValue;
34
+ try {
35
+ parsedValue = typeof value !== "string" ? value || [] : JSON.parse(value || "[]");
36
+ } catch (e) {
37
+ parsedValue = [];
38
+ }
39
+ return Array.isArray(parsedValue) ? parsedValue.map(
40
+ (val) => possibleOptions.find((option) => option.value === val)
41
+ ).filter((option) => !!option) : [];
42
+ }, [value, possibleOptions]);
43
+ const fieldError = useMemo(() => {
44
+ if (error) return error;
45
+ const { min, max } = attribute;
46
+ const hasNoOptions = required && !possibleOptions.length;
47
+ const belowMin = sanitizedValue.length < min && (required || sanitizedValue.length > 0);
48
+ const aboveMax = sanitizedValue.length > max;
49
+ if (hasNoOptions) {
50
+ return "No options, but field is required";
51
+ }
52
+ if (belowMin) {
53
+ return `Select at least ${min} options`;
54
+ }
55
+ if (aboveMax) {
56
+ return `Select at most ${max} options`;
57
+ }
58
+ return null;
59
+ }, [required, error, possibleOptions, sanitizedValue, attribute]);
60
+ const handleCheckboxChange = (optionValue, isChecked) => {
61
+ let newValues;
62
+ if (isChecked) {
63
+ newValues = [...sanitizedValue.map((v) => v.value), optionValue];
64
+ } else {
65
+ newValues = sanitizedValue.map((v) => v.value).filter((v) => v !== optionValue);
66
+ }
67
+ onChange({
68
+ target: {
69
+ name,
70
+ value: newValues.length ? JSON.stringify(newValues) : null,
71
+ type: attribute.type
72
+ }
73
+ });
74
+ };
75
+ return /* @__PURE__ */ jsx(
76
+ Field.Root,
77
+ {
78
+ hint: description?.id ? formatMessage(description) : hint,
79
+ error: fieldError,
80
+ name,
81
+ required,
82
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 1, children: [
83
+ /* @__PURE__ */ jsx(Field.Label, { children: intlLabel?.id ? formatMessage(intlLabel) : label }),
84
+ possibleOptions.length === 0 ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral400", children: "No options available. Please configure options in the field settings." }) : /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsx(Flex, { gap: 2, direction: "column", alignItems: "stretch", children: possibleOptions.map((option) => {
85
+ const isChecked = sanitizedValue.some((v) => v.value === option.value);
86
+ const isDisabled = disabled || sanitizedValue.length >= attribute["max"] && !isChecked;
87
+ return /* @__PURE__ */ jsx(
88
+ Checkbox,
89
+ {
90
+ checked: isChecked,
91
+ disabled: isDisabled,
92
+ onCheckedChange: (checked) => handleCheckboxChange(option.value, checked),
93
+ children: /* @__PURE__ */ jsx(CapitalizedText, { children: formatMessage({
94
+ id: option.label,
95
+ defaultMessage: option.label
96
+ }) })
97
+ },
98
+ option.value
99
+ );
100
+ }) }) }),
101
+ /* @__PURE__ */ jsx(Field.Hint, {}),
102
+ /* @__PURE__ */ jsx(Field.Error, {})
103
+ ] })
104
+ }
105
+ );
106
+ };
107
+ export {
108
+ MultiSelect as default
109
+ };
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const designSystem = require("@strapi/design-system");
5
+ const admin = require("@strapi/strapi/admin");
6
+ const react = require("react");
7
+ const reactIntl = require("react-intl");
8
+ const styled = require("styled-components");
9
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
11
+ const CapitalizedText = styled__default.default.p`
12
+ &::first-letter {
13
+ text-transform: uppercase;
14
+ }
15
+ `;
16
+ const MultiSelect = ({
17
+ hint,
18
+ label,
19
+ name,
20
+ intlLabel,
21
+ required,
22
+ attribute,
23
+ description,
24
+ placeholder,
25
+ disabled
26
+ }) => {
27
+ const { formatMessage } = reactIntl.useIntl();
28
+ const { onChange, value, error } = admin.useField(name);
29
+ const possibleOptions = react.useMemo(() => {
30
+ return (attribute["options"] || []).map((option) => {
31
+ const [label2, value2] = [...option.split(/:(.*)/s), option];
32
+ if (!label2 || !value2) return null;
33
+ return { label: label2, value: value2 };
34
+ }).filter(Boolean);
35
+ }, [attribute]);
36
+ const sanitizedValue = react.useMemo(() => {
37
+ let parsedValue;
38
+ try {
39
+ parsedValue = typeof value !== "string" ? value || [] : JSON.parse(value || "[]");
40
+ } catch (e) {
41
+ parsedValue = [];
42
+ }
43
+ return Array.isArray(parsedValue) ? parsedValue.map(
44
+ (val) => possibleOptions.find((option) => option.value === val)
45
+ ).filter((option) => !!option) : [];
46
+ }, [value, possibleOptions]);
47
+ const fieldError = react.useMemo(() => {
48
+ if (error) return error;
49
+ const { min, max } = attribute;
50
+ const hasNoOptions = required && !possibleOptions.length;
51
+ const belowMin = sanitizedValue.length < min && (required || sanitizedValue.length > 0);
52
+ const aboveMax = sanitizedValue.length > max;
53
+ if (hasNoOptions) {
54
+ return "No options, but field is required";
55
+ }
56
+ if (belowMin) {
57
+ return `Select at least ${min} options`;
58
+ }
59
+ if (aboveMax) {
60
+ return `Select at most ${max} options`;
61
+ }
62
+ return null;
63
+ }, [required, error, possibleOptions, sanitizedValue, attribute]);
64
+ const handleCheckboxChange = (optionValue, isChecked) => {
65
+ let newValues;
66
+ if (isChecked) {
67
+ newValues = [...sanitizedValue.map((v) => v.value), optionValue];
68
+ } else {
69
+ newValues = sanitizedValue.map((v) => v.value).filter((v) => v !== optionValue);
70
+ }
71
+ onChange({
72
+ target: {
73
+ name,
74
+ value: newValues.length ? JSON.stringify(newValues) : null,
75
+ type: attribute.type
76
+ }
77
+ });
78
+ };
79
+ return /* @__PURE__ */ jsxRuntime.jsx(
80
+ designSystem.Field.Root,
81
+ {
82
+ hint: description?.id ? formatMessage(description) : hint,
83
+ error: fieldError,
84
+ name,
85
+ required,
86
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 1, children: [
87
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: intlLabel?.id ? formatMessage(intlLabel) : label }),
88
+ possibleOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral400", children: "No options available. Please configure options in the field settings." }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, direction: "column", alignItems: "stretch", children: possibleOptions.map((option) => {
89
+ const isChecked = sanitizedValue.some((v) => v.value === option.value);
90
+ const isDisabled = disabled || sanitizedValue.length >= attribute["max"] && !isChecked;
91
+ return /* @__PURE__ */ jsxRuntime.jsx(
92
+ designSystem.Checkbox,
93
+ {
94
+ checked: isChecked,
95
+ disabled: isDisabled,
96
+ onCheckedChange: (checked) => handleCheckboxChange(option.value, checked),
97
+ children: /* @__PURE__ */ jsxRuntime.jsx(CapitalizedText, { children: formatMessage({
98
+ id: option.label,
99
+ defaultMessage: option.label
100
+ }) })
101
+ },
102
+ option.value
103
+ );
104
+ }) }) }),
105
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
106
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
107
+ ] })
108
+ }
109
+ );
110
+ };
111
+ exports.default = MultiSelect;
@@ -1,3 +1,159 @@
1
1
  "use strict";
2
- const index = require("../_chunks/index-dXy7p2G8.js");
3
- module.exports = index.index;
2
+ const react = require("react");
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const designSystem = require("@strapi/design-system");
5
+ const icons = require("@strapi/icons");
6
+ const styled = require("styled-components");
7
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
8
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
9
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
10
+ const v = glob[path];
11
+ if (v) {
12
+ return typeof v === "function" ? v() : Promise.resolve(v);
13
+ }
14
+ return new Promise((_, reject) => {
15
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
16
+ reject.bind(
17
+ null,
18
+ new Error(
19
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
20
+ )
21
+ )
22
+ );
23
+ });
24
+ };
25
+ const PLUGIN_ID = "multiselect-checkbox";
26
+ const Initializer = ({ setPlugin }) => {
27
+ const ref = react.useRef(setPlugin);
28
+ react.useEffect(() => {
29
+ ref.current(PLUGIN_ID);
30
+ }, []);
31
+ return null;
32
+ };
33
+ const IconBox = styled__default.default(designSystem.Flex)`
34
+ background-color: #f0f0ff; /* primary100 */
35
+ border: 1px solid #d9d8ff; /* primary200 */
36
+
37
+ svg > path {
38
+ fill: #4945ff; /* primary600 */
39
+ }
40
+ `;
41
+ const PluginIcon = () => {
42
+ return /* @__PURE__ */ jsxRuntime.jsx(IconBox, { justifyContent: "center", alignItems: "center", width: 7, height: 6, hasRadius: true, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}) });
43
+ };
44
+ const prefixKey = (key) => `${PLUGIN_ID}.${key}`;
45
+ const index = {
46
+ register(app) {
47
+ app.customFields.register({
48
+ name: "multiselect-checkbox",
49
+ pluginId: `${PLUGIN_ID}`,
50
+ type: "text",
51
+ icon: PluginIcon,
52
+ intlLabel: {
53
+ id: `${PLUGIN_ID}.label`,
54
+ defaultMessage: "Multiselect Checkbox"
55
+ },
56
+ intlDescription: {
57
+ id: `${PLUGIN_ID}.description`,
58
+ defaultMessage: "Select multiple options using checkboxes. Data stored as JSON array."
59
+ },
60
+ components: {
61
+ Input: async () => Promise.resolve().then(() => require("../_chunks/index-DRjsLClm.js"))
62
+ },
63
+ options: {
64
+ base: [
65
+ {
66
+ sectionTitle: null,
67
+ items: [
68
+ {
69
+ name: "options",
70
+ type: "textarea-enum",
71
+ intlLabel: {
72
+ id: prefixKey("options.available-options.label"),
73
+ defaultMessage: "Options (one per line)"
74
+ },
75
+ description: {
76
+ id: prefixKey("options.available-options.description"),
77
+ defaultMessage: "Enter one option per line."
78
+ },
79
+ placeholder: {
80
+ id: prefixKey("options.available-options.placeholder"),
81
+ defaultMessage: "Option 1\nOption 2\nOption 3"
82
+ }
83
+ },
84
+ {
85
+ name: "default",
86
+ type: "json",
87
+ intlLabel: {
88
+ id: prefixKey("options.default.label"),
89
+ defaultMessage: "Default value"
90
+ },
91
+ description: {
92
+ id: prefixKey("options.default.description"),
93
+ defaultMessage: 'Set the default value in JSON format, ex: ["Option 1", "Option 2"]'
94
+ },
95
+ defaultValue: "[]"
96
+ }
97
+ ]
98
+ }
99
+ ],
100
+ //
101
+ // Strapi default advanced options.
102
+ //
103
+ advanced: [
104
+ {
105
+ sectionTitle: {
106
+ id: "global.settings",
107
+ defaultMessage: "Settings"
108
+ },
109
+ items: [
110
+ {
111
+ name: "required",
112
+ type: "checkbox",
113
+ intlLabel: {
114
+ id: "content-type-builder.form.attribute.item.requiredField",
115
+ defaultMessage: "Required field"
116
+ },
117
+ description: {
118
+ id: "content-type-builder.form.attribute.item.requiredField.description",
119
+ defaultMessage: "You won't be able to create an entry if this field is empty"
120
+ }
121
+ },
122
+ {
123
+ name: "private",
124
+ type: "checkbox",
125
+ intlLabel: {
126
+ id: "content-type-builder.form.attribute.item.privateField",
127
+ defaultMessage: "Private field"
128
+ },
129
+ description: {
130
+ id: "content-type-builder.form.attribute.item.privateField.description",
131
+ defaultMessage: "This field will not show up in the API response"
132
+ }
133
+ }
134
+ ]
135
+ }
136
+ ]
137
+ }
138
+ });
139
+ app.registerPlugin({
140
+ id: PLUGIN_ID,
141
+ initializer: Initializer,
142
+ isReady: false,
143
+ name: PLUGIN_ID
144
+ });
145
+ },
146
+ async registerTrads({ locales }) {
147
+ return Promise.all(
148
+ locales.map(async (locale) => {
149
+ try {
150
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("../_chunks/en-pkXKBXLR.js")) }), `./translations/${locale}.json`, 3);
151
+ return { data, locale };
152
+ } catch {
153
+ return { data: {}, locale };
154
+ }
155
+ })
156
+ );
157
+ }
158
+ };
159
+ module.exports = index;
@@ -1,4 +1,158 @@
1
- import { i } from "../_chunks/index-Wx6rXcrm.mjs";
1
+ import { useRef, useEffect } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Flex } from "@strapi/design-system";
4
+ import { Check } from "@strapi/icons";
5
+ import styled from "styled-components";
6
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
7
+ const v = glob[path];
8
+ if (v) {
9
+ return typeof v === "function" ? v() : Promise.resolve(v);
10
+ }
11
+ return new Promise((_, reject) => {
12
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
13
+ reject.bind(
14
+ null,
15
+ new Error(
16
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
17
+ )
18
+ )
19
+ );
20
+ });
21
+ };
22
+ const PLUGIN_ID = "multiselect-checkbox";
23
+ const Initializer = ({ setPlugin }) => {
24
+ const ref = useRef(setPlugin);
25
+ useEffect(() => {
26
+ ref.current(PLUGIN_ID);
27
+ }, []);
28
+ return null;
29
+ };
30
+ const IconBox = styled(Flex)`
31
+ background-color: #f0f0ff; /* primary100 */
32
+ border: 1px solid #d9d8ff; /* primary200 */
33
+
34
+ svg > path {
35
+ fill: #4945ff; /* primary600 */
36
+ }
37
+ `;
38
+ const PluginIcon = () => {
39
+ return /* @__PURE__ */ jsx(IconBox, { justifyContent: "center", alignItems: "center", width: 7, height: 6, hasRadius: true, "aria-hidden": true, children: /* @__PURE__ */ jsx(Check, {}) });
40
+ };
41
+ const prefixKey = (key) => `${PLUGIN_ID}.${key}`;
42
+ const index = {
43
+ register(app) {
44
+ app.customFields.register({
45
+ name: "multiselect-checkbox",
46
+ pluginId: `${PLUGIN_ID}`,
47
+ type: "text",
48
+ icon: PluginIcon,
49
+ intlLabel: {
50
+ id: `${PLUGIN_ID}.label`,
51
+ defaultMessage: "Multiselect Checkbox"
52
+ },
53
+ intlDescription: {
54
+ id: `${PLUGIN_ID}.description`,
55
+ defaultMessage: "Select multiple options using checkboxes. Data stored as JSON array."
56
+ },
57
+ components: {
58
+ Input: async () => import("../_chunks/index-CG0vMCYh.mjs")
59
+ },
60
+ options: {
61
+ base: [
62
+ {
63
+ sectionTitle: null,
64
+ items: [
65
+ {
66
+ name: "options",
67
+ type: "textarea-enum",
68
+ intlLabel: {
69
+ id: prefixKey("options.available-options.label"),
70
+ defaultMessage: "Options (one per line)"
71
+ },
72
+ description: {
73
+ id: prefixKey("options.available-options.description"),
74
+ defaultMessage: "Enter one option per line."
75
+ },
76
+ placeholder: {
77
+ id: prefixKey("options.available-options.placeholder"),
78
+ defaultMessage: "Option 1\nOption 2\nOption 3"
79
+ }
80
+ },
81
+ {
82
+ name: "default",
83
+ type: "json",
84
+ intlLabel: {
85
+ id: prefixKey("options.default.label"),
86
+ defaultMessage: "Default value"
87
+ },
88
+ description: {
89
+ id: prefixKey("options.default.description"),
90
+ defaultMessage: 'Set the default value in JSON format, ex: ["Option 1", "Option 2"]'
91
+ },
92
+ defaultValue: "[]"
93
+ }
94
+ ]
95
+ }
96
+ ],
97
+ //
98
+ // Strapi default advanced options.
99
+ //
100
+ advanced: [
101
+ {
102
+ sectionTitle: {
103
+ id: "global.settings",
104
+ defaultMessage: "Settings"
105
+ },
106
+ items: [
107
+ {
108
+ name: "required",
109
+ type: "checkbox",
110
+ intlLabel: {
111
+ id: "content-type-builder.form.attribute.item.requiredField",
112
+ defaultMessage: "Required field"
113
+ },
114
+ description: {
115
+ id: "content-type-builder.form.attribute.item.requiredField.description",
116
+ defaultMessage: "You won't be able to create an entry if this field is empty"
117
+ }
118
+ },
119
+ {
120
+ name: "private",
121
+ type: "checkbox",
122
+ intlLabel: {
123
+ id: "content-type-builder.form.attribute.item.privateField",
124
+ defaultMessage: "Private field"
125
+ },
126
+ description: {
127
+ id: "content-type-builder.form.attribute.item.privateField.description",
128
+ defaultMessage: "This field will not show up in the API response"
129
+ }
130
+ }
131
+ ]
132
+ }
133
+ ]
134
+ }
135
+ });
136
+ app.registerPlugin({
137
+ id: PLUGIN_ID,
138
+ initializer: Initializer,
139
+ isReady: false,
140
+ name: PLUGIN_ID
141
+ });
142
+ },
143
+ async registerTrads({ locales }) {
144
+ return Promise.all(
145
+ locales.map(async (locale) => {
146
+ try {
147
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("../_chunks/en-tWwdhLO5.mjs") }), `./translations/${locale}.json`, 3);
148
+ return { data, locale };
149
+ } catch {
150
+ return { data: {}, locale };
151
+ }
152
+ })
153
+ );
154
+ }
155
+ };
2
156
  export {
3
- i as default
157
+ index as default
4
158
  };
@@ -1,23 +1,12 @@
1
- import type { FieldValue, InputProps } from '@strapi/strapi/admin';
2
- /** The properties for our `Multiselect` component below. */
3
- type Props = InputProps & FieldValue & {
4
- attribute: {
5
- options: string[] | undefined;
6
- };
7
- };
8
- /**
9
- * `Multiselect` is a custom form component that allows users to select multiple options
10
- * via checkboxes. The selected values are stored as an array of strings.
11
- *
12
- * ### Props:
13
- * - `attribute`: An object containing the list of selectable `options: string[]`.
14
- * - `disabled`: (Optional) Disables all checkboxes when `true`.
15
- * - `hint`: (Optional) A string providing contextual help, shown below the field.
16
- * - `name`: The name of the form field (used in the synthetic `onChange` event).
17
- * - `label`: A label for the field, displayed above the checkboxes.
18
- * - `onChange`: A handler that receives the updated selection as an array of strings.
19
- * - `required`: (Optional) Indicates if the field is required.
20
- * - `value`: The current value as an array of selected options.
21
- */
22
- declare const Multiselect: (props: Props) => import("react/jsx-runtime").JSX.Element;
23
- export default Multiselect;
1
+ declare const MultiSelect: ({ hint, label, name, intlLabel, required, attribute, description, placeholder, disabled, }: {
2
+ hint: string;
3
+ label: string;
4
+ name: string;
5
+ intlLabel: any;
6
+ required: boolean;
7
+ attribute: any;
8
+ description: any;
9
+ placeholder: string;
10
+ disabled: boolean;
11
+ }) => import("react/jsx-runtime").JSX.Element;
12
+ export default MultiSelect;
@@ -19,12 +19,8 @@ const register = ({ strapi }) => {
19
19
  strapi.customFields.register({
20
20
  name: "multiselect-checkbox",
21
21
  plugin: "multiselect-checkbox",
22
- // The data type stored in the database - JSON array
23
- type: "json",
24
- inputSize: {
25
- default: 12,
26
- isResizable: true
27
- }
22
+ // The data type stored in the database - text (serialized JSON)
23
+ type: "text"
28
24
  });
29
25
  };
30
26
  const contentAPIRoutes = [];
@@ -18,12 +18,8 @@ const register = ({ strapi }) => {
18
18
  strapi.customFields.register({
19
19
  name: "multiselect-checkbox",
20
20
  plugin: "multiselect-checkbox",
21
- // The data type stored in the database - JSON array
22
- type: "json",
23
- inputSize: {
24
- default: 12,
25
- isResizable: true
26
- }
21
+ // The data type stored in the database - text (serialized JSON)
22
+ type: "text"
27
23
  });
28
24
  };
29
25
  const contentAPIRoutes = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tunghtml/strapi-plugin-multiselect-checkbox",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A Strapi v5 custom field plugin with checkbox UI that stores selected values as an array of strings (JSON type) instead of comma-separated strings.",
5
5
  "keywords": [
6
6
  "strapi",
@@ -1,51 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const jsxRuntime = require("react/jsx-runtime");
4
- const designSystem = require("@strapi/design-system");
5
- const reactIntl = require("react-intl");
6
- const styled = require("styled-components");
7
- const index = require("./index-dXy7p2G8.js");
8
- const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
9
- const styled__default = /* @__PURE__ */ _interopDefault(styled);
10
- const config = {
11
- /**
12
- * The default options used as fallbacks in case the user-defined values are missing.
13
- */
14
- defaultOptions: []
15
- };
16
- const CapitalizedText = styled__default.default.p`
17
- &::first-letter {
18
- text-transform: uppercase;
19
- }
20
- `;
21
- const EmptyState = () => {
22
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral400", children: /* @__PURE__ */ jsxRuntime.jsx(reactIntl.FormattedMessage, { id: index.prefixKey("empty-state.text") }) });
23
- };
24
- const Multiselect = (props) => {
25
- const { attribute, disabled, hint, label, name, onChange, required, type, value } = props;
26
- const availableOptions = attribute?.options || config.defaultOptions;
27
- const selectedOptions = Array.isArray(value) ? value : [];
28
- const updateValue = (value2) => onChange({ target: { name, value: value2, type } });
29
- const updateSelectedOptions = (option, isSelected) => {
30
- const nextSelectedOptions = isSelected ? selectedOptions.concat(option) : selectedOptions.filter((selectedOption) => selectedOption !== option);
31
- const sortedNextSelectedOptions = nextSelectedOptions.sort(
32
- (lhs, rhs) => availableOptions.indexOf(lhs) - availableOptions.indexOf(rhs)
33
- );
34
- updateValue(sortedNextSelectedOptions);
35
- };
36
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { hint, name, required, children: [
37
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: label }),
38
- availableOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(EmptyState, {}) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, direction: "column", alignItems: "leading", children: availableOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
39
- designSystem.Checkbox,
40
- {
41
- checked: selectedOptions.includes(option),
42
- disabled: disabled ?? false,
43
- onCheckedChange: (isSelected) => updateSelectedOptions(option, isSelected),
44
- children: /* @__PURE__ */ jsxRuntime.jsx(CapitalizedText, { children: option })
45
- },
46
- option
47
- )) }) }),
48
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
49
- ] });
50
- };
51
- exports.default = Multiselect;
@@ -1,159 +0,0 @@
1
- import { useRef, useEffect } from "react";
2
- import { jsx } from "react/jsx-runtime";
3
- import { Flex } from "@strapi/design-system";
4
- import { Check } from "@strapi/icons";
5
- import styled from "styled-components";
6
- const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
7
- const v = glob[path];
8
- if (v) {
9
- return typeof v === "function" ? v() : Promise.resolve(v);
10
- }
11
- return new Promise((_, reject) => {
12
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
13
- reject.bind(
14
- null,
15
- new Error(
16
- "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
17
- )
18
- )
19
- );
20
- });
21
- };
22
- const PLUGIN_ID = "multiselect-checkbox";
23
- const Initializer = ({ setPlugin }) => {
24
- const ref = useRef(setPlugin);
25
- useEffect(() => {
26
- ref.current(PLUGIN_ID);
27
- }, []);
28
- return null;
29
- };
30
- const IconBox = styled(Flex)`
31
- background-color: #f0f0ff; /* primary100 */
32
- border: 1px solid #d9d8ff; /* primary200 */
33
-
34
- svg > path {
35
- fill: #4945ff; /* primary600 */
36
- }
37
- `;
38
- const PluginIcon = () => {
39
- return /* @__PURE__ */ jsx(IconBox, { justifyContent: "center", alignItems: "center", width: 7, height: 6, hasRadius: true, "aria-hidden": true, children: /* @__PURE__ */ jsx(Check, {}) });
40
- };
41
- const prefixKey = (key) => `${PLUGIN_ID}.${key}`;
42
- const index = {
43
- register(app) {
44
- app.customFields.register({
45
- name: "multiselect-checkbox",
46
- pluginId: `${PLUGIN_ID}`,
47
- type: "json",
48
- icon: PluginIcon,
49
- intlLabel: {
50
- id: `${PLUGIN_ID}.label`,
51
- defaultMessage: "Multiselect Checkbox"
52
- },
53
- intlDescription: {
54
- id: `${PLUGIN_ID}.description`,
55
- defaultMessage: "Select multiple options using checkboxes. Data stored as JSON array."
56
- },
57
- components: {
58
- Input: async () => import("./index-mocR0IHA.mjs")
59
- },
60
- options: {
61
- base: [
62
- {
63
- sectionTitle: null,
64
- items: [
65
- {
66
- name: "options",
67
- type: "textarea-enum",
68
- intlLabel: {
69
- id: prefixKey("options.available-options.label"),
70
- defaultMessage: "Options (one per line)"
71
- },
72
- description: {
73
- id: prefixKey("options.available-options.description"),
74
- defaultMessage: "Enter one option per line."
75
- },
76
- placeholder: {
77
- id: prefixKey("options.available-options.placeholder"),
78
- defaultMessage: "Option 1\nOption 2\nOption 3"
79
- }
80
- },
81
- {
82
- name: "default",
83
- type: "json",
84
- intlLabel: {
85
- id: prefixKey("options.default.label"),
86
- defaultMessage: "Default value"
87
- },
88
- description: {
89
- id: prefixKey("options.default.description"),
90
- defaultMessage: 'Set the default value in JSON format, ex: ["Option 1", "Option 2"]'
91
- },
92
- defaultValue: "[]"
93
- }
94
- ]
95
- }
96
- ],
97
- //
98
- // Strapi default advanced options.
99
- //
100
- advanced: [
101
- {
102
- sectionTitle: {
103
- id: "global.settings",
104
- defaultMessage: "Settings"
105
- },
106
- items: [
107
- {
108
- name: "required",
109
- type: "checkbox",
110
- intlLabel: {
111
- id: "content-type-builder.form.attribute.item.requiredField",
112
- defaultMessage: "Required field"
113
- },
114
- description: {
115
- id: "content-type-builder.form.attribute.item.requiredField.description",
116
- defaultMessage: "You won't be able to create an entry if this field is empty"
117
- }
118
- },
119
- {
120
- name: "private",
121
- type: "checkbox",
122
- intlLabel: {
123
- id: "content-type-builder.form.attribute.item.privateField",
124
- defaultMessage: "Private field"
125
- },
126
- description: {
127
- id: "content-type-builder.form.attribute.item.privateField.description",
128
- defaultMessage: "This field will not show up in the API response"
129
- }
130
- }
131
- ]
132
- }
133
- ]
134
- }
135
- });
136
- app.registerPlugin({
137
- id: PLUGIN_ID,
138
- initializer: Initializer,
139
- isReady: false,
140
- name: PLUGIN_ID
141
- });
142
- },
143
- async registerTrads({ locales }) {
144
- return Promise.all(
145
- locales.map(async (locale) => {
146
- try {
147
- const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-tWwdhLO5.mjs") }), `./translations/${locale}.json`, 3);
148
- return { data, locale };
149
- } catch {
150
- return { data: {}, locale };
151
- }
152
- })
153
- );
154
- }
155
- };
156
- export {
157
- index as i,
158
- prefixKey as p
159
- };
@@ -1,160 +0,0 @@
1
- "use strict";
2
- const react = require("react");
3
- const jsxRuntime = require("react/jsx-runtime");
4
- const designSystem = require("@strapi/design-system");
5
- const icons = require("@strapi/icons");
6
- const styled = require("styled-components");
7
- const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
8
- const styled__default = /* @__PURE__ */ _interopDefault(styled);
9
- const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
10
- const v = glob[path];
11
- if (v) {
12
- return typeof v === "function" ? v() : Promise.resolve(v);
13
- }
14
- return new Promise((_, reject) => {
15
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
16
- reject.bind(
17
- null,
18
- new Error(
19
- "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
20
- )
21
- )
22
- );
23
- });
24
- };
25
- const PLUGIN_ID = "multiselect-checkbox";
26
- const Initializer = ({ setPlugin }) => {
27
- const ref = react.useRef(setPlugin);
28
- react.useEffect(() => {
29
- ref.current(PLUGIN_ID);
30
- }, []);
31
- return null;
32
- };
33
- const IconBox = styled__default.default(designSystem.Flex)`
34
- background-color: #f0f0ff; /* primary100 */
35
- border: 1px solid #d9d8ff; /* primary200 */
36
-
37
- svg > path {
38
- fill: #4945ff; /* primary600 */
39
- }
40
- `;
41
- const PluginIcon = () => {
42
- return /* @__PURE__ */ jsxRuntime.jsx(IconBox, { justifyContent: "center", alignItems: "center", width: 7, height: 6, hasRadius: true, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}) });
43
- };
44
- const prefixKey = (key) => `${PLUGIN_ID}.${key}`;
45
- const index = {
46
- register(app) {
47
- app.customFields.register({
48
- name: "multiselect-checkbox",
49
- pluginId: `${PLUGIN_ID}`,
50
- type: "json",
51
- icon: PluginIcon,
52
- intlLabel: {
53
- id: `${PLUGIN_ID}.label`,
54
- defaultMessage: "Multiselect Checkbox"
55
- },
56
- intlDescription: {
57
- id: `${PLUGIN_ID}.description`,
58
- defaultMessage: "Select multiple options using checkboxes. Data stored as JSON array."
59
- },
60
- components: {
61
- Input: async () => Promise.resolve().then(() => require("./index-BIpQIwH7.js"))
62
- },
63
- options: {
64
- base: [
65
- {
66
- sectionTitle: null,
67
- items: [
68
- {
69
- name: "options",
70
- type: "textarea-enum",
71
- intlLabel: {
72
- id: prefixKey("options.available-options.label"),
73
- defaultMessage: "Options (one per line)"
74
- },
75
- description: {
76
- id: prefixKey("options.available-options.description"),
77
- defaultMessage: "Enter one option per line."
78
- },
79
- placeholder: {
80
- id: prefixKey("options.available-options.placeholder"),
81
- defaultMessage: "Option 1\nOption 2\nOption 3"
82
- }
83
- },
84
- {
85
- name: "default",
86
- type: "json",
87
- intlLabel: {
88
- id: prefixKey("options.default.label"),
89
- defaultMessage: "Default value"
90
- },
91
- description: {
92
- id: prefixKey("options.default.description"),
93
- defaultMessage: 'Set the default value in JSON format, ex: ["Option 1", "Option 2"]'
94
- },
95
- defaultValue: "[]"
96
- }
97
- ]
98
- }
99
- ],
100
- //
101
- // Strapi default advanced options.
102
- //
103
- advanced: [
104
- {
105
- sectionTitle: {
106
- id: "global.settings",
107
- defaultMessage: "Settings"
108
- },
109
- items: [
110
- {
111
- name: "required",
112
- type: "checkbox",
113
- intlLabel: {
114
- id: "content-type-builder.form.attribute.item.requiredField",
115
- defaultMessage: "Required field"
116
- },
117
- description: {
118
- id: "content-type-builder.form.attribute.item.requiredField.description",
119
- defaultMessage: "You won't be able to create an entry if this field is empty"
120
- }
121
- },
122
- {
123
- name: "private",
124
- type: "checkbox",
125
- intlLabel: {
126
- id: "content-type-builder.form.attribute.item.privateField",
127
- defaultMessage: "Private field"
128
- },
129
- description: {
130
- id: "content-type-builder.form.attribute.item.privateField.description",
131
- defaultMessage: "This field will not show up in the API response"
132
- }
133
- }
134
- ]
135
- }
136
- ]
137
- }
138
- });
139
- app.registerPlugin({
140
- id: PLUGIN_ID,
141
- initializer: Initializer,
142
- isReady: false,
143
- name: PLUGIN_ID
144
- });
145
- },
146
- async registerTrads({ locales }) {
147
- return Promise.all(
148
- locales.map(async (locale) => {
149
- try {
150
- const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-pkXKBXLR.js")) }), `./translations/${locale}.json`, 3);
151
- return { data, locale };
152
- } catch {
153
- return { data: {}, locale };
154
- }
155
- })
156
- );
157
- }
158
- };
159
- exports.index = index;
160
- exports.prefixKey = prefixKey;
@@ -1,49 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import { Field, Box, Flex, Checkbox, Typography } from "@strapi/design-system";
3
- import { FormattedMessage } from "react-intl";
4
- import styled from "styled-components";
5
- import { p as prefixKey } from "./index-Wx6rXcrm.mjs";
6
- const config = {
7
- /**
8
- * The default options used as fallbacks in case the user-defined values are missing.
9
- */
10
- defaultOptions: []
11
- };
12
- const CapitalizedText = styled.p`
13
- &::first-letter {
14
- text-transform: uppercase;
15
- }
16
- `;
17
- const EmptyState = () => {
18
- return /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral400", children: /* @__PURE__ */ jsx(FormattedMessage, { id: prefixKey("empty-state.text") }) });
19
- };
20
- const Multiselect = (props) => {
21
- const { attribute, disabled, hint, label, name, onChange, required, type, value } = props;
22
- const availableOptions = attribute?.options || config.defaultOptions;
23
- const selectedOptions = Array.isArray(value) ? value : [];
24
- const updateValue = (value2) => onChange({ target: { name, value: value2, type } });
25
- const updateSelectedOptions = (option, isSelected) => {
26
- const nextSelectedOptions = isSelected ? selectedOptions.concat(option) : selectedOptions.filter((selectedOption) => selectedOption !== option);
27
- const sortedNextSelectedOptions = nextSelectedOptions.sort(
28
- (lhs, rhs) => availableOptions.indexOf(lhs) - availableOptions.indexOf(rhs)
29
- );
30
- updateValue(sortedNextSelectedOptions);
31
- };
32
- return /* @__PURE__ */ jsxs(Field.Root, { hint, name, required, children: [
33
- /* @__PURE__ */ jsx(Field.Label, { children: label }),
34
- availableOptions.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {}) : /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsx(Flex, { gap: 2, direction: "column", alignItems: "leading", children: availableOptions.map((option) => /* @__PURE__ */ jsx(
35
- Checkbox,
36
- {
37
- checked: selectedOptions.includes(option),
38
- disabled: disabled ?? false,
39
- onCheckedChange: (isSelected) => updateSelectedOptions(option, isSelected),
40
- children: /* @__PURE__ */ jsx(CapitalizedText, { children: option })
41
- },
42
- option
43
- )) }) }),
44
- /* @__PURE__ */ jsx(Field.Hint, {})
45
- ] });
46
- };
47
- export {
48
- Multiselect as default
49
- };