※ lwc023CreateCustomComboboxGroup.html
<template>
<div class="test">
<div tabindex='0' onfocus={openBg} onblur={closeBg} class="custom-dropdown">
<!-- visual input -->
<div class={dropdownObj.isOpenCss}> <!-- Class명 - visual-input + no-data -->
<p class="info-msg">수강할 과목을 선택해 주세요</p>
<p class="show-data">{testValueString}</p>
</div>
<!-- dropdown checkbox -->
<template if:true={dropdownObj.isMainOpen}>
<div class="content">
<input type="text" value={searchKeyword} tabindex='0'
onclick={openInput} onblur={closeInput}
onkeyup={handleSearchKeyword} />
<div class="icon-wrap">
<template if:true={searchKeyword}>
<span onclick={deleteSearchKeyword} class="icon delete-icon"></span>
</template>
</div>
<template for:each={testCheckBoxList} for:item="item" for:index="idx">
<div key={item.value} data-index={idx}
class={item.activeCss} onclick={testValueChange}>
<span class="custom-checkbox"></span>
<p><lightning-formatted-rich-text value={item.splitLabel}>
</lightning-formatted-rich-text></p>
<!-- <p>{item.newLabel}</p> -->
</div>
</template>
</div>
</template>
</div>
</div>
</template>
※ lwc023CreateCustomComboboxGroup.js
import { LightningElement, track } from 'lwc';
import {subjectList} from './tempData.js';
export default class Lwc023CreateCustomComboboxGroup extends LightningElement {
@track dropdownObj = {
isMainOpen : false,
isBgOpen : false,
isInputOpen : false,
isOpenCss : 'visual-input no-data'
};
@track defaultCheckBoxList = subjectList;
@track testCheckBoxList = [];
@track testValueList = []; //선택된 value값
@track testValueString = ''; //선택된 라벨값
@track searchKeyword = '';
connectedCallback() {
this.defaultCheckBoxList.forEach((el, index) => {
el.activeCss = 'box-wrap';
el.splitLabel = el.label; //splitLabel
});
this.testCheckBoxList = this.defaultCheckBoxList;
}
//전체 화면 열고 닫기
openBg(){
console.log('▶ openBg ');
this.dropdownObj.isBgOpen = true;
this.dropdownMain();
}
closeBg(e){
console.log('▶ closeBg ', e.relatedTarget);
if(!e.relatedTarget){
this.dropdownObj.isBgOpen = false;
this.dropdownMain();
}
}
openInput(){
console.log('▷ openInput ');
this.dropdownObj.isInputOpen = true;
this.dropdownMain();
}
closeInput(){
console.log('▷ closeInput ');
this.dropdownObj.isInputOpen = false;
this.dropdownObj.isBgOpen = false;
this.dropdownMain();
}
dropdownMain(){
console.log('◈ dropdownMain ');
console.log(this.dropdownObj.isBgOpen, '//', this.dropdownObj.isInputOpen);
this.dropdownObj.isMainOpen = (this.dropdownObj.isBgOpen || this.dropdownObj.isInputOpen);
}
//데이터 있는지 확인 후 input영역에 보여줄 영역 선택하기
checkHaveData(){
console.log('testValueChange > testValueList : ', JSON.parse(JSON.stringify(this.testValueList)));
console.log('▶▶ checkHaveData > length : ', this.testValueList.length);
let haveData = this.testValueList.length > 0;
this.dropdownObj.isOpenCss = haveData ? 'visual-input' : 'visual-input no-data';
}
//데이터 바뀔 때 (콤보박스 선택되었을 때)
testValueChange(e){
console.log('▶▶ testValueChange ');
e.preventDefault();
let targetIndex = e.currentTarget.dataset.index;
let targetValue = this.testCheckBoxList[targetIndex].value;
console.log('targetIndex : ', targetIndex);
//값이 있으면 추가, 아니면 삭제
let hasValue = this.testValueList.findIndex((el) => el.value == targetValue);
console.log('hasValue : ', hasValue);
if(hasValue > -1){
this.testValueList.splice(hasValue, 1);
this.testCheckBoxList[targetIndex].activeCss = 'box-wrap';
}else{
this.testValueList.push(this.testCheckBoxList[targetIndex]);
this.testCheckBoxList[targetIndex].activeCss = 'is-checked box-wrap';
}
console.log('testValueChange > testCheckBoxList : ', JSON.parse(JSON.stringify(this.testCheckBoxList)));
this.testValueString = '';
this.testValueList.forEach((el, index) => {
if(el.activeCss == 'is-checked box-wrap'){
this.testValueString += el.label;
if((index + 1) < this.testValueList.length){
this.testValueString += ', ';
}
}
});
this.checkHaveData();
}
//검색 키워드 입력받음
handleSearchKeyword(e){
this.openInput();
let keyword = e.target.value;
this.searchKeyword = keyword;
console.log('handleSearchKeyword > keyword : ', keyword);
this.editList(keyword);
}
//리스트 목록 수정하기
editList(keyword){
let newList = [];
if(keyword == ''){
this.testCheckBoxList = this.defaultCheckBoxList;
}else{
this.defaultCheckBoxList.forEach((el, index) => {
let hasKeyword = el.label.indexOf(keyword);
//console.log('hasKeyword : ',hasKeyword);
if(hasKeyword !== -1){
newList.push(el);
}
});
console.log('editList > newList : ', JSON.parse(JSON.stringify(newList)));
this.testCheckBoxList = newList;
}
this.changeKeywordColor(keyword);
}
//리스트에서 해당되는 Keyword값만 글씨색 변경
changeKeywordColor(keyword){
this.testCheckBoxList.forEach((el, index) => {
if(keyword !== ''){
let splitText = el.label.split(keyword);
let newText = '';
splitText.forEach((el, index) => {
newText = newText + el;
if(splitText.length > (index + 1)){
newText = newText + '<span style="font-weight: bold;color: #DB2F1C;"">' + keyword + '</span>';
}
});
console.log('newLabel : ', JSON.parse(JSON.stringify(newText)))
el.splitLabel = newText;
}else{
el.splitLabel = el.label;
}
});
}
//검색 키워드 삭제하기
deleteSearchKeyword(e){
this.searchKeyword = '';
this.editList(this.searchKeyword);
}
}
※ lwc023CreateCustomComboboxGroup.css
.test {
background-color: #efefef;
max-width: 1600px;
margin: 20px auto;
padding: 20px;
}
.custom-dropdown{
display: block;
background-color: #d3d0e2;
}
.visual-input .info-msg,
.visual-input.no-data .show-data{
display: none;
}
.visual-input.no-data .info-msg,
.visual-input .show-data{
display: block;
}
.visual-input{
padding: 13px 20px;
border-radius: 5px;
border: 1px solid #333;
color: #333333;
background-color: #FFF;
}
.visual-input P{
margin: 0;
}
.content{
margin-top: 2px;
padding: 20px;
border-radius: 5px;
border: 1px solid #333333;
background-color: #FFFFFF;
position: relative;
}
.content input{
width: calc(100% - 30px);
padding: 13px 15px;
border: 1px solid #D2D2D2;
border-radius: 5px;
}
.box-wrap {
display: flex;
align-items: center;
gap: 8px;
height: 20px;
margin-top: 18px;
}
.icon-wrap{
display: flex;;
position: absolute;
top: 27px;
right: 30px;
gap: 5px;
}
.icon-wrap .icon{
height: 30px;
width: 30px;
background-position: center;
background-repeat: no-repeat;
background-size: contain;
}
.delete-icon{
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cg id='패스_44266' data-name='패스 44266' fill='%23d1d8e9'%3E%3Cpath d='M 29.5 29.5 L 0.5 29.5 L 0.5 0.5 L 29.5 0.5 L 29.5 29.5 Z' stroke='none'/%3E%3Cpath d='M 1 1 L 1 29 L 29 29 L 29 1 L 1 1 M 0 0 L 30 0 L 30 30 L 0 30 L 0 0 Z' stroke='none' fill='%23bfcae1'/%3E%3C/g%3E%3Cline id='선_374' data-name='선 374' x2='16' y2='16' transform='translate(7 7)' fill='none' stroke='%2367789e' stroke-width='2'/%3E%3Cpath id='패스_44265' data-name='패스 44265' d='M16,2.231l-16,16' transform='translate(7 4.769)' fill='none' stroke='%2367789e' stroke-width='2'/%3E%3C/svg%3E%0A");
}
.custom-checkbox{
width: 20px;
height: 20px;
display: block;
background-size: cover;
background-repeat: no-repeat;
margin-right: 5px;
background-image: url("data:image/svg+xml,%3Csvg id='checkbox' xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18'%3E%3Cg id='사각형_27' data-name='사각형 27' fill='%23fff' stroke='%23bbb' stroke-width='1'%3E%3Crect width='18' height='18' stroke='none'/%3E%3Crect x='0.5' y='0.5' width='17' height='17' fill='none'/%3E%3C/g%3E%3C/svg%3E%0A");
}
.is-checked .custom-checkbox{
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18'%3E%3Crect id='사각형_27' data-name='사각형 27' width='18' height='18' fill='%23281cdb'/%3E%3Cpath id='check-bold' d='M6.072,12.88,1.79,8.683l1.5-1.457L6.072,9.971,11.294,4.88l1.5,1.457Z' transform='translate(2.21 0.12)' fill='%23fff'/%3E%3C/svg%3E%0A");
}
※ tempData.js (데이터 파일)
const subjectList = [
{label : '수학', value : 'math'},
{label : '국어', value : 'Korean'},
{label : '영어', value : 'english'},
{label : '과학', value : 'science'},
{label : '한국사', value : 'Koreanhistory'},
]
export { subjectList };
● 결과 확인하기
여러가지 방법이 있으니 본인에게 맞는 방법을 사용하면 좋을 것 같습니다.