[TypeScript][Moment] 월 선택기 만들기
68775 단어 typescriptmoment
소개
나는 피카데이를 좋아한다. 이는 프레임워크 독립적이고 훌륭한 브라우저 호환성(IE7 포함!)이기 때문입니다.
그러나 날짜만 선택할 수 있습니다. 이번에는 월을 선택하고 싶습니다.
Chrome 및 Edge 또는 일부 다른 브라우저의 경우 "input type="month""를 사용할 수 있습니다.
그러나 모든 최신 브라우저에서 사용할 수 없고 연도 디자인이 마음에 들지 않기 때문에 사용하고 싶지 않습니다.
jQuery, React 등의 월 선택기를 밖에 찾을 수 없었기 때문에 만들어 보도록 하겠습니다.
이 샘플에서는 이전 브라우저에 대해 신경 쓰지 않습니다.
호환성을 원하는 경우 Autoprefixer를 사용하거나 Flexbox 사용을 중지하고 레이아웃용 테이블로 변경할 수 있습니다.
환경
결과
형세
대상 입력 요소 아래에 창을 놓고 싶습니다.
그래서 "getBoundingClientRect"를 사용하여 대상 rect를 얻습니다.
monthPicker.ts
export class MonthPicker {
private inputTarget: HTMLInputElement|null = null;
private pickerArea: HTMLElement;
public constructor(target: HTMLInputElement) {
this.pickerArea = document.createElement('div');
if(target == null) {
console.error('target was null');
return;
}
this.inputTarget = target;
const parentElement = (target.parentElement == null)? document.body: target.parentElement;
this.addMonthPickerArea(parentElement, target.getBoundingClientRect());
}
private addMonthPickerArea(parent: HTMLElement, targetRect: DOMRect) {
this.pickerArea.className = 'monthpicker_area';
this.pickerArea.style.position = 'absolute';
this.pickerArea.style.left = `${targetRect.left}px`;
this.pickerArea.style.top = `${targetRect.top + targetRect.height}px`;
this.pickerArea.hidden = true;
parent.appendChild(this.pickerArea);
}
}
월 picker("pickerArea")의 루트 요소를 생성한 후 요소를 추가하기만 하면 됩니다.
표시|숨기기
대상 입력 요소를 클릭하면 월 선택기 영역이 표시됩니다.
그런 다음 다른 곳을 클릭하면 숨겨집니다.
표시한 후 해당 영역을 클릭하면 이 순서대로 클릭 이벤트가 발생합니다.
그래서 1.에 "setTimeout"을 사용하고 월 선택기 영역을 클릭하면 2.를 무시합니다.
monthPicker.ts
import { MonthPickerOption } from './monthPicker.type';
export class MonthPicker {
...
public constructor(target: HTMLInputElement, option?: MonthPickerOption) {
...
target.addEventListener('click', _ => this.show());
window.addEventListener('click', _ => this.hide());
}
private addMonthPickerArea(parent: HTMLElement, targetRect: DOMRect) {
...
this.pickerArea.onclick = () => this.setIgnoreHiding();
parent.appendChild(this.pickerArea);
}
...
private setIgnoreHiding() {
if(this.ignoreHiding === true) {
return;
}
this.ignoreHiding = true;
setTimeout((_) => this.ignoreHiding = false, 500);
}
private show() {
this.selectSelectedMonth();
this.setIgnoreHiding();
this.pickerArea.hidden = false;
}
...
private hide() {
if(this.ignoreHiding === true) {
return;
}
this.pickerArea.hidden = true;
}
...
}
선택한 달 가져오기
"날짜"유형을 사용하여 대상 입력 요소 텍스트를 설정하면 값이 아래와 같기 때문입니다.
Mon Jul 05 2021
"
${date.getFullYear()} ${date.getMonth()}
"와 같이 고정된 형식을 사용하는 경우도 있지만 "Moment"를 사용하기로 했습니다.monthPicker.type.ts
export type MonthPickerOption = {
months?: string[],
outputFormat?: string,
}
monthPicker.ts
import moment from 'moment';
import { MonthPickerOption } from './monthPicker.type';
export class MonthPicker {
public constructor(target: HTMLInputElement, option?: MonthPickerOption) {
...
this.addMonthPickerArea(parentElement, target.getBoundingClientRect());
this.addMonthPickerFrame(this.pickerArea, (option)? option: null);
...
}
...
private addMonthArea(parent: HTMLElement, option: MonthPickerOption|null) {
const pickerMonthArea = document.createElement('div');
pickerMonthArea.className = 'monthpicker_month_area';
parent.appendChild(pickerMonthArea);
const monthRow1 = document.createElement('div');
monthRow1.className = 'monthpicker_month_row';
pickerMonthArea.appendChild(monthRow1);
const monthRow2 = document.createElement('div');
monthRow2.className = 'monthpicker_month_row';
pickerMonthArea.appendChild(monthRow2);
const format = (option?.outputFormat)? option.outputFormat: null;
const defaultMonths = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
const months = (option?.months)? option.months: defaultMonths;
for(let i = 0; i < defaultMonths.length; i++) {
const pickerMonth = document.createElement('button');
const month = (months.length > i)? months[i]: defaultMonths[i];
pickerMonth.textContent = month;
pickerMonth.className = 'monthpicker_month_input';
pickerMonth.onclick = (ev) => this.setSelectedDate(ev, month, format);
if(this.pickerMonths.length < 6) {
monthRow1.appendChild(pickerMonth);
} else {
monthRow2.appendChild(pickerMonth);
}
this.pickerMonths.push(pickerMonth);
}
}
...
private setSelectedDate(ev: MouseEvent, month: string, format: string|null) {
if(this.inputTarget == null) {
return;
}
const selectedDate = new Date(`${this.currentYear} ${month}`);
this.inputTarget.value = moment(selectedDate).format((format == null)? 'YYYY-MM': format);
this.hide();
}
...
전체 코드
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Month picker sample</title>
<meta charset="utf-8">
<link rel="stylesheet" href="../css/month_picker.css" />
</head>
<body>
<div class="input_area">
<input type="text" id="month_picker_target">
</div>
<input type="month">
<script src="js/main.page.js"></script>
</body>
</html>
month_picker.css
.monthpicker_frame {
border: 1px solid black;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
z-index: 9999;
box-shadow: 0 5px 15px -5px rgba(0,0,0,.5);
width: 400px;
height: 160px;
background-color: white;
}
.monthpicker_year_area {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 90%;
height: 20%;
}
.monthpicker_year_move_input {
background-color: white;
border: 0;
}
.monthpicker_month_area {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
width: 90%;
height: 65%;
}
.monthpicker_month_row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
height: 40%;
}
.monthpicker_month_input {
background-color: #f5f5f5;
border: 0;
border-radius: 0.4rem;
display: flex;
align-items: center;
justify-content: center;
width: 16%;
height: 100%;
outline: none;
}
.monthpicker_month_input:hover{
background-color: #ff8000;
color: white;
}
.monthpicker_month_input:disabled {
background-color: rgb(51, 170, 255);
color: white;
}
main.page.ts
import { MonthPicker } from "./monthPicker";
export function init() {
const inputElement = document.getElementById('month_picker_target') as HTMLInputElement;
const monthPicker = new MonthPicker(inputElement,{
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
outputFormat: 'MMMM-YYYY'
});
}
init();
monthPicker.ts
import moment from 'moment';
import { MonthPickerOption } from './monthPicker.type';
export class MonthPicker {
private inputTarget: HTMLInputElement|null = null;
private ignoreHiding: boolean = false;
private pickerArea: HTMLElement;
private pickerMonths: HTMLButtonElement[] = [];
private currentYear: number;
private currentYearElement: HTMLElement;
public constructor(target: HTMLInputElement, option?: MonthPickerOption) {
this.currentYearElement = document.createElement('div');
this.currentYear = (new Date()).getFullYear();
this.pickerArea = document.createElement('div');
if(target == null) {
console.error('target was null');
return;
}
this.inputTarget = target;
const parentElement = (target.parentElement == null)? document.body: target.parentElement;
this.addMonthPickerArea(parentElement, target.getBoundingClientRect());
this.addMonthPickerFrame(this.pickerArea, (option)? option: null);
target.addEventListener('click', _ => this.show());
window.addEventListener('click', _ => this.hide());
}
private addMonthPickerArea(parent: HTMLElement, targetRect: DOMRect) {
this.pickerArea.className = 'monthpicker_area';
this.pickerArea.style.position = 'absolute';
this.pickerArea.style.left = `${targetRect.left}px`;
this.pickerArea.style.top = `${targetRect.top + targetRect.height}px`;
this.pickerArea.hidden = true;
this.pickerArea.onclick = () => this.setIgnoreHiding();
parent.appendChild(this.pickerArea);
}
private addMonthPickerFrame(area: HTMLElement, option: MonthPickerOption|null) {
const pickerFrame = document.createElement('div');
pickerFrame.className = 'monthpicker_frame';
area.appendChild(pickerFrame);
this.addYearArea(pickerFrame);
this.addMonthArea(pickerFrame, option);
}
private addYearArea(parent: HTMLElement) {
const pickerYearArea = document.createElement('div');
pickerYearArea.className = 'monthpicker_year_area';
parent.appendChild(pickerYearArea);
const moveBackward = document.createElement('button');
moveBackward.className = 'monthpicker_year_move_input';
moveBackward.textContent = '◀';
pickerYearArea.appendChild(moveBackward);
moveBackward.onclick = () => this.changeYear(-1);
this.currentYearElement.className = 'monthpicker_year_current';
this.currentYearElement.textContent = `${this.currentYear}`;
pickerYearArea.appendChild(this.currentYearElement);
const moveForward = document.createElement('button');
moveForward.className = 'monthpicker_year_move_input';
moveForward.textContent = '▶';
pickerYearArea.appendChild(moveForward);
moveForward.onclick = () => this.changeYear(1);
}
private addMonthArea(parent: HTMLElement, option: MonthPickerOption|null) {
const pickerMonthArea = document.createElement('div');
pickerMonthArea.className = 'monthpicker_month_area';
parent.appendChild(pickerMonthArea);
const monthRow1 = document.createElement('div');
monthRow1.className = 'monthpicker_month_row';
pickerMonthArea.appendChild(monthRow1);
const monthRow2 = document.createElement('div');
monthRow2.className = 'monthpicker_month_row';
pickerMonthArea.appendChild(monthRow2);
const format = (option?.outputFormat)? option.outputFormat: null;
const defaultMonths = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
const months = (option?.months)? option.months: defaultMonths;
for(let i = 0; i < defaultMonths.length; i++) {
const pickerMonth = document.createElement('button');
const month = (months.length > i)? months[i]: defaultMonths[i];
pickerMonth.textContent = month;
pickerMonth.className = 'monthpicker_month_input';
pickerMonth.onclick = (ev) => this.setSelectedDate(ev, month, format);
if(this.pickerMonths.length < 6) {
monthRow1.appendChild(pickerMonth);
} else {
monthRow2.appendChild(pickerMonth);
}
this.pickerMonths.push(pickerMonth);
}
}
private setIgnoreHiding() {
if(this.ignoreHiding === true) {
return;
}
this.ignoreHiding = true;
setTimeout((_) => this.ignoreHiding = false, 500);
}
private show() {
this.selectSelectedMonth();
this.setIgnoreHiding();
this.pickerArea.hidden = false;
}
private getSelectedMonthIndex(currentValue: string): number {
const currentDate = new Date(currentValue);
if(currentDate.getFullYear() !== this.currentYear) {
return -1;
}
return currentDate.getMonth();
}
private selectSelectedMonth() {
if(this.inputTarget?.value == null) {
return;
}
const selectedMonthIndex = this.getSelectedMonthIndex(this.inputTarget.value);
for(let i = 0; i < this.pickerMonths.length; i++) {
this.pickerMonths[i].disabled = (i === selectedMonthIndex);
}
}
private hide() {
if(this.ignoreHiding === true) {
return;
}
this.pickerArea.hidden = true;
}
private setSelectedDate(ev: MouseEvent, month: string, format: string|null) {
if(this.inputTarget == null) {
return;
}
const selectedDate = new Date(`${this.currentYear} ${month}`);
this.inputTarget.value = moment(selectedDate).format((format == null)? 'YYYY-MM': format);
this.hide();
}
private changeYear(addYear: number) {
this.currentYear += addYear;
this.currentYearElement.textContent = `${this.currentYear}`;
this.selectSelectedMonth();
}
}
Reference
이 문제에 관하여([TypeScript][Moment] 월 선택기 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/masanori_msl/typescript-moment-create-month-picker-3f57텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)