diff --git a/packages/viewer/src/header.ts b/packages/viewer/src/header.ts index 546c0dd..3fcc720 100644 --- a/packages/viewer/src/header.ts +++ b/packages/viewer/src/header.ts @@ -27,18 +27,28 @@ class Header { private modal: HTMLElement | null = null + private errorModal: HTMLElement | null = null + private pageNumber: HTMLElement | null = null private infoButton: HTMLElement | null = null private printButton: HTMLElement | null = null - constructor(view: HTMLElement, content: HTMLElement, pages: HTMLElement[]) { + private copyErrorButton: HTMLElement | null = null + + private error: Error | null = null + + constructor(view: HTMLElement, content: HTMLElement, pages: HTMLElement[], error: Error | null) { this.content = content this.pages = pages + this.error = error + + this.copyErrorMessage = this.copyErrorMessage.bind(this) this.container = this.drawContainer(view) - this.modal = this.drawModal(view) + this.modal = this.drawInfoModal(view) + this.errorModal = this.drawErrorModal(view) this.observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { @@ -78,6 +88,8 @@ class Header { styleSheet.innerText = buttonStyle document.head.appendChild(styleSheet) } + + if (this.error) this.errorModal.style.display = 'flex' } updatePageNumber(pageNumber: number) { @@ -97,9 +109,11 @@ class Header { this.infoButton?.removeEventListener('click', this.handleInfoButtionClick) this.infoButton = null - this.printButton?.removeEventListener('click', this.handleInfoButtionClick) + if (!this.error) this.printButton?.removeEventListener('click', this.handleInfoButtionClick) this.printButton = null + this.copyErrorButton?.removeEventListener('click', this.copyErrorMessage) + this.pageNumber = null } @@ -129,7 +143,7 @@ class Header { return content } - drawModal(view: HTMLElement) { + drawInfoModal(view: HTMLElement) { const modal = document.createElement('div') modal.style.position = 'absolute' modal.style.zIndex = '2' @@ -175,6 +189,84 @@ class Header { return modal } + copyErrorMessage() { + navigator.clipboard.writeText(this.error?.stack || '') + .then(() => { + if (this.copyErrorButton) this.copyErrorButton.textContent = '복사 성공!' + }) + } + + drawErrorModal(view: HTMLElement) { + const modal = document.createElement('div') + modal.style.position = 'absolute' + modal.style.zIndex = '2' + modal.style.top = '0' + modal.style.right = '0' + modal.style.bottom = '0' + modal.style.left = '0' + modal.style.background = 'rgba(0, 0, 0, 0.7)' + modal.style.display = 'none' + modal.style.justifyContent = 'center' + modal.style.alignItems = 'center' + + const content = document.createElement('div') + content.style.background = '#FFF' + content.style.borderRadius = '5px' + content.style.padding = '0 24px' + content.style.cursor = 'initial' + content.style.boxShadow = '0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)' + + const title = document.createElement('h1') + title.textContent = '오류가 발생했어요.' + + const report = document.createElement('div') + const reportText1 = document.createElement('span') + reportText1.textContent = '오류를 파일, 오류메시지와 함께 ' + report.appendChild(reportText1) + + const reportLink = document.createElement('a') + reportLink.textContent = '제보' + reportLink.href = `https://github.com/hahnlee/hwp.js/issues/new?title=Error: ${this.error?.message}` + reportLink.target = '_blink' + reportLink.rel = 'noopener noreferrer' + reportLink.style.textDecoration = 'underline' + report.appendChild(reportLink) + + const reportText2 = document.createElement('span') + reportText2.textContent = '해 주시면 문제 해결에 큰 도움이 됩니다.' + report.appendChild(reportText2) + + const reportNL = document.createElement('br') + + const description = document.createElement('textarea') + description.textContent = this.error?.stack || '' + description.style.width = '100%' + description.style.height = '100px' + description.style.resize = 'none' + description.style.outline = 'none' + description.readOnly = true + + const copy = document.createElement('button') + copy.textContent = '복사' + this.copyErrorButton = copy + + const copyright = document.createElement('p') + copyright.textContent = 'Copyright 2020 Han Lee and other contributors.' + + content.appendChild(title) + content.appendChild(report) + content.appendChild(reportNL) + content.appendChild(description) + content.appendChild(copy) + content.appendChild(copyright) + modal.appendChild(content) + view.appendChild(modal) + + copy.addEventListener('click', this.copyErrorMessage) + + return modal + } + handleModalClick = (event: MouseEvent) => { if (event.currentTarget !== event.target) return if (this.modal) { @@ -194,7 +286,7 @@ class Header { drawPageNumber() { this.pageNumber = document.createElement('span') - this.pageNumber.textContent = '1' + this.pageNumber.textContent = this.pages.length ? '1' : '0' const totalPages = document.createElement('span') totalPages.textContent = `/${this.pages.length}쪽` @@ -226,7 +318,7 @@ class Header { buttion.classList.add('hwpjs-header-control') buttion.innerHTML = '' buttion.style.marginLeft = 'auto' - buttion.addEventListener('click', this.handlePrintButtionClick) + if (!this.error) buttion.addEventListener('click', this.handlePrintButtionClick) this.container.appendChild(buttion) diff --git a/packages/viewer/src/viewer.ts b/packages/viewer/src/viewer.ts index 3ee97f1..af041fd 100644 --- a/packages/viewer/src/viewer.ts +++ b/packages/viewer/src/viewer.ts @@ -80,10 +80,20 @@ class HWPViewer { private header: Header | null = null + private error: Error | null = null + constructor(container: HTMLElement, data: Uint8Array, option: CFB$ParsingOptions = { type: 'binary' }) { this.container = container - this.hwpDocument = parsePage(parse(data, option)) - this.draw() + try { + this.hwpDocument = parsePage(parse(data, option)) + } + catch (e) { + this.error = e + throw new Error(e) + } + finally { + this.draw() + } } distory() { @@ -370,11 +380,11 @@ class HWPViewer { content.style.position = 'relative' content.style.zIndex = '0' - this.hwpDocument.sections.forEach((section, index) => { + if (!this.error) this.hwpDocument.sections.forEach((section, index) => { this.drawSection(content, section, index) }) - this.header = new Header(this.viewer, this.container, this.pages) + this.header = new Header(this.viewer, this.container, this.pages, this.error) this.viewer.appendChild(content) this.container.appendChild(this.viewer)