다른 라이브러리를 사용하지 않고 JS와 CSS로만 만들었습니다.
<template>
<div class="wrapper">
<div class="input-wrap">
<lightning-input type="text" label="총 페이징 개수" class="total-input" value={numData.total}></lightning-input>
<lightning-input type="text" label="한번에 보여지는 페이징 개수" class="pagenum-input" value={numData.showPage}></lightning-input>
<lightning-button label="설정" title="Non-primary action" onclick={handleClick}
class="slds-m-left_x-small"></lightning-button>
</div>
<c-lwc038-paging-child
total={numData.total}
show-count={numData.showPage}
onnumchange={numChange}
></c-lwc038-paging-child>
<p class="current-txt">[lwc037Paging] 현재 페이지 번호 : {currentPageNum}</p>
</div>
</template>
import { LightningElement, track} from 'lwc';
export default class Lwc037Paging extends LightningElement {
// table 페이징에 사용
@track numData = {
total : 25, showPage : 10
};
@track showInput = true; //reset에 사용
@track currentPageNum;
async handleClick(){
let totalEl = this.template.querySelector('.total-input');
if(totalEl){
console.log('totalEl.value : ', totalEl.value);
this.numData.total = totalEl.value;
}
let pagenumEl = this.template.querySelector('.pagenum-input');
if(pagenumEl){
console.log('pagenumEl.value : ', pagenumEl.value);
this.numData.showPage = pagenumEl.value;
}
}
numChange(e){
console.log('e : ', JSON.parse(JSON.stringify(e.detail)));
this.currentPageNum = e.detail.activeNum;
}
}
.wrapper{
background-color: #efefef;
padding: 30px 20px;
}
.input-wrap{
width: 500px;
display: grid;
gap: 20px;
grid-template-columns: 3fr 3fr 2fr;
padding-bottom: 30px;
align-items: end;
}
.input-area {
display: flex;
align-items: flex-end;
}
.current-txt{
margin-top: 30px;
background-color: #ddd;
padding: 5px 17px 5px 5px;
border-radius: 5px;
display: inline-flex;
}
<template>
<div class="wrapper" data-total={total} data-count={showCount}>
<div class="paging-wrap">
<span class="first" onclick={goFirst}></span>
<span class="prev" onclick={goPrev}></span>
<div class="show-menu">
<div class="num-wrap">
<template lwc:if={pagingList} for:each={pagingList} for:item="item" for:index="idx">
<a class="paging-num" key={item} onclick={numActive} data-idx={idx}>
{item.label}
</a>
</template>
</div>
</div>
<span class="next" onclick={goNext}></span>
<span class="last" onclick={goLast}></span>
</div>
</div>
</template>
import { LightningElement, track, api} from 'lwc';
export default class Lwc038PagingChild extends LightningElement {
@api total;/* total 세팅 */
@api showCount;/* 한번에 보여지는 숫자의 개수 세팅 */
@track defaultTotal = 0;
@track defaultShowCount = 0;
@track pagingList = [];
@track activeNum = 0; // 현재 활성화 되어있는 페이지 (0부터시작)
@track activePage = 0;
@track isFirstRender = true;
async renderedCallback() {
// console.log('-----------------------------------------------');
// console.log('this.isFirstRender == true : ', this.isFirstRender == true);
// console.log('this.defaultTotal != this.total : ', this.defaultTotal != this.total);
// console.log('(this.defaultShowCount != this.showCount) : ', (this.defaultShowCount != this.showCount));
if((this.defaultTotal != this.total) || (this.defaultShowCount != this.showCount) || (this.isFirstRender == true)){
let newList = [];
for (let index = 0; index < this.total; index++) {
newList.push({label : (index + 1)});
}
this.pagingList = newList;
let numElAll = this.template.querySelectorAll('.num-wrap a');
if(numElAll.length == this.total){
let oneWidth = this.total > 999 ? 50 : 35;
numElAll.forEach((el) => { el.style.minWidth = oneWidth + 'px' });
console.log('numElAll : ', numElAll.length);
this.isFirstRender = false;
this.activeNum = 0;
this.activePage = 0;
this.defaultTotal = this.total;
this.defaultShowCount = this.showCount;
// console.log('this.pagingList : ', this.pagingList);
await this.setPagingPosition();
}
}
}
// 해당 탭의 Number를 클릭했을 때
numActive(e){
let idx = e.currentTarget.dataset.idx;
console.log('idx : ', idx);
this.activeNum = idx;
this.setPagingPosition();
}
// 다음 페이지 이동
goNext(){
let pageNum = this.activePage + 1;
let nextNum = pageNum * this.showCount;
if(nextNum < this.total){
this.activeNum = nextNum < this.total ? nextNum : this.activeNum;
this.activePage = pageNum;
this.setPagingPosition();
}
}
// 이전 페이지 이동
goPrev(){
if(this.activeNum != 0){
let pageNum = this.activePage - 1 > 0 ? this.activePage - 1 : 0;
let prevNum = pageNum * this.showCount;
this.activeNum = prevNum >= 0 ? prevNum : this.activeNum;
this.activePage = pageNum;
this.setPagingPosition();
}
}
//맨 처음 페이지로 이동
goFirst(){
if(this.activePage != 0){
this.activeNum = 0;
this.activePage = 0;
this.setPagingPosition();
}
}
//맨 마지막 페이지로 이동
goLast(){
if(((this.activePage + 1) * this.showCount) < this.total){
let pageNum = this.total % this.showCount == 0 ? (this.total/this.showCount - 1) : Math.floor(this.total/this.showCount);
this.activePage = pageNum;
this.activeNum = pageNum * this.showCount;
this.setPagingPosition();
}
}
setPagingPosition(){
// 보여지는 값의 max-width 변경
let showMenu = this.template.querySelector('.show-menu');
let oneWidth = this.total > 999 ? 50 : 35;
// console.log('oneWidth : ', oneWidth);
if((this.activePage + 1) * this.showCount < this.total ){
showMenu.style.maxWidth = (this.showCount * oneWidth) + 'px';
}else{
let lastChild = this.total - (this.activePage * this.showCount);
showMenu.style.maxWidth = (lastChild * oneWidth) + 'px';
}
// 보여지는 값의 위치 변경
let numWrap = this.template.querySelector('.num-wrap');
// console.log('this.showCount : ', this.showCount, ', this.activePage : ', this.activePage);
numWrap.style.left = (this.activePage * this.showCount * -1 * oneWidth) + 'px';
let numElAll = this.template.querySelectorAll('.num-wrap a');
numElAll.forEach((el) => { el.style.minWidth = oneWidth + 'px' });
// console.log('numElAll : ', numElAll.length)
let numList = this.template.querySelectorAll('.paging-num');
// 버튼 활성화 표시
numList.forEach((el, index) => {
if(this.activeNum == index){
// console.log('this.activeNum : , ', this.activeNum);
el.classList.add("active");
}else{
el.classList.remove("active");
}
});
// 마지막 페이지인지 확인하고 disabled하기
let isLastPage = ((this.activePage + 1) * this.showCount) >= this.total;
let nextCmp = this.template.querySelector('.next');
let lastCmp = this.template.querySelector('.last');
if(isLastPage){
this.addDisabled(nextCmp);
this.addDisabled(lastCmp);
}else{
this.removeDisabled(nextCmp);
this.removeDisabled(lastCmp);
}
// 첫번째 페이지인지 확인하고 disabled하기
let isFirstPage = (this.activePage == 0);
let firstCmp = this.template.querySelector('.first');
let prevCmp = this.template.querySelector('.prev');
if(isFirstPage){
this.addDisabled(firstCmp);
this.addDisabled(prevCmp);
}else{
this.removeDisabled(firstCmp);
this.removeDisabled(prevCmp);
}
this.sendCurrentPage();
}
addDisabled(item){
item.setAttribute('disabled', 'disabled');
item.classList.add("disabled");
}
removeDisabled(item){
item.removeAttribute('disabled');
item.classList.remove("disabled");
}
// 현재 활성화된 페이지가 몇페이지인지 보내줌
sendCurrentPage(){
// console.log('▶ 현재 활성화된 페이지 this.activeNum + 1 : ', (Number(this.activeNum) + 1));
const customEvt = new CustomEvent('numchange', {
detail: {
activeNum: Number(this.activeNum) + 1
}
});
this.dispatchEvent(customEvt);
}
}
.paging-wrap{
display: flex;
gap: 5px;
align-items: center;
}
.show-menu{
width: 100%;
overflow: hidden;
overflow-x: hidden;
margin: 0 10px;
}
.num-wrap{
/* left: -200px; */
transition: all 0.2s ease-out;
position: relative;
display: flex;
}
.num-wrap .paging-num{
text-align:center;
color: #282828;
text-decoration: none;
position: relative;
}
.num-wrap .paging-num.active{
font-weight: bold;
text-decoration: dashed;
}
.num-wrap .paging-num.active:after{
content: '';
position: absolute;
bottom: 0px;
width: 40%;
height: 1px;
background-color: #282828;
left: 50%;
transform: translateX(-50%);
}
.common-pagination a{
display: inline-flex;
text-align: center;
}
.next.disabled, .last.disabled, .first.disabled, .prev.disabled{
opacity: 0.3;
}
.next, .last, .first, .prev{
width: 16px;
height: 16px;
display: inline-flex;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.next{
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cg id='그룹_1' data-name='그룹 1' transform='translate(-49 -128)'%3E%3Cg id='사각형_1' data-name='사각형 1' transform='translate(49 128)' fill='%23fff' stroke='%23323145' stroke-linejoin='round' stroke-width='1'%3E%3Crect width='15' height='15' stroke='none'/%3E%3Crect x='0.5' y='0.5' width='14' height='14' fill='none'/%3E%3C/g%3E%3Cpath id='패스_2' data-name='패스 2' d='M0-.5l5,5-5,5' transform='translate(54 131)' fill='none' stroke='%23323145' stroke-linejoin='round' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E%0A");
}
.last{
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cg id='그룹_2' data-name='그룹 2' transform='translate(-49 -128)'%3E%3Cpath id='패스_1' data-name='패스 1' d='M0,0' transform='translate(57.5 133.5)' fill='none' stroke='%23707070' stroke-width='1'/%3E%3Cg id='그룹_1' data-name='그룹 1'%3E%3Cg id='사각형_1' data-name='사각형 1' transform='translate(49 128)' fill='%23fff' stroke='%23323145' stroke-linejoin='round' stroke-width='1'%3E%3Crect width='15' height='15' stroke='none'/%3E%3Crect x='0.5' y='0.5' width='14' height='14' fill='none'/%3E%3C/g%3E%3Cpath id='패스_2' data-name='패스 2' d='M0-.5l5,5-5,5' transform='translate(56 131)' fill='none' stroke='%23323145' stroke-linejoin='round' stroke-width='1'/%3E%3Cpath id='패스_3' data-name='패스 3' d='M0-.5l5,5-5,5' transform='translate(53 131)' fill='none' stroke='%23323145' stroke-linejoin='round' stroke-width='1'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A");
}
.first{
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cg id='그룹_2' data-name='그룹 2' transform='translate(64 143) rotate(180)'%3E%3Cpath id='패스_1' data-name='패스 1' d='M0,0' transform='translate(57.5 133.5)' fill='none' stroke='%23707070' stroke-width='1'/%3E%3Cg id='그룹_1' data-name='그룹 1'%3E%3Cg id='사각형_1' data-name='사각형 1' transform='translate(49 128)' fill='%23fff' stroke='%23323145' stroke-linejoin='round' stroke-width='1'%3E%3Crect width='15' height='15' stroke='none'/%3E%3Crect x='0.5' y='0.5' width='14' height='14' fill='none'/%3E%3C/g%3E%3Cpath id='패스_2' data-name='패스 2' d='M0-.5l5,5-5,5' transform='translate(56 131)' fill='none' stroke='%23323145' stroke-linejoin='round' stroke-width='1'/%3E%3Cpath id='패스_3' data-name='패스 3' d='M0-.5l5,5-5,5' transform='translate(53 131)' fill='none' stroke='%23323145' stroke-linejoin='round' stroke-width='1'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A");
}
.prev{
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cg id='사각형_1' data-name='사각형 1' transform='translate(15 15) rotate(180)' fill='%23fff' stroke='%23323145' stroke-linejoin='round' stroke-width='1'%3E%3Crect width='15' height='15' stroke='none'/%3E%3Crect x='0.5' y='0.5' width='14' height='14' fill='none'/%3E%3C/g%3E%3Cpath id='패스_2' data-name='패스 2' d='M0-.5l5,5-5,5' transform='translate(10 12) rotate(180)' fill='none' stroke='%23323145' stroke-linejoin='round' stroke-width='1'/%3E%3C/svg%3E%0A");
}