Skip to content

Commit 72baa5b

Browse files
committed
basic feature added
1 parent b36e492 commit 72baa5b

File tree

4 files changed

+138
-42
lines changed

4 files changed

+138
-42
lines changed

public/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
manifest.json provides metadata used when your web app is installed on a
1515
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
1616
-->
17+
<link rel="stylesheet" crossorigin="anonymous" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;300;400;500;600;700;800&amp;display=swap">
1718
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
1819
<!--
1920
Notice the use of %PUBLIC_URL% in the tags above.

src/App.css

+54-15
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,62 @@ html,
22
body {
33
padding: 100px;
44
font-size: 10px;
5-
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
5+
font-family: "Montserrat", sans-serif;
66
}
77
.flx {
88
display: inline-flex;
99
align-items: center;
1010
}
11-
12-
.msl-wrp {
13-
position: relative;
14-
margin: 20px;
11+
.msl-vars {
12+
--menu-max-height: 400px;
1513
--font-size: 16px;
16-
--bg: white;
1714
--border-radius: 8px;
1815
--transition: 200ms;
19-
--option-bg-shadow: 1px 1px 5px 0px gray;
16+
--menu-shadow: 1px 1px 5px 0px gray;
2017
--option-bg-hover: rgb(233, 233, 233);
2118
}
19+
20+
.msl-wrp {
21+
position: relative;
22+
margin: 20px;
23+
}
2224
.msl {
2325
overflow: hidden;
2426
border: 1px solid;
25-
border-color: black;
27+
border-color: rgb(117, 117, 117);
2628
border-radius: var(--border-radius);
2729
outline: none;
2830
cursor: text;
2931
}
3032
.msl-active {
31-
box-shadow: var(--option-bg-shadow);
33+
box-shadow: var(--menu-shadow);
3234
border-color: transparent;
3335
border-radius: var(--border-radius) var(--border-radius) 0 0;
3436
}
3537
.msl-active ~ .msl-options {
36-
box-shadow: var(--option-bg-shadow);
38+
box-shadow: var(--menu-shadow);
3739
clip-path: inset(0px -10px -10px -10px);
38-
max-height: 200px;
40+
max-height: var(--menu-max-height);
3941
}
4042
.msl-input-wrp {
4143
display: inline-block;
42-
align-items: center;
44+
vertical-align: top;
4345
}
4446
.msl-input {
4547
display: inline-block;
48+
vertical-align: top;
4649
outline: none;
4750
margin: 5px;
51+
padding: 3px;
4852
font-size: var(--font-size);
4953
}
5054

55+
.msl-input::before {
56+
color: rgb(146, 146, 146);
57+
}
58+
.msl-input[data-placeholder]:not([data-placeholder=""]):empty::before {
59+
content: attr(data-placeholder);
60+
}
5161
.msl-chip {
5262
background: rgb(238, 238, 238);
5363
border-radius: calc(var(--border-radius) - 3px);
@@ -60,26 +70,52 @@ body {
6070
line-break: anywhere;
6171
white-space: break-spaces;
6272
}
63-
73+
.msl-single-value {
74+
display: inline-block;
75+
margin: 5px;
76+
padding: 3px;
77+
vertical-align: top;
78+
overflow: hidden;
79+
text-overflow: ellipsis;
80+
font-size: var(--font-size);
81+
line-break: strict;
82+
white-space: nowrap;
83+
}
6484
.msl-options {
6585
position: absolute;
6686
width: 100%;
6787
border-radius: 0 0 var(--border-radius) var(--border-radius);
68-
background: var(--bg);
88+
background: white;
6989
max-height: 0;
7090
overflow: auto;
7191
transition: max-height 100ms ease-in;
7292
}
93+
.msl-options::-webkit-scrollbar {
94+
width: 8px;
95+
}
96+
.msl-options::-webkit-scrollbar-track {
97+
background: transparent;
98+
}
99+
100+
.msl-options::-webkit-scrollbar-thumb {
101+
background: rgb(0, 0, 0, 0.1);
102+
border-radius: 20px;
103+
}
104+
105+
.msl-options::-webkit-scrollbar-thumb:hover {
106+
background: rgb(0, 0, 0, 0.2);
107+
}
73108
.msl-options > .msl-option {
74109
font-size: var(--font-size);
75110
border-radius: calc(var(--border-radius) - 3px);
76111
padding: 5px;
77112
cursor: pointer;
78-
margin: 3px 5px 4px 5px;
113+
margin: 4px 6px 6px 6px;
79114
line-height: 1;
80115
line-break: anywhere;
81116
line-height: 1.2;
82117
white-space: break-spaces;
118+
transition: background 200ms;
83119
}
84120
.msl-options > .msl-option:hover {
85121
background: var(--option-bg-hover);
@@ -88,6 +124,9 @@ body {
88124
background: #e6f4fa;
89125
color: #0351ff;
90126
}
127+
.msl-option-disable {
128+
color: hsla(0, 0%, 0%, 0.3);
129+
}
91130

92131
.msl-btn {
93132
border-radius: 50%;

src/App.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ function App() {
1010
// select limit
1111
const [val, setval] = useState([])
1212
const options = [
13-
{ label: "option 1", value: "opt1", icon: "red" },
13+
{ label: "option 1", value: "opt 1", icon: "red" },
1414
{ label: "Option 2", value: "opt2", icon: "red" },
15-
{ label: "Option 3", value: "opt3", icon: "red" },
15+
{ label: "Option 3", value: "opt3", icon: "red", disabled: true },
1616
{ label: "Option asdf asdf asd fad fdsdfasf assdfdasf adf 3", value: "opt4", icon: "red" },
1717
{ label: "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", value: "opt5", icon: "red" },
1818
];
@@ -25,8 +25,19 @@ function App() {
2525
return (
2626
<div>
2727
{/* <Test /> */}
28-
<h3>{val.map((itm, i) => <span key={`i-${i + 9}`}>{itm.label}</span>)}</h3>
29-
<MultiSelectDropdown width={300} options={options} onChange={handleChange} />
28+
<h3>{typeof val === 'object' ? val.map((itm, i) => <span key={`i-${i + 9}`}>{itm.label}</span>) : 'strng ' + val}</h3>
29+
<MultiSelectDropdown
30+
// clearable={false}
31+
// downArrow={false}
32+
// jsonValue
33+
// defaultValue={"opt2,opt 1,''"}
34+
//defaultValue={[{ label: "option 1", value: "opt 1", icon: "red", style: { background: 'red' } },
35+
//{ label: "Option 2", value: "opt2", icon: "red" },]}
36+
singleSelect={false}
37+
disableChip={true}
38+
options={options}
39+
onChange={handleChange}
40+
/>
3041
</div>
3142
);
3243
}

src/MultiSelectDropdown.jsx

+68-23
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,59 @@
1-
import React, { useState, useRef, memo } from 'react'
1+
import React, { useState, memo } from 'react'
22
import CloseIcon from './CloseIcon';
33
import DownIcon from './DownIcon';
44

55
MultiSelectDropdown.defaultProps = {
66
clearable: true,
7-
downArrow: true
7+
downArrow: true,
8+
width: 300,
9+
singleSelect: false,
10+
jsonValue: false,
11+
defaultValue: '',
12+
disableChip: false,
13+
placeholder: 'Select...',
14+
onChange: () => { },
15+
options: [{ label: 'Empty', value: '', disabled: true, style: { textAlign: 'center' } }],
816
}
917

10-
function MultiSelectDropdown(props) {
11-
const { options, width, downArrowIcon, clearable, downArrow, onChange } = props;
12-
const mslInputRef = useRef(null);
18+
function MultiSelectDropdown({ options, width, downArrowIcon, clearable, downArrow, onChange, singleSelect, jsonValue, defaultValue, className, placeholder, disableChip }) {
19+
1320
const [menuOpen, setMenuOpen] = useState(false);
14-
const [value, setValue] = useState([]);
21+
22+
let preDefinedValue = []
23+
if (defaultValue !== '' || defaultValue.length > 0) {
24+
if (typeof defaultValue === 'string') {
25+
const valueArr = defaultValue.split(",")
26+
preDefinedValue = options.filter(itm => -1 !== valueArr.indexOf(itm.value))
27+
if (singleSelect && preDefinedValue.length > 1) {
28+
preDefinedValue = [preDefinedValue[0]]
29+
}
30+
} else if (Array.isArray(preDefinedValue)) {
31+
preDefinedValue = options.filter(opt => defaultValue.some(pval => opt.value === pval.value));
32+
if (singleSelect && preDefinedValue.length > 1) {
33+
preDefinedValue = [preDefinedValue[0]]
34+
}
35+
}
36+
}
37+
const [value, setValue] = useState(preDefinedValue);
1538
let stopPropagation = true
1639

1740
const setNewValue = val => {
1841
setValue(val)
19-
onChange(val)
42+
if (jsonValue) {
43+
onChange(val)
44+
} else {
45+
let stringvalue = ''
46+
stringvalue += val.map(itm => itm.value)
47+
onChange(stringvalue)
48+
}
2049
}
2150

2251
const inputRefFocus = (e, focus) => {
2352
let parentNode = null
2453
let inputNode = null
25-
if(e.target.hasAttribute('data-msl')){
54+
if (e.target.hasAttribute('data-msl')) {
2655
parentNode = e.target
27-
}else if (e.target.parentNode.hasAttribute('data-msl')) {
56+
} else if (e.target.parentNode.hasAttribute('data-msl')) {
2857
parentNode = e.target.parentNode
2958
} else if (e.target.parentNode.parentNode.hasAttribute('data-msl')) {
3059
parentNode = e.target.parentNode.parentNode
@@ -38,7 +67,6 @@ function MultiSelectDropdown(props) {
3867
}
3968

4069
if (inputNode !== null) {
41-
console.log('----------',inputNode)
4270
focus ? inputNode.focus() : inputNode.blur()
4371
}
4472
}
@@ -98,13 +126,17 @@ function MultiSelectDropdown(props) {
98126
}
99127

100128
const addValue = (i) => {
101-
const tmp = [...value]
102-
if (!checkValueExist(options[i], value)) {
103-
tmp.push(options[i])
104-
setNewValue(tmp)
129+
let tmp = [...value]
130+
if (singleSelect) {
131+
tmp = [options[i]]
105132
} else {
106-
deleteValue(i)
133+
if (!checkValueExist(options[i], value)) {
134+
tmp.push(options[i])
135+
} else {
136+
tmp = tmp.filter(itm => itm.value !== options[i].value)
137+
}
107138
}
139+
setNewValue(tmp)
108140
}
109141

110142
const deleteValue = i => {
@@ -116,18 +148,28 @@ function MultiSelectDropdown(props) {
116148
const clearValue = () => {
117149
setNewValue([])
118150
}
151+
const showSearchOption = () => {
152+
if (!singleSelect && !disableChip ) {
153+
return true
154+
} else if (singleSelect && !value.length) {
155+
return true
156+
}else if(!singleSelect && disableChip && !value.length){
157+
return true
158+
}
159+
return false
160+
}
119161

120162
return (
121163
<>
122164
{console.log('----------mounted')}
123-
<div onClick={handleClickInput} style={{ width }} className="msl-wrp">
165+
<div onClick={handleClickInput} style={{ width }} className={`msl-wrp msl-vars ${className}`}>
124166
<div
125167
data-msl
126-
className={`msl ${menuOpen ? "msl-active" : ""}`}
168+
className={`msl ${menuOpen ? "msl-active" : ""} `}
127169
>
128170
<div data-msl className="msl-input-wrp"
129-
style={{ marginRight: clearable && downArrow ? 60 : downArrow || clearable ? 40 : 0 }}>
130-
{value.map((val, i) => (
171+
style={{ marginRight: clearable && downArrow ? 60 : downArrow || clearable ? 40 : 5 }}>
172+
{(!singleSelect && !disableChip) && value.map((val, i) => (
131173
<div key={`chip-${i + 11}`} className="msl-chip">
132174
{val.label}
133175
<button onClick={() => deleteValue(i)} className="msl-btn msl-chip-delete flx">
@@ -136,7 +178,9 @@ function MultiSelectDropdown(props) {
136178
<span />
137179
</div>
138180
))}
139-
<div data-msl /* onBlur={onInputBlur} */ ref={mslInputRef} className="msl-input" contentEditable />
181+
{!singleSelect && disableChip && value.length === 1 ? <span className="msl-single-value" data-msl style={{ width: width - (clearable && downArrow ? 60 : downArrow || clearable ? 40 : 5) }}>{value[0].label}d</span> : disableChip && value.length > 1 && <span className="msl-single-value" data-msl style={{ width: width - (clearable && downArrow ? 60 : downArrow || clearable ? 40 : 5) }}>{value.length} Selected</span>}
182+
{singleSelect && value.length === 1 && <span className="msl-single-value" data-msl style={{ width: width - (clearable && downArrow ? 60 : downArrow || clearable ? 40 : 5) }}>{value[0].label}</span>}
183+
{showSearchOption() && <div data-msl data-placeholder={placeholder} className="msl-input" contentEditable />}
140184
</div>
141185
{(clearable || downArrow) && (
142186
<div className="msl-actions flx">
@@ -160,11 +204,12 @@ function MultiSelectDropdown(props) {
160204
<div className="msl-options">
161205
{options.map((opt, i) => (
162206
<option
163-
data-msl
164-
onClick={() => addValue(i)}
207+
{...!singleSelect && { 'data-msl': true }}
208+
style={{ ...opt.style && opt.style }}
209+
onClick={() => { !opt.disabled && addValue(i) }}
165210
title={opt.label}
166211
key={opt.value + i + 10}
167-
className={`msl-option ${checkValueExist(opt, value) && 'msl-option-active'}`}
212+
className={`msl-option ${checkValueExist(opt, value) && 'msl-option-active'} ${opt.disabled && 'msl-option-disable'} ${opt.classes}`}
168213
value={opt.value}>
169214
{opt.label}
170215
</option>

0 commit comments

Comments
 (0)