diff --git a/src/Documentation/Markdown/Markdown.js b/src/Documentation/Markdown/Markdown.js
index 815025b887..12160744f2 100644
--- a/src/Documentation/Markdown/Markdown.js
+++ b/src/Documentation/Markdown/Markdown.js
@@ -2,6 +2,7 @@ import React, { Component } from 'react'
// components
import ReactMarkdown from 'react-markdown'
import { LightButton } from '../LightButton'
+import Tooltip from '../../Tooltip'
// syntax highlighter
import SyntaxHighlighter, {
registerLanguage
@@ -48,15 +49,18 @@ const HeadingRenderer = ({ level, children }) => {
}
const HtmlRenderer = props => {
- if (props.tag !== 'details') {
+ if (props.tag !== 'details' && props.tag !== 'abbr') {
return React.createElement(props.tag, {}, props.children)
- } else {
+ } else if (props.tag === 'details') {
const text = props.children[0].props.children[0]
return (
{props.children.slice(1)}
)
+ } else if (props.tag === 'abbr') {
+ const text = props.children[0]
+ return
}
}
diff --git a/src/Documentation/glossary.js b/src/Documentation/glossary.js
new file mode 100644
index 0000000000..c8002f3119
--- /dev/null
+++ b/src/Documentation/glossary.js
@@ -0,0 +1,34 @@
+export default {
+ name: 'Glossary',
+ desc:
+ 'This guide is aimed to familiarize the users with definitions to ' +
+ 'relevant DVC concepts and terminologies which are frequently used.',
+ contents: [
+ {
+ name: 'Workspace directory',
+ match: ['workspace'],
+ desc:
+ 'The **workspace** contains all of your DVC **project** files and ' +
+ "directories. It's typically also a Git **repository**. See also " +
+ '[`dvc init`](/doc/commands-reference/init).'
+ },
+ {
+ name: 'DVC cache',
+ match: ['cache'],
+ desc:
+ 'DVC cache is a hidden storage which is by default found at ' +
+ '`.dvc/cache`. This storage is used to manage different versions of ' +
+ 'files which are under DVC control. For more information on cache, ' +
+ 'please refer to this [guide](/doc/commands-reference/config#cache).'
+ },
+ {
+ name: 'Data artifact',
+ match: ['data artifact', 'data artifacts'],
+ desc:
+ 'Any **data** file or directory, as well as intermediate or final ' +
+ 'result (such as extracted features or a ML model file) that is ' +
+ 'under DVC control. Refer to [Data and Model Files Versioning]' +
+ '(/doc/use-cases/data-and-model-files-versioning) for more details.'
+ }
+ ]
+}
diff --git a/src/Tooltip/index.js b/src/Tooltip/index.js
new file mode 100644
index 0000000000..0de90694c3
--- /dev/null
+++ b/src/Tooltip/index.js
@@ -0,0 +1,244 @@
+import React, { Component } from 'react'
+import ReactMarkdown from 'react-markdown'
+import styled from 'styled-components'
+
+import glossary from '../Documentation/glossary'
+import { OnlyDesktop, OnlyMobile } from '../styles'
+
+class Tooltip extends Component {
+ state = {
+ description: '',
+ header: '',
+ hover: false,
+ id: null,
+ margin: -70,
+ match: false,
+ pointBorderAfter: 'white transparent transparent transparent',
+ pointBorderBefore: '#d1d5da transparent transparent transparent',
+ pointMargin: -15,
+ pointTop: 100,
+ pointTopAfter: -14,
+ pointTopBefore: 16,
+ timeout: null,
+ top: 'unset',
+ width: 400
+ }
+
+ componentDidMount() {
+ glossary.contents.forEach((glossaryItem, index) => {
+ if (glossaryItem.match.includes(this.props.text)) {
+ this.setState({
+ description: glossaryItem.desc,
+ header: glossaryItem.name,
+ key: index,
+ match: true
+ })
+ }
+ })
+ }
+
+ tooltipPositionEval = () => {
+ const headerHeight = document.getElementsByClassName('header')[0]
+ .offsetHeight
+ const markdownBody = document.getElementsByClassName('markdown-body')[0]
+ const tooltipBoundary = document
+ .getElementById(`tooltip-text-${this.state.key}`)
+ .getBoundingClientRect()
+ const tooltipBoxHeight = document.getElementById(
+ `tooltip-box-${this.state.key}`
+ ).offsetHeight
+ const tooltipHeight = tooltipBoundary.top - tooltipBoxHeight
+ const maxWidth = markdownBody.offsetLeft + markdownBody.clientWidth
+ const container = document.getElementsByClassName('tooltip-container')[0]
+ const tooltipWidth = container.offsetLeft + this.state.width
+ const vertical = tooltipHeight > headerHeight ? 'top' : 'bottom'
+ const horizontal = tooltipWidth > maxWidth ? 'right' : 'left'
+
+ switch (`${horizontal} ${vertical}`) {
+ case 'left top':
+ this.setState({
+ margin: -70,
+ pointBorderAfter: 'white transparent transparent transparent',
+ pointBorderBefore: '#d1d5da transparent transparent transparent',
+ pointMargin: -15,
+ pointTop: 100,
+ pointTopAfter: 'unset',
+ pointTopBefore: 'unset',
+ top: -tooltipBoxHeight
+ })
+ break
+ case 'right top':
+ this.setState({
+ margin: -340,
+ pointBorderAfter: 'white transparent transparent transparent',
+ pointBorderBefore: '#d1d5da transparent transparent transparent',
+ pointMargin: 260,
+ pointTop: 100,
+ pointTopAfter: 'unset',
+ pointTopBefore: 'unset',
+ top: -tooltipBoxHeight
+ })
+ break
+ case 'left bottom':
+ this.setState({
+ margin: -70,
+ pointBorderAfter: 'transparent transparent white transparent',
+ pointBorderBefore: 'transparent transparent #d1d5da transparent',
+ pointMargin: -15,
+ pointTop: -15,
+ pointTopAfter: -20,
+ pointTopBefore: -23,
+ top: 40
+ })
+ break
+ case 'right bottom':
+ this.setState({
+ margin: -340,
+ pointBorderAfter: 'transparent transparent white transparent',
+ pointBorderBefore: 'transparent transparent #d1d5da transparent',
+ pointMargin: 260,
+ pointTop: -15,
+ pointTopAfter: -20,
+ pointTopBefore: -23,
+ top: 40
+ })
+ break
+ }
+ }
+
+ hoverIn = () => {
+ if (this.state.interval) {
+ clearTimeout(this.state.interval)
+ this.setState(
+ {
+ interval: null,
+ hover: true
+ },
+ this.tooltipPositionEval
+ )
+ } else {
+ this.setState(
+ {
+ hover: true
+ },
+ this.tooltipPositionEval
+ )
+ }
+ }
+
+ hoverOut = () => {
+ this.setState({
+ interval: setTimeout(() => {
+ this.setState({
+ hover: false
+ })
+ }, 100)
+ })
+ }
+
+ render() {
+ if (this.state.match) {
+ return (
+ <>
+
+
+
+ {this.props.text}
+
+
+ {this.state.hover && (
+
+
+ {this.state.header}
+
+
+
+ )}
+
+
+ {this.props.text}
+
+ >
+ )
+ } else {
+ return {this.props.text}
+ }
+ }
+}
+
+const HighlightedText = styled.span`
+ border-bottom: 1px black dotted;
+`
+
+const TooltipContainer = styled.div`
+ position: absolute;
+ display: inline-block;
+ z-index: 300000000;
+ background-color: white;
+`
+
+const TooltipText = styled.div`
+ padding: 8px 10px;
+ border: 1px solid #d1d5da;
+ border-radius: 3px;
+ background-color: white;
+ position: absolute;
+ z-index: 1;
+ top: ${props => {
+ if (props.top === 'unset') {
+ return 'unset'
+ } else {
+ return `${props.top}px`
+ }
+ }};
+ margin-left: ${props => props.margin || -70}px;
+ width: ${props => props.width || 400}px;
+
+ &:after,
+ &:before {
+ content: '';
+ position: absolute;
+ top: ${props => props.pointTop}%;
+ border-style: solid;
+ margin-left: ${props => props.pointMargin || -15}px;
+ }
+
+ &:after {
+ top: ${props => props.pointTopAfter}px;
+ left: 10%;
+ border-width: 10px;
+ border-color: ${props => props.pointBorderAfter};
+ }
+ &:before {
+ top: ${props => props.pointTopBefore}px;
+ left: 10%;
+ border-width: 11px;
+ border-color: ${props => props.pointBorderBefore};
+ }
+
+ .header {
+ font-size: 1.3em;
+ font-weight: bold;
+ }
+`
+
+export default Tooltip
diff --git a/src/TopMenu/index.js b/src/TopMenu/index.js
index 07e15b3540..a01a63cd88 100644
--- a/src/TopMenu/index.js
+++ b/src/TopMenu/index.js
@@ -48,7 +48,11 @@ class TopMenu extends Component {
return (
-
+
cache directory |
+| `Filesystem type` | Shows the filesystem type (eg. ext4, FAT, etc.) and mount point of workspace and the cache directory |
> If `dvc version` is executed outside a DVC workspace, the command outputs the
> filesystem type of the current working directory.