371 lines
13 KiB
JavaScript
371 lines
13 KiB
JavaScript
function uiGnbHandler() {
|
|
/**
|
|
* .menu-first-depth, mouseenter일때 함수 작동
|
|
* 문서의 header 찾음
|
|
* secondDeptSelector의 각 자식요소의 갯수의 max를 받아서
|
|
* header에 data-sub에 넣고 이를 css에서 높이 결정을 함
|
|
*
|
|
*/
|
|
const headerSelector = document.getElementsByTagName('header')[0];
|
|
const secondDeptSelector = Array.from(headerSelector.getElementsByClassName('menu-second-depth'));
|
|
const childrenLengthArray = secondDeptSelector.map((i) => i.children.length);
|
|
const maxChildren = Math.max(...childrenLengthArray);
|
|
|
|
headerSelector.setAttribute('open', 'true');
|
|
headerSelector.style.setProperty('--max-length', maxChildren)
|
|
secondDeptSelector.forEach(element => {
|
|
element.setAttribute('open', 'true');
|
|
});
|
|
|
|
headerSelector.addEventListener('mouseleave', () => {
|
|
headerSelector.setAttribute('open', 'false');
|
|
secondDeptSelector.forEach(element => {
|
|
element.setAttribute('open', 'false');
|
|
});
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function uiAttrOpen({ selector, state }) {
|
|
if (state === 'set') {
|
|
selector.setAttribute('open', '');
|
|
} else {
|
|
selector.removeAttribute('open');
|
|
}
|
|
}
|
|
|
|
function uiTableCollapseHandler(event) {
|
|
|
|
/**
|
|
* 함수는 table > tbody > tr > td > details 위치에 onClick으로 작동해야함
|
|
* html ui구조는 table > tbody > tr > td > details > summary 구조
|
|
* details 태그의 open 상태에 따라 attribute의 open set 또는 remove
|
|
*/
|
|
const parentTd = event.target.offsetParent;
|
|
const parentTr = parentTd.offsetParent;
|
|
const isDetailOpen = parentTd.children[0].open;
|
|
parentTr.toggleAttribute('open', !isDetailOpen);
|
|
}
|
|
|
|
function uiTableCollapseAllHandler(event) {
|
|
const targetData = event.target.dataset;
|
|
const dataSet = targetData.state;
|
|
const tbodySelector = document.getElementsByTagName('tbody')[0];
|
|
const trSelector = Array.from(tbodySelector.querySelectorAll('tr'));
|
|
const trWithDetails = trSelector.filter(tr => tr.querySelector('details'));
|
|
|
|
trWithDetails.forEach(tr => {
|
|
const details = tr.querySelector('details');
|
|
if (details) {
|
|
dataSet === 'close' ?
|
|
(
|
|
uiAttrOpen({ selector: tr, state: 'set' }),
|
|
uiAttrOpen({ selector: details, state: 'set' }),
|
|
event.target.setAttribute('data-state', 'open'),
|
|
event.target.textContent = '전체 닫기'
|
|
)
|
|
: (
|
|
uiAttrOpen({ selector: tr, state: 'remove' }),
|
|
uiAttrOpen({ selector: details, state: 'remove' }),
|
|
event.target.setAttribute('data-state', 'close'),
|
|
event.target.textContent = '전체 열기'
|
|
)
|
|
}
|
|
});
|
|
}
|
|
|
|
function uiModalOpenHandler(event) {
|
|
const currentTarget = event.target;
|
|
const targetModal = currentTarget.offsetParent;
|
|
const isOpen = targetModal.dataset.state;
|
|
if (isOpen !== undefined && isOpen === 'open') {
|
|
targetModal.setAttribute('data-state', 'close');
|
|
}
|
|
if (isOpen === undefined) {
|
|
const getDataSet = currentTarget.dataset.modal;
|
|
const modalElement = document.querySelector(`[data-modal="${getDataSet}"].modal`);
|
|
if (modalElement) {
|
|
modalElement.setAttribute('data-state', 'open');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function uiScrollHandler(event) {
|
|
const currentScrollPosition = document.scrollingElement.scrollTop;
|
|
const indicatorSelector = document.getElementsByClassName('indicators')[0];
|
|
const articles = Array.from(indicatorSelector.getElementsByClassName('editor-article'));
|
|
const indicatorLinks = Array.from(document.querySelectorAll('.editor-indicator a'));
|
|
|
|
articles.forEach((article, index) => {
|
|
const articleTop = article.offsetTop;
|
|
const articleHeight = article.offsetHeight;
|
|
const nextArticleTop = index < articles.length - 1 ? articles[index + 1].offsetTop : document.body.scrollHeight;
|
|
|
|
if (currentScrollPosition >= articleTop && currentScrollPosition < nextArticleTop) {
|
|
indicatorLinks.forEach(link => link.removeAttribute('current'));
|
|
|
|
const correspondingLink = indicatorLinks.find(link => link.getAttribute('href') === `#${article.id}`);
|
|
if (correspondingLink) {
|
|
correspondingLink.setAttribute('current', '');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
function uiDropZoneHandler() {
|
|
const dropZone = document.getElementById('dropZone');
|
|
const fileInput = document.getElementById('fileInput');
|
|
const fileList = document.getElementById('fileList');
|
|
|
|
dropZone.addEventListener('click', () => fileInput.click());
|
|
|
|
fileInput.addEventListener('change', uiUpdateFileList);
|
|
|
|
dropZone.addEventListener('dragover', (event) => {
|
|
event.preventDefault();
|
|
dropZone.classList.add('hover');
|
|
});
|
|
|
|
dropZone.addEventListener('dragleave', () => dropZone.classList.remove('hover'));
|
|
|
|
dropZone.addEventListener('drop', (event) => {
|
|
event.preventDefault();
|
|
dropZone.classList.remove('hover');
|
|
const files = Array.from(event.dataTransfer.files);
|
|
fileInput.files = new DataTransfer().files;
|
|
Array.from(files).forEach(file => fileInput.files = new DataTransfer().files.add(file));
|
|
uiUpdateFileList(files);
|
|
});
|
|
}
|
|
|
|
|
|
function uiUpdateFileList(files = Array.from(fileInput.files)) {
|
|
fileList.innerHTML = '';
|
|
Array.from(files).forEach(file => {
|
|
const listItem = document.createElement('div');
|
|
listItem.textContent = `${file.name} - ${Math.round(file.size / 1024)} KB`;
|
|
fileList.appendChild(listItem);
|
|
});
|
|
}
|
|
|
|
function uiEventCalendarHandler() {
|
|
/**
|
|
* 주요일정 캘린더 .event-dot 요소 찾아 배열
|
|
*
|
|
* [HTML영역]
|
|
* <div class="event-dot" style="--background-color:..." data-title="..." data-desc="..." />
|
|
* 로 되어 있어야함.
|
|
* 각 요소 mouseenter가 되었을때 .event-hint 요소 생성
|
|
* dataSet의 data-title, data-desc을 가져와서 event-hint 요소에 출력
|
|
* style의 --background-color에 색상값을 넣으면 css변수를 이용하여
|
|
* event-dot의 동그라미 요소와 event-hint에 같이 색상 출력
|
|
*
|
|
*/
|
|
const eventDotElements = document.querySelectorAll('.event-dot');
|
|
|
|
eventDotElements.forEach((i) => {
|
|
i.addEventListener('mouseenter', (j) => {
|
|
const title = j.target.getAttribute('data-title');
|
|
const desc = j.target.getAttribute('data-desc');
|
|
const hintElement = document.createElement('div');
|
|
hintElement.classList.add('event-hint');
|
|
hintElement.innerHTML = `
|
|
<div>
|
|
<p>${title}</p>
|
|
<p>${desc}</p>
|
|
</div>
|
|
`;
|
|
|
|
j.target.appendChild(hintElement);
|
|
});
|
|
|
|
i.addEventListener('mouseleave', (j) => {
|
|
const hintElement = j.target.querySelector('.event-hint');
|
|
if (hintElement) {
|
|
hintElement.remove();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
function uiSelectorRenderer(style) {
|
|
/**
|
|
* <select>...</select> HTML요소를
|
|
* <details><summary>...</summary></details>로 변경
|
|
* <select>에 disappear 클래스를 추가하여 안보이게 처리함
|
|
* 24-11-18 dataSet에 width추가
|
|
* 24-11-28 외부 클릭 시 모든 open 속성 제거
|
|
*/
|
|
const styleClass = style ? style : 'default';
|
|
const selectElement = document.querySelectorAll('select');
|
|
|
|
selectElement.forEach((i, index) => {
|
|
i.classList.add('disappear');
|
|
i.classList.add(`origin-select-${index}`);
|
|
const dataWidth = i.dataset.width ? `${i.dataset.width}px` : 'auto';
|
|
const optionsElement = Array.from(i.options);
|
|
const element = document.createElement('div');
|
|
element.classList.add('render-select');
|
|
element.classList.add(styleClass);
|
|
element.style.width = dataWidth;
|
|
element.innerHTML =
|
|
`<details onclick="uiRenderedHandler(event)">` +
|
|
`<summary class="render-select-${index}">${optionsElement[0].label}</summary>` +
|
|
`<div class="render-options">` +
|
|
optionsElement.map((option, index) => {
|
|
return `<div data-index="${index}" data-label="${option.label}" data-value="${option.value}">${option.label}</div>`;
|
|
}).join('');
|
|
+ `</div>`
|
|
i.insertAdjacentElement('beforebegin', element);
|
|
+ '</details>'
|
|
});
|
|
|
|
document.addEventListener('click', (event) => {
|
|
const detailsElements = document.querySelectorAll('details[open]');
|
|
detailsElements.forEach((details) => {
|
|
if (!details.contains(event.target)) {
|
|
details.removeAttribute('open');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
function uiRenderedHandler(event) {
|
|
/**
|
|
* uiSelectorRenderer()로 변경된 <details>요소가 변경될때
|
|
* <select>의 요소도 같이 변경
|
|
* <select>는 onchange메소드가 아닌 onclick메소드로 처리해 줘야함
|
|
* 그러면 select의 변경 이벤트를 처리할 수 있음
|
|
* ex) X <select onchange="console.log(event)">
|
|
* O <select onclick="console.log(event)">
|
|
*/
|
|
const eventParentElement = event.target.parentElement;
|
|
const options = eventParentElement.classList.value;
|
|
const isOptions = options === 'render-options';
|
|
|
|
if (isOptions) {
|
|
const eventTargetIndex = event.target.parentNode.parentNode.parentNode.nextSibling.classList.value.split('-')[2];
|
|
const eventSibilingSelect = document.querySelector(`.origin-select-${eventTargetIndex}`)
|
|
const isDetailElement = event.target.parentElement.parentElement;
|
|
const summaryElement = isDetailElement.children[0];
|
|
const dataLabel = event.target.dataset.label;
|
|
const dataIndex = event.target.dataset.index;
|
|
isDetailElement && isDetailElement.removeAttribute('open');
|
|
eventSibilingSelect.selectedIndex = dataIndex;
|
|
summaryElement.innerHTML = dataLabel;
|
|
eventSibilingSelect.click();
|
|
}
|
|
}
|
|
|
|
|
|
function uiSpinnerSizeHandler(selector, callback) {
|
|
const targetElement = document.querySelector(selector);
|
|
|
|
if (!targetElement) {
|
|
return;
|
|
}
|
|
const observer = new IntersectionObserver((entries, observer) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
callback();
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
});
|
|
observer.observe(targetElement);
|
|
}
|
|
|
|
function uiCalcSetSpaceHeight(selector) {
|
|
const defaultSelector = selector ? selector : '.spinner-wrap';
|
|
const windowInnerHeight = window.innerHeight;
|
|
const bodyHeight = document.body.clientHeight;
|
|
const targetElement = document.querySelector(defaultSelector);
|
|
|
|
if (!targetElement) {
|
|
absoluteFooter.classList.remove('absolute')
|
|
return;
|
|
}
|
|
|
|
const remainingHeight = windowInnerHeight - bodyHeight;
|
|
if (remainingHeight > 0) {
|
|
targetElement.style.height = `${remainingHeight}px`;
|
|
const absoluteFooter = document.querySelector('footer');
|
|
absoluteFooter.classList.add('absolute')
|
|
} else {
|
|
targetElement.style.height = 'auto';
|
|
}
|
|
}
|
|
|
|
|
|
function uiColumnChartRender({ selector, data }) {
|
|
am5.ready(function () {
|
|
const root = am5.Root.new(selector);
|
|
|
|
root.setThemes([
|
|
am5themes_Animated.new(root)
|
|
]);
|
|
|
|
const chart = root.container.children.push(
|
|
am5xy.XYChart.new(root, {
|
|
panX: false,
|
|
panY: false,
|
|
// wheelX: "panX",
|
|
// wheelY: "zoomX",
|
|
// pinchZoomX: true
|
|
})
|
|
);
|
|
|
|
const xAxis = chart.xAxes.push(
|
|
am5xy.CategoryAxis.new(root, {
|
|
categoryField: "x",
|
|
renderer: am5xy.AxisRendererX.new(root, { minGridDistance: 30 }),
|
|
tooltip: am5.Tooltip.new(root, {})
|
|
})
|
|
);
|
|
|
|
const yAxis = chart.yAxes.push(
|
|
am5xy.ValueAxis.new(root, {
|
|
renderer: am5xy.AxisRendererY.new(root, {
|
|
stroke: am5.color('#DCDCDC'),
|
|
strokeWidth: 1
|
|
})
|
|
})
|
|
);
|
|
|
|
const series = chart.series.push(
|
|
am5xy.ColumnSeries.new(root, {
|
|
name: "sector",
|
|
xAxis: xAxis,
|
|
yAxis: yAxis,
|
|
valueYField: "y",
|
|
categoryXField: "x",
|
|
// tooltip: am5.Tooltip.new(root, {
|
|
// labelText: "{valueY}"
|
|
// })
|
|
})
|
|
);
|
|
|
|
|
|
series.columns.template.setAll({
|
|
fill: am5.color("#7292CD"),
|
|
stroke: am5.color("#7292CD"),
|
|
cornerRadiusTL: 4,
|
|
cornerRadiusTR: 4,
|
|
width: 38
|
|
});
|
|
|
|
xAxis.data.setAll(data);
|
|
series.data.setAll(data);
|
|
|
|
series.appear(0);
|
|
chart.appear(0, 0);
|
|
});
|
|
} |