@tunghtml/strapi-plugin-multiselect-checkbox 1.0.0
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/LICENSE +19 -0
- package/README.md +165 -0
- package/dist/_chunks/en-pkXKBXLR.js +14 -0
- package/dist/_chunks/en-tWwdhLO5.mjs +14 -0
- package/dist/_chunks/index-8Txh7SeC.js +54 -0
- package/dist/_chunks/index-CTLgAy6y.mjs +136 -0
- package/dist/_chunks/index-DTMA037h.js +135 -0
- package/dist/_chunks/index-tpMZnVWK.mjs +52 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/components/Initializer/index.d.ts +5 -0
- package/dist/admin/src/components/Multiselect/index.d.ts +25 -0
- package/dist/admin/src/components/PluginIcon/index.d.ts +2 -0
- package/dist/admin/src/index.d.ts +10 -0
- package/dist/admin/src/pluginId.d.ts +1 -0
- package/dist/admin/src/utils/prefixKey.d.ts +2 -0
- package/dist/server/index.js +49 -0
- package/dist/server/index.mjs +50 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +5 -0
- package/dist/server/src/content-types/index.d.ts +2 -0
- package/dist/server/src/controllers/controller.d.ts +5 -0
- package/dist/server/src/controllers/index.d.ts +6 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/index.d.ts +35 -0
- package/dist/server/src/middlewares/index.d.ts +2 -0
- package/dist/server/src/policies/index.d.ts +2 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/content-api.d.ts +2 -0
- package/dist/server/src/routes/index.d.ts +7 -0
- package/dist/server/src/services/index.d.ts +6 -0
- package/dist/server/src/services/service.d.ts +5 -0
- package/package.json +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2025 Felix Mau <me@felix.hamburg>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# @tunghtml/strapi-plugin-multiselect-checkbox
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- ✅ **Checkbox UI** for intuitive multi-selection
|
|
12
|
+
- ✅ **Array Storage** - Stores data as `["Option 1", "Option 2"]` instead of `"Option 1,Option 2"`
|
|
13
|
+
- ✅ **JSON Type** - Uses Strapi's JSON field type for proper array handling
|
|
14
|
+
- ✅ **Strapi v5** compatible
|
|
15
|
+
- ✅ **TypeScript** support
|
|
16
|
+
- ✅ **Easy Configuration** - Define options directly in Content-Type Builder
|
|
17
|
+
|
|
18
|
+
## 📦 Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @tunghtml/strapi-plugin-multiselect-checkbox
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
or with yarn:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
yarn add @tunghtml/strapi-plugin-multiselect-checkbox
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 🔧 Configuration
|
|
31
|
+
|
|
32
|
+
Add the plugin to your `config/plugins.js` (or `config/plugins.ts`):
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
module.exports = {
|
|
36
|
+
// ...
|
|
37
|
+
'multiselect-checkbox': {
|
|
38
|
+
enabled: true,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then restart your Strapi application.
|
|
44
|
+
|
|
45
|
+
## 🎯 Usage
|
|
46
|
+
|
|
47
|
+
### 1. Add Field in Content-Type Builder
|
|
48
|
+
|
|
49
|
+
1. Go to **Content-Type Builder**
|
|
50
|
+
2. Select a content type or create a new one
|
|
51
|
+
3. Click **Add another field**
|
|
52
|
+
4. Choose **Custom** tab
|
|
53
|
+
5. Select **Multiselect Checkbox**
|
|
54
|
+
6. Configure your field:
|
|
55
|
+
- **Name**: Field name
|
|
56
|
+
- **Available Options**: Enter options (one per line)
|
|
57
|
+
7. Click **Finish** and **Save**
|
|
58
|
+
|
|
59
|
+
### 2. Use in Content Manager
|
|
60
|
+
|
|
61
|
+
The field will appear as a list of checkboxes in the Content Manager. Select multiple options and save.
|
|
62
|
+
|
|
63
|
+
### 3. API Response
|
|
64
|
+
|
|
65
|
+
The API will return selected values as an array:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"id": 1,
|
|
70
|
+
"myField": ["Option 1", "Option 3", "Option 5"]
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 📊 Data Format Comparison
|
|
75
|
+
|
|
76
|
+
### This Plugin (Array)
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"field": ["Option 1", "Option 2", "Option 3"]
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Other Plugins (String)
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"field": "Option 1,Option 2,Option 3"
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 🎨 Example Schema
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"kind": "collectionType",
|
|
97
|
+
"collectionName": "products",
|
|
98
|
+
"info": {
|
|
99
|
+
"singularName": "product",
|
|
100
|
+
"pluralName": "products",
|
|
101
|
+
"displayName": "Product"
|
|
102
|
+
},
|
|
103
|
+
"attributes": {
|
|
104
|
+
"sizes": {
|
|
105
|
+
"type": "customField",
|
|
106
|
+
"customField": "plugin::multiselect-checkbox.multiselect-checkbox",
|
|
107
|
+
"options": {
|
|
108
|
+
"availableOptions": ["Small", "Medium", "Large", "XL"]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## 🔍 Querying Data
|
|
116
|
+
|
|
117
|
+
Since data is stored as JSON array, you can query it using Strapi's filters:
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
// Find entries where field contains "Option 1"
|
|
121
|
+
const entries = await strapi.entityService.findMany('api::product.product', {
|
|
122
|
+
filters: {
|
|
123
|
+
sizes: {
|
|
124
|
+
$contains: 'Small',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 🛠️ Development
|
|
131
|
+
|
|
132
|
+
### Build the Plugin
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm run build
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Watch Mode
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
npm run watch
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 🙏 Credits
|
|
145
|
+
|
|
146
|
+
This plugin is based on [strapi-plugin-multiselect-field](https://github.com/fxm90/strapi-plugin-multiselect-field) by Felix M., modified to store data as arrays instead of comma-separated strings.
|
|
147
|
+
|
|
148
|
+
## 📝 License
|
|
149
|
+
|
|
150
|
+
MIT
|
|
151
|
+
|
|
152
|
+
## 🐛 Issues
|
|
153
|
+
|
|
154
|
+
Found a bug? Please report it on [GitHub Issues](https://github.com/finnwasabi/strapi-plugin-multiselect-checkbox/issues).
|
|
155
|
+
|
|
156
|
+
## 🤝 Contributing
|
|
157
|
+
|
|
158
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
159
|
+
|
|
160
|
+
## 📧 Author
|
|
161
|
+
|
|
162
|
+
**Tung Le**
|
|
163
|
+
|
|
164
|
+
- GitHub: [@finnwasabi](https://github.com/finnwasabi)
|
|
165
|
+
- Email: tunghtml@gmail.com
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const en = {
|
|
4
|
+
"multiselect-field.label": "Multiselect",
|
|
5
|
+
"multiselect-field.description": "A custom field for Strapi that allows users to select multiple options from a predefined list.",
|
|
6
|
+
"multiselect-field.options.available-options.label": "Available Options",
|
|
7
|
+
"multiselect-field.options.available-options.description": "One option per line.",
|
|
8
|
+
"multiselect-field.options.available-options.placeholder": "Option 1\nOption 2\nOption 3",
|
|
9
|
+
"multiselect-field.options.delimiter.label": "Delimiter",
|
|
10
|
+
"multiselect-field.options.delimiter.description": "The delimiter to use when storing the selected options.",
|
|
11
|
+
"multiselect-field.options.delimiter.placeholder": ",",
|
|
12
|
+
"multiselect-field.empty-state.text": "⚠️ Please provide the available options in the content builder."
|
|
13
|
+
};
|
|
14
|
+
exports.default = en;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const en = {
|
|
2
|
+
"multiselect-field.label": "Multiselect",
|
|
3
|
+
"multiselect-field.description": "A custom field for Strapi that allows users to select multiple options from a predefined list.",
|
|
4
|
+
"multiselect-field.options.available-options.label": "Available Options",
|
|
5
|
+
"multiselect-field.options.available-options.description": "One option per line.",
|
|
6
|
+
"multiselect-field.options.available-options.placeholder": "Option 1\nOption 2\nOption 3",
|
|
7
|
+
"multiselect-field.options.delimiter.label": "Delimiter",
|
|
8
|
+
"multiselect-field.options.delimiter.description": "The delimiter to use when storing the selected options.",
|
|
9
|
+
"multiselect-field.options.delimiter.placeholder": ",",
|
|
10
|
+
"multiselect-field.empty-state.text": "⚠️ Please provide the available options in the content builder."
|
|
11
|
+
};
|
|
12
|
+
export {
|
|
13
|
+
en as default
|
|
14
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
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-DTMA037h.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
|
+
availableOptions: []
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const CapitalizedText = styled__default.default.p`
|
|
19
|
+
&::first-letter {
|
|
20
|
+
text-transform: uppercase;
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
const EmptyState = () => {
|
|
24
|
+
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral400", children: /* @__PURE__ */ jsxRuntime.jsx(reactIntl.FormattedMessage, { id: index.prefixKey("empty-state.text") }) });
|
|
25
|
+
};
|
|
26
|
+
const Multiselect = (props) => {
|
|
27
|
+
const { attribute, disabled, hint, label, name, onChange, required, type, value } = props;
|
|
28
|
+
const { availableOptions = config.defaultOptions.availableOptions } = attribute.options;
|
|
29
|
+
const selectedOptions = Array.isArray(value) ? value : [];
|
|
30
|
+
const updateValue = (value2) => onChange({ target: { name, value: value2, type } });
|
|
31
|
+
const updateSelectedOptions = (option, isSelected) => {
|
|
32
|
+
const nextSelectedOptions = isSelected ? selectedOptions.concat(option) : selectedOptions.filter((selectedOption) => selectedOption !== option);
|
|
33
|
+
const sortedNextSelectedOptions = nextSelectedOptions.sort(
|
|
34
|
+
(lhs, rhs) => availableOptions.indexOf(lhs) - availableOptions.indexOf(rhs)
|
|
35
|
+
);
|
|
36
|
+
updateValue(sortedNextSelectedOptions);
|
|
37
|
+
};
|
|
38
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { hint, name, required, children: [
|
|
39
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: label }),
|
|
40
|
+
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(
|
|
41
|
+
designSystem.Checkbox,
|
|
42
|
+
{
|
|
43
|
+
option,
|
|
44
|
+
checked: selectedOptions.includes(option),
|
|
45
|
+
disabled: disabled ?? false,
|
|
46
|
+
onCheckedChange: (isSelected) => updateSelectedOptions(option, isSelected),
|
|
47
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(CapitalizedText, { children: option })
|
|
48
|
+
},
|
|
49
|
+
option
|
|
50
|
+
)) }) }),
|
|
51
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
|
|
52
|
+
] });
|
|
53
|
+
};
|
|
54
|
+
exports.default = Multiselect;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { useRef, useEffect } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
|
|
4
|
+
const v = glob[path];
|
|
5
|
+
if (v) {
|
|
6
|
+
return typeof v === "function" ? v() : Promise.resolve(v);
|
|
7
|
+
}
|
|
8
|
+
return new Promise((_, reject) => {
|
|
9
|
+
(typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
|
|
10
|
+
reject.bind(
|
|
11
|
+
null,
|
|
12
|
+
new Error(
|
|
13
|
+
"Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
|
|
14
|
+
)
|
|
15
|
+
)
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const PLUGIN_ID = "multiselect-checkbox";
|
|
20
|
+
const Initializer = ({ setPlugin }) => {
|
|
21
|
+
const ref = useRef(setPlugin);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
ref.current(PLUGIN_ID);
|
|
24
|
+
}, []);
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
const checkmarkNotepad = "data:image/svg+xml,%3c?xml%20version='1.0'%20encoding='iso-8859-1'?%3e%3c!--%20Uploaded%20to:%20SVG%20Repo,%20www.svgrepo.com,%20Generator:%20SVG%20Repo%20Mixer%20Tools%20--%3e%3csvg%20height='800px'%20width='800px'%20version='1.1'%20id='Layer_1'%20xmlns='http://www.w3.org/2000/svg'%20xmlns:xlink='http://www.w3.org/1999/xlink'%20viewBox='0%200%20512%20512'%20xml:space='preserve'%3e%3cpath%20style='fill:%23E1C3A0;'%20d='M406.069,512H105.931c-19.501,0-35.31-15.809-35.31-35.31V70.621c0-19.501,15.809-35.31,35.31-35.31%20h300.138c19.501,0,35.31,15.809,35.31,35.31V476.69C441.379,496.191,425.57,512,406.069,512z'/%3e%3cpath%20style='fill:%23EFF2FA;'%20d='M406.069,485.517H105.931c-4.875,0-8.828-3.953-8.828-8.828V70.621c0-4.875,3.953-8.828,8.828-8.828%20h300.138c4.875,0,8.828,3.953,8.828,8.828V476.69C414.897,481.565,410.944,485.517,406.069,485.517z'/%3e%3cpath%20style='fill:%23C7CFE2;'%20d='M308.966,35.31h-26.483v-8.828C282.483,11.857,270.626,0,256,0s-26.483,11.857-26.483,26.483v8.828%20h-26.483c-9.75,0-17.655,7.904-17.655,17.655v17.655h141.241V52.966C326.621,43.214,318.716,35.31,308.966,35.31z%20M256,35.31%20c-4.875,0-8.828-3.953-8.828-8.828s3.953-8.828,8.828-8.828s8.828,3.953,8.828,8.828S260.875,35.31,256,35.31z'/%3e%3cpath%20style='fill:%23AFB9D2;'%20d='M326.621,79.448H185.379c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h141.241c4.875,0,8.828,3.953,8.828,8.828l0,0C335.448,75.496,331.496,79.448,326.621,79.448z'/%3e%3cpath%20style='fill:%2382889D;'%20d='M167.724,203.034c-2.259,0-4.518-0.862-6.241-2.586L135,173.965c-3.448-3.448-3.448-9.035,0-12.483%20c3.448-3.448,9.035-3.448,12.483,0l20.241,20.242l37.897-37.897c3.448-3.448,9.035-3.448,12.483,0c3.448,3.448,3.448,9.035,0,12.483%20l-44.138,44.138C172.242,202.173,169.983,203.034,167.724,203.034z'/%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23AFB9D2;'%20d='M317.793,167.724h-70.621c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h70.621c4.875,0,8.828,3.953,8.828,8.828l0,0C326.621,163.772,322.668,167.724,317.793,167.724z'/%3e%3c/g%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23C7CFE2;'%20d='M361.931,203.034H247.172c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h114.759c4.875,0,8.828,3.953,8.828,8.828l0,0C370.759,199.082,366.806,203.034,361.931,203.034z'/%3e%3c/g%3e%3cpath%20style='fill:%2382889D;'%20d='M167.724,414.897c-2.259,0-4.518-0.862-6.241-2.586L135,385.827c-3.448-3.448-3.448-9.035,0-12.483%20c3.448-3.448,9.035-3.448,12.483,0l20.241,20.242l37.897-37.897c3.448-3.448,9.035-3.448,12.483,0c3.448,3.448,3.448,9.035,0,12.483%20l-44.138,44.138C172.242,414.035,169.983,414.897,167.724,414.897z'/%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23AFB9D2;'%20d='M317.793,379.586h-70.621c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h70.621c4.875,0,8.828,3.953,8.828,8.828l0,0C326.621,375.634,322.668,379.586,317.793,379.586z'/%3e%3c/g%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23C7CFE2;'%20d='M361.931,414.897H247.172c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h114.759c4.875,0,8.828,3.953,8.828,8.828l0,0C370.759,410.944,366.806,414.897,361.931,414.897z'/%3e%3c/g%3e%3cpath%20style='fill:%2382889D;'%20d='M167.724,308.966c-2.259,0-4.518-0.862-6.241-2.586L135,279.896c-3.448-3.448-3.448-9.035,0-12.483%20c3.448-3.448,9.035-3.448,12.483,0l20.241,20.242l37.897-37.897c3.448-3.448,9.035-3.448,12.483,0c3.448,3.448,3.448,9.035,0,12.483%20l-44.138,44.138C172.242,308.104,169.983,308.966,167.724,308.966z'/%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23AFB9D2;'%20d='M317.793,273.655h-70.621c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h70.621c4.875,0,8.828,3.953,8.828,8.828l0,0C326.621,269.703,322.668,273.655,317.793,273.655z'/%3e%3c/g%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23C7CFE2;'%20d='M361.931,308.966H247.172c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h114.759c4.875,0,8.828,3.953,8.828,8.828l0,0C370.759,305.013,366.806,308.966,361.931,308.966z'/%3e%3c/g%3e%3c/svg%3e";
|
|
28
|
+
const PluginIcon = () => /* @__PURE__ */ jsx("img", { src: checkmarkNotepad });
|
|
29
|
+
const prefixKey = (key) => `${PLUGIN_ID}.${key}`;
|
|
30
|
+
const index = {
|
|
31
|
+
register(app) {
|
|
32
|
+
app.customFields.register({
|
|
33
|
+
name: "multiselect-checkbox",
|
|
34
|
+
pluginId: `${PLUGIN_ID}`,
|
|
35
|
+
type: "json",
|
|
36
|
+
intlLabel: {
|
|
37
|
+
id: `${PLUGIN_ID}.label`,
|
|
38
|
+
defaultMessage: "Multiselect"
|
|
39
|
+
},
|
|
40
|
+
intlDescription: {
|
|
41
|
+
id: `${PLUGIN_ID}.description`,
|
|
42
|
+
defaultMessage: "A custom field for Strapi that allows users to select multiple options from a predefined list."
|
|
43
|
+
},
|
|
44
|
+
icon: PluginIcon,
|
|
45
|
+
components: {
|
|
46
|
+
Input: async () => import("./index-tpMZnVWK.mjs")
|
|
47
|
+
},
|
|
48
|
+
options: {
|
|
49
|
+
base: [
|
|
50
|
+
{
|
|
51
|
+
sectionTitle: null,
|
|
52
|
+
items: [
|
|
53
|
+
{
|
|
54
|
+
name: "options.availableOptions",
|
|
55
|
+
type: "textarea-enum",
|
|
56
|
+
intlLabel: {
|
|
57
|
+
id: prefixKey("options.available-options.label"),
|
|
58
|
+
defaultMessage: "Available Options"
|
|
59
|
+
},
|
|
60
|
+
description: {
|
|
61
|
+
id: prefixKey("options.available-options.description"),
|
|
62
|
+
defaultMessage: "One option per line."
|
|
63
|
+
},
|
|
64
|
+
placeholder: {
|
|
65
|
+
id: prefixKey("options.available-options.placeholder"),
|
|
66
|
+
defaultMessage: "Option 1\nOption 2\nOption 3"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
//
|
|
73
|
+
// Strapi default advanced options.
|
|
74
|
+
//
|
|
75
|
+
// - See also: https://github.com/strapi/strapi/blob/develop/packages/core/content-type-builder/admin/src/components/FormModal/attributes/attributeOptions.ts
|
|
76
|
+
//
|
|
77
|
+
advanced: [
|
|
78
|
+
{
|
|
79
|
+
sectionTitle: {
|
|
80
|
+
id: "global.settings",
|
|
81
|
+
defaultMessage: "Settings"
|
|
82
|
+
},
|
|
83
|
+
items: [
|
|
84
|
+
{
|
|
85
|
+
name: "required",
|
|
86
|
+
type: "checkbox",
|
|
87
|
+
intlLabel: {
|
|
88
|
+
id: "content-type-builder.form.attribute.item.requiredField",
|
|
89
|
+
defaultMessage: "Required field"
|
|
90
|
+
},
|
|
91
|
+
description: {
|
|
92
|
+
id: "content-type-builder.form.attribute.item.requiredField.description",
|
|
93
|
+
defaultMessage: "You won't be able to create an entry if this field is empty"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "private",
|
|
98
|
+
type: "checkbox",
|
|
99
|
+
intlLabel: {
|
|
100
|
+
id: "content-type-builder.form.attribute.item.privateField",
|
|
101
|
+
defaultMessage: "Private field"
|
|
102
|
+
},
|
|
103
|
+
description: {
|
|
104
|
+
id: "content-type-builder.form.attribute.item.privateField.description",
|
|
105
|
+
defaultMessage: "This field will not show up in the API response"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
app.registerPlugin({
|
|
114
|
+
id: PLUGIN_ID,
|
|
115
|
+
initializer: Initializer,
|
|
116
|
+
isReady: false,
|
|
117
|
+
name: PLUGIN_ID
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
async registerTrads({ locales }) {
|
|
121
|
+
return Promise.all(
|
|
122
|
+
locales.map(async (locale) => {
|
|
123
|
+
try {
|
|
124
|
+
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-tWwdhLO5.mjs") }), `./translations/${locale}.json`, 3);
|
|
125
|
+
return { data, locale };
|
|
126
|
+
} catch {
|
|
127
|
+
return { data: {}, locale };
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
export {
|
|
134
|
+
index as i,
|
|
135
|
+
prefixKey as p
|
|
136
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const react = require("react");
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
|
|
5
|
+
const v = glob[path];
|
|
6
|
+
if (v) {
|
|
7
|
+
return typeof v === "function" ? v() : Promise.resolve(v);
|
|
8
|
+
}
|
|
9
|
+
return new Promise((_, reject) => {
|
|
10
|
+
(typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
|
|
11
|
+
reject.bind(
|
|
12
|
+
null,
|
|
13
|
+
new Error(
|
|
14
|
+
"Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
|
|
15
|
+
)
|
|
16
|
+
)
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const PLUGIN_ID = "multiselect-checkbox";
|
|
21
|
+
const Initializer = ({ setPlugin }) => {
|
|
22
|
+
const ref = react.useRef(setPlugin);
|
|
23
|
+
react.useEffect(() => {
|
|
24
|
+
ref.current(PLUGIN_ID);
|
|
25
|
+
}, []);
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
const checkmarkNotepad = "data:image/svg+xml,%3c?xml%20version='1.0'%20encoding='iso-8859-1'?%3e%3c!--%20Uploaded%20to:%20SVG%20Repo,%20www.svgrepo.com,%20Generator:%20SVG%20Repo%20Mixer%20Tools%20--%3e%3csvg%20height='800px'%20width='800px'%20version='1.1'%20id='Layer_1'%20xmlns='http://www.w3.org/2000/svg'%20xmlns:xlink='http://www.w3.org/1999/xlink'%20viewBox='0%200%20512%20512'%20xml:space='preserve'%3e%3cpath%20style='fill:%23E1C3A0;'%20d='M406.069,512H105.931c-19.501,0-35.31-15.809-35.31-35.31V70.621c0-19.501,15.809-35.31,35.31-35.31%20h300.138c19.501,0,35.31,15.809,35.31,35.31V476.69C441.379,496.191,425.57,512,406.069,512z'/%3e%3cpath%20style='fill:%23EFF2FA;'%20d='M406.069,485.517H105.931c-4.875,0-8.828-3.953-8.828-8.828V70.621c0-4.875,3.953-8.828,8.828-8.828%20h300.138c4.875,0,8.828,3.953,8.828,8.828V476.69C414.897,481.565,410.944,485.517,406.069,485.517z'/%3e%3cpath%20style='fill:%23C7CFE2;'%20d='M308.966,35.31h-26.483v-8.828C282.483,11.857,270.626,0,256,0s-26.483,11.857-26.483,26.483v8.828%20h-26.483c-9.75,0-17.655,7.904-17.655,17.655v17.655h141.241V52.966C326.621,43.214,318.716,35.31,308.966,35.31z%20M256,35.31%20c-4.875,0-8.828-3.953-8.828-8.828s3.953-8.828,8.828-8.828s8.828,3.953,8.828,8.828S260.875,35.31,256,35.31z'/%3e%3cpath%20style='fill:%23AFB9D2;'%20d='M326.621,79.448H185.379c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h141.241c4.875,0,8.828,3.953,8.828,8.828l0,0C335.448,75.496,331.496,79.448,326.621,79.448z'/%3e%3cpath%20style='fill:%2382889D;'%20d='M167.724,203.034c-2.259,0-4.518-0.862-6.241-2.586L135,173.965c-3.448-3.448-3.448-9.035,0-12.483%20c3.448-3.448,9.035-3.448,12.483,0l20.241,20.242l37.897-37.897c3.448-3.448,9.035-3.448,12.483,0c3.448,3.448,3.448,9.035,0,12.483%20l-44.138,44.138C172.242,202.173,169.983,203.034,167.724,203.034z'/%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23AFB9D2;'%20d='M317.793,167.724h-70.621c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h70.621c4.875,0,8.828,3.953,8.828,8.828l0,0C326.621,163.772,322.668,167.724,317.793,167.724z'/%3e%3c/g%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23C7CFE2;'%20d='M361.931,203.034H247.172c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h114.759c4.875,0,8.828,3.953,8.828,8.828l0,0C370.759,199.082,366.806,203.034,361.931,203.034z'/%3e%3c/g%3e%3cpath%20style='fill:%2382889D;'%20d='M167.724,414.897c-2.259,0-4.518-0.862-6.241-2.586L135,385.827c-3.448-3.448-3.448-9.035,0-12.483%20c3.448-3.448,9.035-3.448,12.483,0l20.241,20.242l37.897-37.897c3.448-3.448,9.035-3.448,12.483,0c3.448,3.448,3.448,9.035,0,12.483%20l-44.138,44.138C172.242,414.035,169.983,414.897,167.724,414.897z'/%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23AFB9D2;'%20d='M317.793,379.586h-70.621c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h70.621c4.875,0,8.828,3.953,8.828,8.828l0,0C326.621,375.634,322.668,379.586,317.793,379.586z'/%3e%3c/g%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23C7CFE2;'%20d='M361.931,414.897H247.172c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h114.759c4.875,0,8.828,3.953,8.828,8.828l0,0C370.759,410.944,366.806,414.897,361.931,414.897z'/%3e%3c/g%3e%3cpath%20style='fill:%2382889D;'%20d='M167.724,308.966c-2.259,0-4.518-0.862-6.241-2.586L135,279.896c-3.448-3.448-3.448-9.035,0-12.483%20c3.448-3.448,9.035-3.448,12.483,0l20.241,20.242l37.897-37.897c3.448-3.448,9.035-3.448,12.483,0c3.448,3.448,3.448,9.035,0,12.483%20l-44.138,44.138C172.242,308.104,169.983,308.966,167.724,308.966z'/%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23AFB9D2;'%20d='M317.793,273.655h-70.621c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h70.621c4.875,0,8.828,3.953,8.828,8.828l0,0C326.621,269.703,322.668,273.655,317.793,273.655z'/%3e%3c/g%3e%3cg%20style='opacity:0.97;'%3e%3cpath%20style='fill:%23C7CFE2;'%20d='M361.931,308.966H247.172c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828%20h114.759c4.875,0,8.828,3.953,8.828,8.828l0,0C370.759,305.013,366.806,308.966,361.931,308.966z'/%3e%3c/g%3e%3c/svg%3e";
|
|
29
|
+
const PluginIcon = () => /* @__PURE__ */ jsxRuntime.jsx("img", { src: checkmarkNotepad });
|
|
30
|
+
const prefixKey = (key) => `${PLUGIN_ID}.${key}`;
|
|
31
|
+
const index = {
|
|
32
|
+
register(app) {
|
|
33
|
+
app.customFields.register({
|
|
34
|
+
name: "multiselect-checkbox",
|
|
35
|
+
pluginId: `${PLUGIN_ID}`,
|
|
36
|
+
type: "json",
|
|
37
|
+
intlLabel: {
|
|
38
|
+
id: `${PLUGIN_ID}.label`,
|
|
39
|
+
defaultMessage: "Multiselect"
|
|
40
|
+
},
|
|
41
|
+
intlDescription: {
|
|
42
|
+
id: `${PLUGIN_ID}.description`,
|
|
43
|
+
defaultMessage: "A custom field for Strapi that allows users to select multiple options from a predefined list."
|
|
44
|
+
},
|
|
45
|
+
icon: PluginIcon,
|
|
46
|
+
components: {
|
|
47
|
+
Input: async () => Promise.resolve().then(() => require("./index-8Txh7SeC.js"))
|
|
48
|
+
},
|
|
49
|
+
options: {
|
|
50
|
+
base: [
|
|
51
|
+
{
|
|
52
|
+
sectionTitle: null,
|
|
53
|
+
items: [
|
|
54
|
+
{
|
|
55
|
+
name: "options.availableOptions",
|
|
56
|
+
type: "textarea-enum",
|
|
57
|
+
intlLabel: {
|
|
58
|
+
id: prefixKey("options.available-options.label"),
|
|
59
|
+
defaultMessage: "Available Options"
|
|
60
|
+
},
|
|
61
|
+
description: {
|
|
62
|
+
id: prefixKey("options.available-options.description"),
|
|
63
|
+
defaultMessage: "One option per line."
|
|
64
|
+
},
|
|
65
|
+
placeholder: {
|
|
66
|
+
id: prefixKey("options.available-options.placeholder"),
|
|
67
|
+
defaultMessage: "Option 1\nOption 2\nOption 3"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
//
|
|
74
|
+
// Strapi default advanced options.
|
|
75
|
+
//
|
|
76
|
+
// - See also: https://github.com/strapi/strapi/blob/develop/packages/core/content-type-builder/admin/src/components/FormModal/attributes/attributeOptions.ts
|
|
77
|
+
//
|
|
78
|
+
advanced: [
|
|
79
|
+
{
|
|
80
|
+
sectionTitle: {
|
|
81
|
+
id: "global.settings",
|
|
82
|
+
defaultMessage: "Settings"
|
|
83
|
+
},
|
|
84
|
+
items: [
|
|
85
|
+
{
|
|
86
|
+
name: "required",
|
|
87
|
+
type: "checkbox",
|
|
88
|
+
intlLabel: {
|
|
89
|
+
id: "content-type-builder.form.attribute.item.requiredField",
|
|
90
|
+
defaultMessage: "Required field"
|
|
91
|
+
},
|
|
92
|
+
description: {
|
|
93
|
+
id: "content-type-builder.form.attribute.item.requiredField.description",
|
|
94
|
+
defaultMessage: "You won't be able to create an entry if this field is empty"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "private",
|
|
99
|
+
type: "checkbox",
|
|
100
|
+
intlLabel: {
|
|
101
|
+
id: "content-type-builder.form.attribute.item.privateField",
|
|
102
|
+
defaultMessage: "Private field"
|
|
103
|
+
},
|
|
104
|
+
description: {
|
|
105
|
+
id: "content-type-builder.form.attribute.item.privateField.description",
|
|
106
|
+
defaultMessage: "This field will not show up in the API response"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
app.registerPlugin({
|
|
115
|
+
id: PLUGIN_ID,
|
|
116
|
+
initializer: Initializer,
|
|
117
|
+
isReady: false,
|
|
118
|
+
name: PLUGIN_ID
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
async registerTrads({ locales }) {
|
|
122
|
+
return Promise.all(
|
|
123
|
+
locales.map(async (locale) => {
|
|
124
|
+
try {
|
|
125
|
+
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-pkXKBXLR.js")) }), `./translations/${locale}.json`, 3);
|
|
126
|
+
return { data, locale };
|
|
127
|
+
} catch {
|
|
128
|
+
return { data: {}, locale };
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
exports.index = index;
|
|
135
|
+
exports.prefixKey = prefixKey;
|
|
@@ -0,0 +1,52 @@
|
|
|
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-CTLgAy6y.mjs";
|
|
6
|
+
const config = {
|
|
7
|
+
/**
|
|
8
|
+
* The default options used as fallbacks in case the user-defined values are missing.
|
|
9
|
+
*/
|
|
10
|
+
defaultOptions: {
|
|
11
|
+
availableOptions: []
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const CapitalizedText = styled.p`
|
|
15
|
+
&::first-letter {
|
|
16
|
+
text-transform: uppercase;
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
const EmptyState = () => {
|
|
20
|
+
return /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral400", children: /* @__PURE__ */ jsx(FormattedMessage, { id: prefixKey("empty-state.text") }) });
|
|
21
|
+
};
|
|
22
|
+
const Multiselect = (props) => {
|
|
23
|
+
const { attribute, disabled, hint, label, name, onChange, required, type, value } = props;
|
|
24
|
+
const { availableOptions = config.defaultOptions.availableOptions } = attribute.options;
|
|
25
|
+
const selectedOptions = Array.isArray(value) ? value : [];
|
|
26
|
+
const updateValue = (value2) => onChange({ target: { name, value: value2, type } });
|
|
27
|
+
const updateSelectedOptions = (option, isSelected) => {
|
|
28
|
+
const nextSelectedOptions = isSelected ? selectedOptions.concat(option) : selectedOptions.filter((selectedOption) => selectedOption !== option);
|
|
29
|
+
const sortedNextSelectedOptions = nextSelectedOptions.sort(
|
|
30
|
+
(lhs, rhs) => availableOptions.indexOf(lhs) - availableOptions.indexOf(rhs)
|
|
31
|
+
);
|
|
32
|
+
updateValue(sortedNextSelectedOptions);
|
|
33
|
+
};
|
|
34
|
+
return /* @__PURE__ */ jsxs(Field.Root, { hint, name, required, children: [
|
|
35
|
+
/* @__PURE__ */ jsx(Field.Label, { children: label }),
|
|
36
|
+
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(
|
|
37
|
+
Checkbox,
|
|
38
|
+
{
|
|
39
|
+
option,
|
|
40
|
+
checked: selectedOptions.includes(option),
|
|
41
|
+
disabled: disabled ?? false,
|
|
42
|
+
onCheckedChange: (isSelected) => updateSelectedOptions(option, isSelected),
|
|
43
|
+
children: /* @__PURE__ */ jsx(CapitalizedText, { children: option })
|
|
44
|
+
},
|
|
45
|
+
option
|
|
46
|
+
)) }) }),
|
|
47
|
+
/* @__PURE__ */ jsx(Field.Hint, {})
|
|
48
|
+
] });
|
|
49
|
+
};
|
|
50
|
+
export {
|
|
51
|
+
Multiselect as default
|
|
52
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
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: {
|
|
6
|
+
availableOptions: string[] | undefined;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* `Multiselect` is a custom form component that allows users to select multiple options
|
|
12
|
+
* via checkboxes. The selected values are stored as an array of strings.
|
|
13
|
+
*
|
|
14
|
+
* ### Props:
|
|
15
|
+
* - `attribute`: An object containing the list of selectable `options: string[]`.
|
|
16
|
+
* - `disabled`: (Optional) Disables all checkboxes when `true`.
|
|
17
|
+
* - `hint`: (Optional) A string providing contextual help, shown below the field.
|
|
18
|
+
* - `name`: The name of the form field (used in the synthetic `onChange` event).
|
|
19
|
+
* - `label`: A label for the field, displayed above the checkboxes.
|
|
20
|
+
* - `onChange`: A handler that receives the updated selection as an array of strings.
|
|
21
|
+
* - `required`: (Optional) Indicates if the field is required.
|
|
22
|
+
* - `value`: The current value as an array of selected options.
|
|
23
|
+
*/
|
|
24
|
+
declare const Multiselect: (props: Props) => import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export default Multiselect;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PLUGIN_ID = "multiselect-checkbox";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const bootstrap = ({ strapi }) => {
|
|
3
|
+
};
|
|
4
|
+
const config = {
|
|
5
|
+
default: {},
|
|
6
|
+
validator() {
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
const contentTypes = {};
|
|
10
|
+
const controller = ({ strapi }) => ({});
|
|
11
|
+
const controllers = {
|
|
12
|
+
controller
|
|
13
|
+
};
|
|
14
|
+
const destroy = ({ strapi }) => {
|
|
15
|
+
};
|
|
16
|
+
const middlewares = {};
|
|
17
|
+
const policies = {};
|
|
18
|
+
const register = ({ strapi }) => {
|
|
19
|
+
strapi.customFields.register({
|
|
20
|
+
name: "multiselect-checkbox",
|
|
21
|
+
plugin: "multiselect-checkbox",
|
|
22
|
+
// The data type stored in the database - using json to store array of strings.
|
|
23
|
+
type: "json"
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
const contentAPIRoutes = [];
|
|
27
|
+
const routes = {
|
|
28
|
+
"content-api": {
|
|
29
|
+
type: "content-api",
|
|
30
|
+
routes: contentAPIRoutes
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const service = ({ strapi }) => ({});
|
|
34
|
+
const services = {
|
|
35
|
+
service
|
|
36
|
+
};
|
|
37
|
+
const index = {
|
|
38
|
+
register,
|
|
39
|
+
bootstrap,
|
|
40
|
+
destroy,
|
|
41
|
+
config,
|
|
42
|
+
controllers,
|
|
43
|
+
routes,
|
|
44
|
+
services,
|
|
45
|
+
contentTypes,
|
|
46
|
+
policies,
|
|
47
|
+
middlewares
|
|
48
|
+
};
|
|
49
|
+
module.exports = index;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const bootstrap = ({ strapi }) => {
|
|
2
|
+
};
|
|
3
|
+
const config = {
|
|
4
|
+
default: {},
|
|
5
|
+
validator() {
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
const contentTypes = {};
|
|
9
|
+
const controller = ({ strapi }) => ({});
|
|
10
|
+
const controllers = {
|
|
11
|
+
controller
|
|
12
|
+
};
|
|
13
|
+
const destroy = ({ strapi }) => {
|
|
14
|
+
};
|
|
15
|
+
const middlewares = {};
|
|
16
|
+
const policies = {};
|
|
17
|
+
const register = ({ strapi }) => {
|
|
18
|
+
strapi.customFields.register({
|
|
19
|
+
name: "multiselect-checkbox",
|
|
20
|
+
plugin: "multiselect-checkbox",
|
|
21
|
+
// The data type stored in the database - using json to store array of strings.
|
|
22
|
+
type: "json"
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const contentAPIRoutes = [];
|
|
26
|
+
const routes = {
|
|
27
|
+
"content-api": {
|
|
28
|
+
type: "content-api",
|
|
29
|
+
routes: contentAPIRoutes
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const service = ({ strapi }) => ({});
|
|
33
|
+
const services = {
|
|
34
|
+
service
|
|
35
|
+
};
|
|
36
|
+
const index = {
|
|
37
|
+
register,
|
|
38
|
+
bootstrap,
|
|
39
|
+
destroy,
|
|
40
|
+
config,
|
|
41
|
+
controllers,
|
|
42
|
+
routes,
|
|
43
|
+
services,
|
|
44
|
+
contentTypes,
|
|
45
|
+
policies,
|
|
46
|
+
middlewares
|
|
47
|
+
};
|
|
48
|
+
export {
|
|
49
|
+
index as default
|
|
50
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
register: ({ strapi }: {
|
|
3
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
4
|
+
}) => void;
|
|
5
|
+
bootstrap: ({ strapi }: {
|
|
6
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
7
|
+
}) => void;
|
|
8
|
+
destroy: ({ strapi }: {
|
|
9
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
10
|
+
}) => void;
|
|
11
|
+
config: {
|
|
12
|
+
default: {};
|
|
13
|
+
validator(): void;
|
|
14
|
+
};
|
|
15
|
+
controllers: {
|
|
16
|
+
controller: ({ strapi }: {
|
|
17
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
18
|
+
}) => {};
|
|
19
|
+
};
|
|
20
|
+
routes: {
|
|
21
|
+
'content-api': {
|
|
22
|
+
type: string;
|
|
23
|
+
routes: any[];
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
services: {
|
|
27
|
+
service: ({ strapi }: {
|
|
28
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
29
|
+
}) => {};
|
|
30
|
+
};
|
|
31
|
+
contentTypes: {};
|
|
32
|
+
policies: {};
|
|
33
|
+
middlewares: {};
|
|
34
|
+
};
|
|
35
|
+
export default _default;
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tunghtml/strapi-plugin-multiselect-checkbox",
|
|
3
|
+
"version": "1.0.0",
|
|
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
|
+
"keywords": [
|
|
6
|
+
"strapi",
|
|
7
|
+
"strapi-plugin",
|
|
8
|
+
"plugin",
|
|
9
|
+
"multiselect",
|
|
10
|
+
"checkbox",
|
|
11
|
+
"custom-field",
|
|
12
|
+
"array",
|
|
13
|
+
"json"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Tung Le",
|
|
18
|
+
"email": "tunghtml@gmail.com",
|
|
19
|
+
"url": "https://github.com/finnwasabi"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/finnwasabi/strapi-plugin-multiselect-checkbox.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/finnwasabi/strapi-plugin-multiselect-checkbox/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/finnwasabi/strapi-plugin-multiselect-checkbox#readme",
|
|
29
|
+
"type": "commonjs",
|
|
30
|
+
"exports": {
|
|
31
|
+
"./package.json": "./package.json",
|
|
32
|
+
"./strapi-admin": {
|
|
33
|
+
"types": "./dist/admin/src/index.d.ts",
|
|
34
|
+
"source": "./admin/src/index.ts",
|
|
35
|
+
"import": "./dist/admin/index.mjs",
|
|
36
|
+
"require": "./dist/admin/index.js",
|
|
37
|
+
"default": "./dist/admin/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./strapi-server": {
|
|
40
|
+
"types": "./dist/server/src/index.d.ts",
|
|
41
|
+
"source": "./server/src/index.ts",
|
|
42
|
+
"import": "./dist/server/index.mjs",
|
|
43
|
+
"require": "./dist/server/index.js",
|
|
44
|
+
"default": "./dist/server/index.js"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "strapi-plugin build",
|
|
52
|
+
"test:ts:back": "run -T tsc -p server/tsconfig.json",
|
|
53
|
+
"test:ts:front": "run -T tsc -p admin/tsconfig.json",
|
|
54
|
+
"verify": "strapi-plugin verify",
|
|
55
|
+
"watch": "strapi-plugin watch",
|
|
56
|
+
"watch:link": "strapi-plugin watch:link"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@strapi/design-system": "^2.0.0-rc.29",
|
|
60
|
+
"@strapi/icons": "^2.0.0-rc.29",
|
|
61
|
+
"react-intl": "^7.1.11"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@strapi/sdk-plugin": "^5.3.2",
|
|
65
|
+
"@strapi/strapi": "^5.19.0",
|
|
66
|
+
"@strapi/typescript-utils": "^5.18.0",
|
|
67
|
+
"@types/react": "^19.1.8",
|
|
68
|
+
"@types/react-dom": "^19.1.6",
|
|
69
|
+
"prettier": "^3.6.2",
|
|
70
|
+
"react": "^18.3.1",
|
|
71
|
+
"react-dom": "^18.3.1",
|
|
72
|
+
"react-router-dom": "^6.30.1",
|
|
73
|
+
"styled-components": "^6.1.19",
|
|
74
|
+
"typescript": "^5.8.3"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"@strapi/sdk-plugin": "^5.3.2",
|
|
78
|
+
"@strapi/strapi": "^5.18.0",
|
|
79
|
+
"react": "^18.3.1",
|
|
80
|
+
"react-dom": "^18.3.1",
|
|
81
|
+
"react-router-dom": "^6.30.1",
|
|
82
|
+
"styled-components": "^6.1.19"
|
|
83
|
+
},
|
|
84
|
+
"strapi": {
|
|
85
|
+
"kind": "plugin",
|
|
86
|
+
"name": "multiselect-checkbox",
|
|
87
|
+
"displayName": "Multiselect Checkbox",
|
|
88
|
+
"description": "A custom field for Strapi that allows users to select multiple options from a predefined list and stores as array."
|
|
89
|
+
}
|
|
90
|
+
}
|