├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── admin ├── custom.d.ts ├── src │ ├── components │ │ ├── MultiSelect │ │ │ ├── MultiValueContainer.tsx │ │ │ ├── ReactSelect.tsx │ │ │ └── index.tsx │ │ └── MultiSelectIcon │ │ │ └── index.tsx │ ├── index.ts │ ├── pluginId.ts │ ├── translations │ │ └── en.json │ └── utils │ │ ├── getTrad.ts │ │ └── prefixPluginTranslations.ts ├── tsconfig.build.json └── tsconfig.json ├── package-lock.json ├── package.json ├── screenshots ├── multi-select-default-value.png ├── multi-select-options.png ├── multi-select-plugin.png └── multi-select.png ├── server ├── src │ ├── index.ts │ └── register.ts ├── tsconfig.build.json └── tsconfig.json └── tsconfig ├── base.json └── client.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | ############################ 4 | # OS X 5 | ############################ 6 | 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | Icon 11 | .Spotlight-V100 12 | .Trashes 13 | ._* 14 | 15 | 16 | ############################ 17 | # Linux 18 | ############################ 19 | 20 | *~ 21 | 22 | 23 | ############################ 24 | # Windows 25 | ############################ 26 | 27 | Thumbs.db 28 | ehthumbs.db 29 | Desktop.ini 30 | $RECYCLE.BIN/ 31 | *.cab 32 | *.msi 33 | *.msm 34 | *.msp 35 | 36 | 37 | ############################ 38 | # Packages 39 | ############################ 40 | 41 | *.7z 42 | *.csv 43 | *.dat 44 | *.dmg 45 | *.gz 46 | *.iso 47 | *.jar 48 | *.rar 49 | *.tar 50 | *.zip 51 | *.com 52 | *.class 53 | *.dll 54 | *.exe 55 | *.o 56 | *.seed 57 | *.so 58 | *.swo 59 | *.swp 60 | *.swn 61 | *.swm 62 | *.out 63 | *.pid 64 | 65 | 66 | ############################ 67 | # Logs and databases 68 | ############################ 69 | 70 | .tmp 71 | *.log 72 | *.sql 73 | *.sqlite 74 | *.sqlite3 75 | 76 | 77 | ############################ 78 | # Misc. 79 | ############################ 80 | 81 | *# 82 | ssl 83 | .idea 84 | nbproject 85 | .tsbuildinfo 86 | .eslintcache 87 | .env 88 | 89 | 90 | ############################ 91 | # Strapi 92 | ############################ 93 | 94 | public/uploads/* 95 | !public/uploads/.gitkeep 96 | 97 | 98 | ############################ 99 | # Build 100 | ############################ 101 | 102 | dist 103 | build 104 | 105 | 106 | ############################ 107 | # Node.js 108 | ############################ 109 | 110 | lib-cov 111 | lcov.info 112 | pids 113 | logs 114 | results 115 | node_modules 116 | .node_history 117 | 118 | 119 | ############################ 120 | # Package managers 121 | ############################ 122 | 123 | .yarn/* 124 | !.yarn/cache 125 | !.yarn/unplugged 126 | !.yarn/patches 127 | !.yarn/releases 128 | !.yarn/sdks 129 | !.yarn/versions 130 | .pnp.* 131 | yarn-error.log 132 | 133 | 134 | ############################ 135 | # Tests 136 | ############################ 137 | 138 | coverage 139 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "tabWidth": 2, 4 | "printWidth": 100, 5 | "singleQuote": true, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Zaydme 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Strapi plugin multi-select 2 | 3 | A strapi custom field for selecting multiple options from a provided list of items. 4 | 5 | ### CHANGELOG 6 | **2.1.0** 7 | - Add default value support [#16](https://github.com/Zaydme/strapi-plugin-multi-select/issues/16) 8 | - Add Min and Max selection limits 9 | 10 | ![multi-select screenshot default value JSON](./screenshots/multi-select-default-value.png) 11 | 12 | **2.0.1** 13 | - Preserve selection order when displaying previously selected options [#21](https://github.com/Zaydme/strapi-plugin-multi-select/issues/21) 14 | 15 | **2.0.0** 16 | - Migrated to Strapi v5, and the new plugin structure 17 | - Now using @strapi/sdk-plugin 18 | - Migrated to TypeScript 19 | 20 | **1.2.2** Support colons in option values, only first colon is used as separator 21 | 22 | > example usage: `this_is_label:all:of:this:is:value` 23 | 24 | **1.2.1** Localize option label 25 | 26 | > example usage: `my.custom.translations.key:value` 27 | 28 | 29 | **1.2.0** Replace strapi select with react-select 30 | 31 | 32 | ## Installation 33 | 34 | To install this plugin, you need to add an NPM dependency to your Strapi application: 35 | 36 | ``` 37 | # Using Yarn 38 | yarn add strapi-plugin-multi-select 39 | 40 | # Or using NPM 41 | npm install strapi-plugin-multi-select 42 | ``` 43 | 44 | Then, you'll need to build your admin panel: 45 | 46 | ``` 47 | # Using Yarn 48 | yarn build 49 | 50 | # Or using NPM 51 | npm run build 52 | ``` 53 | 54 | ## Usage 55 | 56 | After installation you will find the multi-select at the custom fields section of the content-type builder. 57 | 58 | ![multi-select screenshot](./screenshots/multi-select-plugin.png) 59 | 60 | You add options to the multi-select by adding a line separated list of options to the options field. 61 | 62 | You can also add a value and a label separated by a colon (e.g. `label:value`). If no value is provided, the label will be used as the value. 63 | 64 | ![multi-select screenshot options](./screenshots/multi-select-options.png) 65 | 66 | then you can select one or more options from the list. 67 | 68 | ![multi-select screenshot](./screenshots/multi-select.png) 69 | 70 | in this case the API will return 71 | 72 | ```json 73 | { 74 | "data": { 75 | "id": 1, 76 | "attributes": { 77 | "stuff": ["Banana", "citron"] 78 | } 79 | } 80 | } 81 | ``` 82 | 83 | --- 84 | 85 | You can also check the [multi-country-select](https://github.com/zaydme/strapi-plugin-multi-country-select) plugin 86 | -------------------------------------------------------------------------------- /admin/custom.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@strapi/design-system/*'; 2 | declare module '@strapi/design-system'; 3 | -------------------------------------------------------------------------------- /admin/src/components/MultiSelect/MultiValueContainer.tsx: -------------------------------------------------------------------------------- 1 | import { Tag } from '@strapi/design-system'; 2 | import { Cross } from '@strapi/icons'; 3 | 4 | export default ({ 5 | selectProps, 6 | data, 7 | }: { 8 | selectProps: any; 9 | data: { 10 | value: string; 11 | label: string; 12 | }; 13 | }) => { 14 | const handleTagClick = (data: { value: string; label: string }) => (e: React.UIEvent) => { 15 | e.preventDefault(); 16 | selectProps.onChange(selectProps.value.filter((v: any) => v !== data)); 17 | }; 18 | return ( 19 | } onClick={handleTagClick(data)}> 20 | {data.label} 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /admin/src/components/MultiSelect/ReactSelect.tsx: -------------------------------------------------------------------------------- 1 | import Select from 'react-select'; 2 | import styled from 'styled-components'; 3 | 4 | const ReactSelect = ({ components, styles, error, ariaErrorMessage, ...props }: any) => { 5 | return ( 6 |