Skip to content

Commit

Permalink
feat(icons): optional icon components (#4489)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmorehouse committed Dec 22, 2019
1 parent 6bd8100 commit d2bef17
Show file tree
Hide file tree
Showing 46 changed files with 17,364 additions and 119 deletions.
34 changes: 26 additions & 8 deletions docs/components/componentdoc.vue
Expand Up @@ -2,7 +2,7 @@
<section v-if="component" class="bd-content">
<b-row tag="header" align-v="center">
<b-col sm="9">
<anchored-heading :id="`comp-ref-${componentName}`" level="3">
<anchored-heading :id="`comp-ref-${componentNameClean}`" level="3">
<code class="notranslate bigger" translate="no">{{ tag }}</code>
</anchored-heading>
<b-badge v-if="version" variant="success">v{{ version }}+</b-badge>
Expand All @@ -16,7 +16,13 @@
</b-badge>
</b-col>
<b-col sm="3" class="text-sm-right">
<b-btn variant="outline-secondary" size="sm" :href="githubURL" target="_blank">
<b-btn
v-if="githubURL"
variant="outline-secondary"
size="sm"
:href="githubURL"
target="_blank"
>
View source
</b-btn>
</b-col>
Expand Down Expand Up @@ -311,18 +317,23 @@ ul.component-ref-mini-toc:empty {

<script>
import Vue from 'vue'
import kebabCase from 'lodash/kebabCase'
import AnchoredHeading from './anchored-heading'
// Fallback descriptions for common props (mainly router-link props)
import commonProps from '../common-props.json'
import { kebabCase } from '../utils'
import AnchoredHeading from './anchored-heading'
export default {
name: 'BDVComponentdoc',
components: { AnchoredHeading },
props: {
component: {},
srcComponent: {
// This prop is used only when the above `component` is a
// "fake" component. This prop specifies a "real" component
// to use when grabbing the component definition options
},
propsMeta: {
// For getting pro descriptions
// For getting prop descriptions
type: Array,
default: () => []
},
Expand All @@ -349,7 +360,7 @@ export default {
},
computed: {
componentOptions() {
const component = Vue.options.components[this.component]
const component = Vue.options.components[this.srcComponent || this.component]
if (!component) {
return {}
}
Expand Down Expand Up @@ -492,15 +503,22 @@ export default {
return this.slots ? this.slots.map(s => ({ ...s })) : []
},
componentName() {
return kebabCase(this.component)
return kebabCase(this.component).replace('{', '-{')
},
componentNameClean() {
return this.componentName.replace('{', '').replace('}', '')
},
tag() {
return `<${this.componentName}>`
},
githubURL() {
const name = this.componentName.replace(/^b-/, '')
if (name.indexOf('{') !== -1) {
// Example component (most likely an auto generated component)
return ''
}
const base = 'https://github.com/bootstrap-vue/bootstrap-vue/tree/dev/src/components'
const slug = this.$route.params.slug
const name = kebabCase(this.component).replace(/^b-/, '')
// Always point to the .js file (which may import a .vue file)
return `${base}/${slug}/${name}.js`
}
Expand Down
20 changes: 11 additions & 9 deletions docs/components/feedback.js
Expand Up @@ -9,7 +9,7 @@ export default {
show() {
const name = this.$route.name
const slug = this.$route.params.slug
return slug || name === 'docs'
return slug || name === 'docs' || name === 'docs-icons'
},
reportIssueUrl() {
// Add appreciate query params for proper issue title
Expand All @@ -18,22 +18,24 @@ export default {
editPageUrl() {
const name = this.$route.name
const slug = this.$route.params.slug
let path = '/'
let path = ''
if (name === 'docs') {
path = `/docs/markdown/intro/README.md`
path = `docs/markdown/intro/README.md`
} else if (name === 'docs-components-slug') {
path = `/src/components/${slug}/README.md`
path = `src/components/${slug}/README.md`
} else if (name === 'docs-icons') {
path = `src/icons/README.md`
} else if (name === 'docs-directives-slug') {
path = `/src/directives/${slug}/README.md`
path = `src/directives/${slug}/README.md`
} else if (name === 'docs-reference-slug') {
path = `/docs/markdown/reference/${slug}/README.md`
path = `docs/markdown/reference/${slug}/README.md`
} else if (name === 'docs-misc-slug') {
if (slug === 'changelog') {
path = '/CHANGELOG.md'
path = 'CHANGELOG.md'
} else if (slug === 'contributing') {
path = '/CONTRIBUTING.md'
path = 'CONTRIBUTING.md'
} else if (slug === 'settings') {
path = '/docs/markdown/misc/settings/README.md'
path = 'docs/markdown/misc/settings/README.md'
}
}
return `${this.baseUrl}/tree/dev/${path}`
Expand Down
1 change: 1 addition & 0 deletions docs/components/footer.vue
Expand Up @@ -19,6 +19,7 @@
<li><b-link to="/docs" exact>Getting started</b-link></li>
<li><b-link to="/docs/components" exact>Components</b-link></li>
<li><b-link to="/docs/directives" exact>Directives</b-link></li>
<li><b-link to="/docs/icons" exact>Icons</b-link></li>
<li><b-link to="/docs/reference" exact>Reference</b-link></li>
<li><b-link to="/docs/misc" exact>Miscellaneous</b-link></li>
<li><b-link to="/play" exact>Playground</b-link></li>
Expand Down
1 change: 1 addition & 0 deletions docs/components/header.vue
Expand Up @@ -33,6 +33,7 @@
<b-nav-item to="/docs" active-class="active" exact>Docs</b-nav-item>
<b-nav-item to="/docs/components" active-class="active">Components</b-nav-item>
<b-nav-item to="/docs/directives" active-class="active">Directives</b-nav-item>
<b-nav-item to="/docs/icons" active-class="active">Icons</b-nav-item>
<b-nav-item to="/docs/reference" active-class="active">Reference</b-nav-item>
<b-nav-item to="/docs/misc" active-class="active">Misc</b-nav-item>
<b-nav-item to="/play" active-class="active">Play</b-nav-item>
Expand Down
162 changes: 162 additions & 0 deletions docs/components/icons-table.vue
@@ -0,0 +1,162 @@
<template>
<div
key="_bv-icons-table_"
class="bv-icons-table notranslate"
role="group"
aria-labeledby="bv-icons-table-title"
>
<b-row align-v="start">
<b-col md="5" lg="6">
<div id="bv-icons-table-title" class="h3 text-muted mb-3 mb-md-0">
Icon explorer
</div>
</b-col>
<b-col md="7" lg="6">
<b-form @submit.prevent>
<b-form-group
label="Search icons"
label-for="bv-icons-table-search"
label-cols-sm="auto"
label-align-sm="right"
:description="`Showing ${filteredIcons.length} of ${totalIcons} icons`"
>
<b-input-group>
<b-input-group-prepend is-text>
<b-icon icon="search"></b-icon>
</b-input-group-prepend>
<b-form-input
id="bv-icons-table-search"
key="_bv-icons-table-search_"
v-model="iconFilter"
type="search"
debounce="250"
aria-controls="bv-icons-table-result"
></b-form-input>
</b-input-group>
</b-form-group>
</b-form>
</b-col>
</b-row>
<div id="bv-icons-table-result">
<transition-group
tag="ul"
name="flip-icon-list"
class="row row-cols-3 row-cols-sm-4 row-cols-lg-6 list-unstyled mb-n3 position-relative"
>
<b-col
v-for="icon in filteredIcons"
:key="`_icon_${icon.name}`"
tag="li"
class="flip-icon-list-icon d-inline-flex flex-column mb-3 text-center"
>
<div class="card bg-light p-3" :title="icon.name">
<b-icon :icon="icon.name" class="mx-auto"></b-icon>
</div>
<b-form-text class="mt-1 text-break" :title="icon.name">{{ icon.name }}</b-form-text>
</b-col>
</transition-group>
<div aria-live="polite" aria-atomic="true">
<b-alert
:show="filteredIcons.length === 0"
:role="null"
:aria-live="null"
:aria-atomic="null"
fade
variant="light"
class="text-center mt-4 d-flex align-items-center justify-content-center"
>
<b-icon icon="alert-triangle-fill" aria-hidden="true"></b-icon>
<span>No matching icons found. Try searching again.</span>
</b-alert>
</div>
</div>
</div>
</template>

<style lang="scss" scoped>
.bv-icons-table {
position: relative;
}
#bv-icons-table-result /deep/ .bi {
font-size: 2rem;
}
.form-group /deep/ .form-text {
text-align: right;
}
// Icon zoom on hover
.flip-icon-list-icon /deep/ .card {
.bi {
transition: transform 0.15s;
}
&:hover .bi {
transform: scale(2);
}
}
// Transion group classes
.flip-icon-list-icon {
transition: all 0.15s;
}
.flip-icon-list-move {
transition: transform 0.3s;
transition-delay: 0.15s;
}
.flip-icon-list-enter,
.flip-icon-list-leave-to {
opacity: 0;
transform: scale(0.75);
}
.flip-icon-list-enter-active {
transition-delay: 0.3s;
}
.flip-icon-list-leave-active {
position: absolute;
}
</style>

<script>
import { iconNames } from '~/../src/icons'
const icons = iconNames
.filter(name => name !== 'BIcon')
.sort()
.map(fullName => {
return {
component: fullName,
name: fullName
.replace(/^BIcon/, '')
.replace(/\B([A-Z])/g, '-$1')
.toLowerCase()
}
})
export default {
name: 'BVDIconsTable',
data() {
return {
iconFilter: '',
totalIcons: icons.length
}
},
computed: {
filteredIcons() {
const terms = this.iconFilter
.trim()
.toLowerCase()
.split(/\s+/)
if (terms.length === 0) {
return icons.slice()
}
return icons.filter(icon => terms.every(term => icon.name.indexOf(term) !== -1))
}
}
}
</script>
11 changes: 6 additions & 5 deletions docs/components/importdoc.vue
Expand Up @@ -118,9 +118,9 @@
</template>

<script>
import hljs from '../utils/hljs'
import kebabCase from 'lodash/kebabCase'
import startCase from 'lodash/startCase'
import hljs from '../utils/hljs'
import { kebabCase } from '../utils'
import AnchoredHeading from './anchored-heading'
const importPath = 'bootstrap-vue'
Expand All @@ -136,10 +136,11 @@ export default {
return 'bootstrap-vue'
},
isComponentRoute() {
return this.$route.name === 'docs-components-slug'
const name = this.$route.name
return name === 'docs-components-slug' || name === 'docs-icons'
},
pluginDir() {
return this.$route.params.slug
return this.$route.params.slug || this.meta.slug
},
pluginName() {
// Directive plugin names are prefixed with `VB`
Expand Down Expand Up @@ -218,7 +219,7 @@ export default {
},
methods: {
componentName(component) {
return kebabCase(component)
return kebabCase(component).replace('{', '-{')
},
componentTag(component) {
return `<${this.componentName(component)}>`
Expand Down
22 changes: 21 additions & 1 deletion docs/content/index.js
@@ -1,4 +1,4 @@
import { importAll, parseVersion } from '~/utils'
import { importAll, parseVersion, parseFullVersion } from '~/utils'
import { version, dependencies, devDependencies, description } from '~/../package.json'
import DEFAULT_CONFIG from '~/../src/utils/config-defaults'

Expand All @@ -8,6 +8,18 @@ export const components = importAll(componentsContext)
const directivesContext = require.context('~/../src/directives/', true, /package.json/)
export const directives = importAll(directivesContext)

const iconsContext = require.context('~/../src/icons', false, /package.json/)
const icons = importAll(iconsContext) || {}
// Since there are over 300 icons, we only return the first BIcon component, plus one
// extra example icon component which we modify the icon name to be `BIcon{IconName}`
// We sort the array to ensure `BIcon` appears first
icons[''].components = icons[''].components
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
.slice(0, 2)
.map(c => ({ ...c }))
icons[''].components[1].component = 'BIcon{IconName}'
export { icons }

const referenceContext = require.context('~/markdown/reference', true, /meta.json/)
export const reference = importAll(referenceContext)

Expand All @@ -32,6 +44,13 @@ export const nav = [
pages: directives,
description: 'BootstrapVue directives and directive group plugins'
},
{
title: 'Icons',
base: 'icons',
new: true,
version: '2.3.0',
description: 'BootstrapVue icons'
},
{
title: 'Reference',
base: 'reference/',
Expand All @@ -50,6 +69,7 @@ export const bootstrapVersion = parseVersion(dependencies.bootstrap)
export const nuxtVersion = parseVersion(devDependencies.nuxt)
export const portalVueVersion = parseVersion(dependencies['portal-vue'])
export const vueVersion = parseVersion(devDependencies.vue)
export const bootstrapIconsVersion = parseFullVersion(devDependencies['bootstrap-icons'])
export const defaultConfig = DEFAULT_CONFIG
export const bvDescription = description

Expand Down

0 comments on commit d2bef17

Please sign in to comment.