UNPKG

@mongodb-js/mongodb-ui-components

Version:

A collection of frequently used functional UI components found on mongodb properties

369 lines (315 loc) 10.9 kB
'use strict'; const React = require('react'); const Dropdown = require('../dropdown'); const clipboardCopy = require('../shared/clipboard-copy'); const easing = require('../shared/easing'); const style = require('../shared/style'); const script = require('../shared/script'); const widgetStyle = require('./style'); class CodeWidget extends React.PureComponent { constructor(props) { super(props); this.state = { activeTab: 0, activeStage: 0, isClipboardSupported: true, copyBtnText: 'Copy Snippet' }; this.minimaps = []; this.stageTitles = props.descriptions.map(item => item.title); this.onTabSelect = this.onTabSelect.bind(this); this.onNavigateStage = this.onNavigateStage.bind(this); this.onDropdownSelect = this.onDropdownSelect.bind(this); this.onCopyBtnClick = this.onCopyBtnClick.bind(this); this.revertCopiedText = this.revertCopiedText.bind(this); this.renderTabBtn = this.renderTabBtn.bind(this); this.renderTabs = this.renderTabs.bind(this); } componentDidMount() { if (!clipboardCopy.isSupported()) { this.setState({ isClipboardSupported: false }); } const cdn = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0'; style('code-widget', widgetStyle()); script('link', `${cdn}/styles/default.min.css`); script('script', `${cdn}/highlight.min.js`).then(() => Promise.all((this.props.languages || []).map(language => script('script', `${cdn}/languages/${language}.min.js`)))).then(() => { this.minimaps.forEach((minimap, i) => { minimap.innerHTML = this.renderMinimap(minimap.innerHTML, minimap.className, this.stageTitles.length); }); this.setHighlightedCode(0); this.initHLJS(); }).catch(console.error.bind(console)); } initHLJS() { if (!window.hljs || !window.hljs.initHighlighting) { return setTimeout(this.initHLJS.bind(this), 300); } window.hljs.configure({ tabReplace: ' ' }); // 2 spaces window.hljs.initHighlighting(); } alertCopied(success) { this.setState({ copyBtnText: success ? 'Copied!' : 'Copy Unsuccessful' }); setTimeout(this.revertCopiedText, 1000); } revertCopiedText() { this.setState({ copyBtnText: 'Copy Snippet' }); } getFileExtension(lang) { switch (lang) { case 'Python': return 'py'; case 'Java': return 'java'; case 'C++': return 'cpp'; case 'C#': return 'cs'; case 'Node.JS': return 'js'; } } getCurrentSnippet() { return this.props.snippets[this.state.activeTab].stages[this.state.activeStage]; } setHighlightedCode(newStage, shouldAnimate) { this.minimaps.forEach((minimap, i) => { const last = minimap.querySelector('.minimap-section--active'); if (last) { last.classList.remove('minimap-section--active'); } const sections = minimap.querySelectorAll('.minimap-section'); sections[newStage].classList.add('minimap-section--active'); if (shouldAnimate === false) { newStage === 0 ? minimap.scrollTop = 0 : minimap.scrollTop = sections[newStage].offsetTop - 20; return; } return newStage === 0 ? this.scrollToSection(minimap, 0) : this.scrollToSection(minimap, sections[newStage].offsetTop - 20); }); } scrollToSection(minimap, dest) { if (dest === minimap.scrollTop) return; let steps = 0; const start = minimap.scrollTop; const totalSteps = 30; const diff = dest - start; function tick() { steps += 1; minimap.scrollTop = start + diff * easing.easeOutQuart(steps / totalSteps); if (steps >= totalSteps) { minimap.scrollTop = dest; return; } window.requestAnimationFrame(tick); } window.requestAnimationFrame(tick); } onTabSelect(e) { const tab = Number(e.currentTarget.getAttribute('data-i')); if (tab === this.state.activeTab) return; this.setState({ activeTab: tab }, () => { this.setHighlightedCode(this.state.activeStage, false); }); } onDropdownSelect(newStage) { this.setState({ activeStage: newStage }); this.setHighlightedCode(newStage); } onNavigateStage(e) { const direction = Number(e.currentTarget.getAttribute('data-direction')); const newStage = this.state.activeStage + direction; if (newStage < 0 || newStage === this.props.snippets.length) return; this.setState({ activeStage: newStage }); this.setHighlightedCode(newStage); } onCopyBtnClick(e) { if (clipboardCopy(this.getCurrentSnippet())) { this.alertCopied(true); } else { this.alertCopied(false); } const cl = e.currentTarget.classList; cl.add('animation--bounce-inwards'); setTimeout(() => cl.remove('animation--bounce-inwards'), 600); } render() { return React.createElement( 'div', { className: 'code-widget' }, React.createElement( 'div', { className: 'flex flex--wrap' }, this.props.snippets.map(this.renderTabBtn) ), React.createElement( 'div', { className: 'code-widget__shadow' }, this.props.snippets.map(this.renderTabs) ) ); } renderTabBtn(snippet, i) { return React.createElement( 'button', { key: i, className: `code-widget__tab ${i === this.state.activeTab ? 'code-widget__tab--active' : ''}`, onClick: this.onTabSelect, 'data-i': i }, snippet.language ); } renderTabs(snippet, i) { const stage = this.state.activeStage; const tab = this.state.activeTab; const downloadUrl = snippet.sourceUrl || `/assets/files/what-is-mongodb.${this.getFileExtension(snippet.language)}`; return React.createElement( 'div', { key: i, style: tab === i ? null : { display: 'none' }, className: 'flex flex--wrap' }, React.createElement( 'div', { className: 'code-widget__column code-widget__column-1' }, React.createElement( 'div', { style: { margin: '20px 20px 0' } }, React.createElement( 'p', null, React.createElement( 'strong', null, 'Choose your step' ) ), React.createElement(Dropdown, { onSelect: this.onDropdownSelect, activeIndex: stage, options: this.stageTitles }) ), React.createElement( 'div', { className: 'code-widget__navigation' }, React.createElement( 'button', { className: `code-widget__navigation-btn ${stage === 0 ? '' : 'code-widget__navigation-btn--active'}`, onClick: this.onNavigateStage, 'data-direction': '-1' }, 'Back' ), React.createElement( 'button', { className: `code-widget__navigation-btn ${stage === this.props.snippets.length - 1 ? '' : 'code-widget__navigation-btn--active'}`, onClick: this.onNavigateStage, 'data-direction': '1' }, 'Next' ) ), React.createElement( 'div', { className: 'code-widget__progress' }, React.createElement('div', { className: `code-widget__progress-bar code-widget__progress-bar--stage-${stage}` }) ), React.createElement('p', { className: 'code-widget__description', dangerouslySetInnerHTML: { __html: this.props.descriptions[this.state.activeStage].content } }), React.createElement( 'div', { className: 'code-widget__column-1-options' }, this.state.isClipboardSupported && React.createElement( 'button', { onClick: this.onCopyBtnClick, className: 'clipboard-btn btn-hollow btn-hollow--gray' }, this.state.copyBtnText ), React.createElement('div', { className: 'divider--horizontal' }), React.createElement( 'a', { href: snippet.documentation[stage] }, React.createElement( 'p', null, React.createElement( 'strong', null, 'Go to Documentation' ), React.createElement( 'svg', { width: '20', height: '10', viewBox: '0 0 20 10', style: { marginLeft: '7px' } }, React.createElement('path', { fill: '#589636', fillRule: 'evenodd', d: 'M14.707.586L13.293 2l2.293 2.293H0v2h15.586l-2.293 2.293L14.707 10l4.707-4.707z' }) ) ) ) ) ), React.createElement( 'div', { className: 'code-widget__column code-widget__column-2' }, snippet.stages.map((code, j) => React.createElement( 'pre', { key: `${i}.${j}`, style: { display: j === stage ? 'inherit' : 'none' } }, React.createElement( 'code', { className: snippet.language.toLowerCase() }, code.trim() ) )) ), React.createElement( 'div', { className: 'code-widget__column code-widget__column-3' }, React.createElement( 'pre', null, React.createElement( 'code', { ref: el => { if (el) this.minimaps.push(el); }, className: snippet.language.toLowerCase() }, snippet.sourceCode ) ), React.createElement( 'a', { href: downloadUrl, download: downloadUrl }, React.createElement( 'button', { className: 'download-file-btn btn-hollow btn-hollow--gray' }, 'Download File' ) ) ) ); } renderMinimap(code, language, numStages) { const commentToken = language === 'python' ? '#' : '//'; let result = code; for (let i = 1, l = numStages + 2; i < l; ++i) { const startIndex = result.indexOf(`${commentToken} ${i}.`); if (i < l - 1) { result = this.insert(result, `<span class='minimap-section' id='minimap-${i}'>`, startIndex); } if (i < 3) continue; const endIndex = result.indexOf(`<span class='minimap-section' id='minimap-${i - 1}'>`) - commentToken.length; result = this.insert(result, '</span>', endIndex); if (i === l - 1) result += '</span>'; } return result; } insert(source, text, index) { return `${source.slice(0, index)}${text}${source.slice(index)}`; } } module.exports = CodeWidget;