Skip to content

Instantly share code, notes, and snippets.

@madyanalj
Created June 6, 2018 22:07
Show Gist options
  • Save madyanalj/9a0c8301ff233a996d9888bafe02a771 to your computer and use it in GitHub Desktop.
Save madyanalj/9a0c8301ff233a996d9888bafe02a771 to your computer and use it in GitHub Desktop.
Wikipedia Concept
//- Add tags/description to CodePen
#app
ma-header
ma-article
//--- Vue component definitions ---//
script#ma-header(type='text/x-template')
div
header.row.header
.row__col.row__col--sm
form.row.row--no-wrap(v-on:submit.prevent='findArticle')
button.header__icon(@click.prevent='store.commit("toggleMenu")' type='button')
i.fas.fa-bars.fa-lg
input.header__text-input(v-model='search')
button.header__icon(type='submit')
i.fas.fa-search
.row__col.row__col--lg
.row.row--right
button.header__icon(
v-for='icon in icons'
@click.prevent='setMode(icon.mode)'
:class='{ "header__icon--active": mode === icon.mode }'
)
i(:class='`${icon.type} fa-${icon.name} fa-fw`')
.row__col.row__col--md
.row.row--right
button.header__icon
i.far.fa-user
.dropdown(@click.prevent='toggleDropdown')
button.header__icon
i.fas.fa-caret-down
ul.dropdown__body.list(v-show='showDropdown')
li
a(href='#').list__link.list__link #[i.fas.fa-cog.fa-fw] Settings
li
a(href='#').list__link #[i.fas.fa-sign-out-alt.fa-fw] Logout
.alert(v-show='mode === "edit"')
i.fas.fa-bell
| The article content below is editable now!
script#ma-article(type='text/x-template')
div
.loader(v-if='store.state.isLoading' key='loading')
i.fas.fa-sun.fa-7x.fa-spin
.error-page(v-else-if='store.state.isArticleNotFound')
h1 #[i.fas.fa-exclamation-triangle] 404 Article Not Found
p Please try searching for something else.
template(v-else)
article.row.row--main
aside.row__col.row__col--sm(v-show='store.state.showMenu')
ma-logo
ma-toc
section.row__col.row__col--lg.article-content(
v-html='store.state.article.content'
:contenteditable='store.state.isEditingEnabled'
)
aside.row__col.row__col--md(
v-show='store.state.article.infobox'
v-html='store.state.article.infobox'
:contenteditable='store.state.isEditingEnabled'
)
script#ma-logo(type='text/x-template')
.logo
a(href='#')
img.logo__image(src='https://upload.wikimedia.org/wikipedia/commons/b/b3/Wikipedia-logo-v2-en.svg')
script#ma-toc(type='text/x-template')
.list
.list__title Contents
ul
li.list__item(v-for='heading in store.state.article.headings')
a.list__link(:href='"#" + heading.id') {{ heading.title }}
ul
li.list__item(v-for='heading in heading.children')
a.list__link.list__link--secondary(:href='"#" + heading.id') {{ heading.title }}
//--- Helpers ---//
function constructTableOfContents(doc) {
const headings = []
doc.querySelectorAll('h2, h3').forEach(e => {
const heading = {
title: e.innerText,
children: [],
}
heading.id = heading.title.replace(/\s+/g, '_')
if (e.nodeName === 'H2') {
headings.push(heading)
} else {
headings[headings.length - 1].children.push(heading)
}
})
return headings
}
// get article from Wikipedia API and 'clean' it
async function fetchArticle(title) {
const url = `https://en.wikipedia.org/w/api.php?action=parse&prop=text&page=${title}&format=json&disabletoc&disableeditsection&origin=*`
const json = await (await fetch(url)).json()
const articleTitle = json.parse.title
const html = json.parse.text['*']
const doc = new DOMParser().parseFromString(html, 'text/html')
const infobox = doc.getElementsByClassName('infobox')[0]
// strip out unneeded meta html elements
const elementsToRemove = [...doc.querySelectorAll('.navbox, .ambox, .sistersitebox, .mw-empty-elt')]
elementsToRemove.push(infobox)
elementsToRemove.forEach(e => { if (e) e.parentElement.removeChild(e) })
// make infobox responsive
if (infobox) infobox.removeAttribute('style')
return {
headings: constructTableOfContents(doc),
content: `<h1>${articleTitle}</h1>${doc.body.innerHTML}`,
infobox: infobox ? infobox.outerHTML : null,
}
}
// display wiki links inside app
function handleWikiLinks() {
document.querySelectorAll('a[href^="/wiki"]').forEach(link => {
function clickHandler(event) {
event.preventDefault()
let href = event.target.href
if (!href || href.indexOf('/wiki/File:') !== -1) return
href = href.substring(href.indexOf('/wiki/') + 6)
const hashIndex = href.indexOf('#')
if (hashIndex !== -1) href = href.substring(0, hashIndex)
store.commit('setArticle', href)
}
link.addEventListener('click', clickHandler)
})
}
//--- Vue Components ---//
const maLogo = {
template: '#ma-logo',
}
const maToc = {
template: '#ma-toc',
}
const maHeader = {
template: '#ma-header',
data() {
return {
search: '',
mode: 'view',
icons: [
{ mode: 'history', type: 'fas', name: 'history' },
{ mode: 'comments', type: 'far', name: 'comment-alt' },
{ mode: 'edit', type: 'fas', name: 'edit' },
{ mode: 'view', type: 'far', name: 'file' },
],
showDropdown: false,
}
},
methods: {
findArticle() {
store.commit('setArticle', this.search)
this.search = ''
},
setMode(mode) {
this.mode = mode
store.state.isEditingEnabled = mode === 'edit'
},
toggleDropdown() {
this.showDropdown = !this.showDropdown
},
},
}
const maArticle = {
template: '#ma-article',
updated() {
handleWikiLinks()
},
components: {
maLogo,
maToc,
},
}
//--- Vuex Store ---//
const store = new Vuex.Store({
state: {
article: {},
isLoading: false,
isArticleNotFound: false,
showMenu: true,
isEditingEnabled: false,
},
mutations: {
setArticle(state, title) {
state.isLoading = true
state.isArticleNotFound = false
fetchArticle(title)
.then(article => state.article = article)
.catch(() => state.isArticleNotFound = true)
.finally(() => state.isLoading = false)
},
toggleMenu(state) {
state.showMenu = !state.showMenu
},
enableEditing(state) {
state.isEditingEnabled = true
},
disableEditing(state) {
state.isEditingEnabled = false
},
},
})
//--- Vue Instance ---//
const vue = new Vue({
el: '#app',
created() {
// get example Wikipedia article
store.commit('setArticle', 'Martinique')
},
components: {
maHeader,
maArticle,
},
store,
})
<script src="https://use.fontawesome.com/releases/v5.0.13/js/all.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.min.js"></script>
//--- Variables ---//
$c-white: #fff
$c-black: #000
$c-grey-lighter: #eff1f2
$c-grey-light: #eae8e8
$c-grey: #888
$c-blue-light: #f5f8f9
$c-blue: #77a1d4
$c-blue-grey: #7d859d
$c-blue-green: #0682c0
$p-w-md: 3em
$p-w-lg: 7em
$p-xs: .5em 1.5em
$p-sm: .5em 2em
$p-md: 1em $p-w-md
$p-lg: 2em $p-w-lg
$f-serif: 'Lora', 'Georgia', 'Times', serif
$border: $c-grey-light 1px solid
//--- Importing Google Fonts ---//
@import url('https://fonts.googleapis.com/css?family=Lora')
//--- Common Styles ---//
*, *:before, *:after
box-sizing: border-box
::selection
background-color: $c-grey
color: $c-black
body
font-size: 16px
line-height: 1.7
h1, h2, h3
font-family: $f-serif
font-weight: normal
margin-top: 1.2em
h1
font-size: 3em
h2
font-size: 2em
margin-top: 2.4em
&:before
content: ''
margin-top: -1em
border-top: $border
width: 100%
position: absolute
left: 0
a
color: $c-blue-green
text-decoration: none
&:hover
text-decoration: underline
img
max-width: 100%
height: auto
ul
list-style-type: none
padding: 0
margin: 0
table
display: block
overflow-x: auto
border-collapse: collapse
th, td
border: $border
padding: $p-xs
th
background-color: $c-blue-light
//--- Wikipedia Classes ---//
// column on the right
.infobox
display: table
width: 100%
font-size: .8em
border-bottom: $border
th
text-align: left
td
background-color: $c-blue-light
th, td
border-right: none
border-left: none
table
display: table
th, td
border: none
padding: 3px 1em
// parent rows
.mergedtoprow
th, td
border-bottom: none
// children rows
.mergedrow
th, td
border: none
// notes in the begining of sections
.hatnote
padding: $p-sm
display: inline-block
background-color: $c-grey-lighter
color: $c-blue-grey
margin-bottom: 1em
border-radius: 2em
// floated boxes in article
.tleft, .floatleft
float: left
clear: left
margin: 0 1.2em 1.2em
.tright, .floatright
float: right
clear: right
margin: 0 1.2em 1.2em
.tleft
margin-left: -1 * $p-w-lg
.tright
margin-right: -1 * $p-w-lg
// captions of floated boxes
.thumbcaption
font-size: .8em
padding-right: .5em
//--- BEM Components ---//
.row
display: flex
flex-wrap: wrap
&__col
&--sm
flex-grow: 1
&--md
flex-grow: 6
&--lg
flex-grow: 10
&--main
& .row__col
overflow-x: auto
&--sm
flex-basis: 200px
&--md
flex-basis: 250px
&--lg
flex-basis: 500px
&--right
justify-content: flex-end
&--no-wrap
flex-wrap: nowrap
.header
background: linear-gradient(to bottom right, $c-blue, $c-blue-grey)
&__icon, &__text-input
color: $c-white
background-color: transparent
outline: none
border: none
opacity: .6
&__icon
padding: .8em .7em
cursor: pointer
&:hover
opacity: 1
&--active
opacity: 1
padding-bottom: .3em
&:after
content: ''
display: block
width: 0
border-right: .5em solid transparent
border-left: .5em solid transparent
border-bottom: .5em solid $c-white
position: relative
top: .5em
&__text-input
font-size: .8em
border-bottom: $border
margin: 1em 0 1em 1em
padding: 0
width: 100%
&:focus
opacity: 1
.dropdown
&__body
position: absolute
right: 0
margin-top: .2em
.list
&__title, &__link
padding: $p-md
background-color: $c-blue-light
border-bottom: $border
font-weight: bold
&__title
color: $c-grey
text-align: center
&__link
display: block
color: $c-black
font-size: .9em
&:hover
text-decoration: none
background-color: $c-grey-lighter
&--secondary
font-weight: unset
padding: $p-sm
padding-left: 4em
border-bottom-width: 0
&__item:last-child &__link--secondary
border-bottom-width: 1px
.alert
padding: $p-sm
text-align: center
border-bottom: $border
.loader
padding: 5em 1em 1em
text-align: center
color: $c-grey
.error-page
padding: $p-sm
.logo
padding: $p-sm
border-bottom: $border
text-align: center
&__image
width: 100%
max-width: 200px
.article-content
padding: $p-lg
border-right: $border
border-left: $border
position: relative
z-index: 1
transition: box-shadow .2s ease-out
&:hover
box-shadow: 0 0 50px -20px
ul
list-style-type: disc
padding-left: 2.5em
margin: 1em 0
//--- Media Queries ---//
@media screen and (max-width: 1220px)
.tleft
margin-left: -1 * $p-w-md
.tright
margin-right: -1 * $p-w-md
.article-content
padding: $p-md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment