Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
14d078a
feat(icons): add stacking support
tmorehouse Jan 21, 2020
a31a510
Update make-icon.js
tmorehouse Jan 21, 2020
2223f51
lint
tmorehouse Jan 21, 2020
7303e08
Create iconstack.js
tmorehouse Jan 21, 2020
d3f3b6d
Update index.js
tmorehouse Jan 21, 2020
9a4e68d
Update iconstack.js
tmorehouse Jan 21, 2020
c24f55b
Update index.d.ts
tmorehouse Jan 21, 2020
5b38350
Update index.d.ts
tmorehouse Jan 21, 2020
766919e
Update make-icon.js
tmorehouse Jan 21, 2020
684c6db
Update create-icons.js
tmorehouse Jan 21, 2020
bc5f6bc
Update package.json
tmorehouse Jan 21, 2020
67274ec
Create iconstack.spec.js
tmorehouse Jan 21, 2020
43bb6a6
lint
tmorehouse Jan 21, 2020
ab4303b
Update index.js
tmorehouse Jan 21, 2020
d17a6f5
Update iconstack.spec.js
tmorehouse Jan 21, 2020
3faecc2
Update iconstack.spec.js
tmorehouse Jan 21, 2020
ca546e7
Update plugin.js
tmorehouse Jan 21, 2020
4aac075
Update package.json
tmorehouse Jan 21, 2020
bf43c61
Update icon.js
tmorehouse Jan 21, 2020
d4da63e
Update index.js
tmorehouse Jan 21, 2020
f995821
Update index.js
tmorehouse Jan 21, 2020
b069f5b
Merge branch 'dev' into icons-stacked
tmorehouse Jan 21, 2020
1384349
Update README.md
tmorehouse Jan 21, 2020
841803b
Merge branch 'dev' into icons-stacked
tmorehouse Jan 21, 2020
66dc433
update auto generated files
tmorehouse Jan 21, 2020
478598e
Update package.json
tmorehouse Jan 21, 2020
1340ac5
Update make-icon.js
tmorehouse Jan 22, 2020
8e0b384
Update README.md
tmorehouse Jan 22, 2020
7126541
Update README.md
tmorehouse Jan 22, 2020
29e1b37
Update README.md
tmorehouse Jan 22, 2020
c7b9f44
Update README.md
tmorehouse Jan 22, 2020
e9370ae
Update iconstack.js
tmorehouse Jan 22, 2020
144d5d4
Update iconstack.js
tmorehouse Jan 22, 2020
525e6b6
Update iconstack.spec.js
tmorehouse Jan 22, 2020
80d572f
Update README.md
tmorehouse Jan 22, 2020
cbba25f
Update README.md
tmorehouse Jan 22, 2020
898dfa8
Merge branch 'dev' into icons-stacked
tmorehouse Jan 22, 2020
2136770
Merge remote-tracking branch 'origin/dev' into pr/4637
jacobmllr95 Jan 22, 2020
3fa43cb
Update index.js
jacobmllr95 Jan 22, 2020
56fd89e
Update README.md
jacobmllr95 Jan 22, 2020
c2d15dc
Update make-icon.js
jacobmllr95 Jan 22, 2020
ee41309
Update index.js
jacobmllr95 Jan 22, 2020
4f3ff38
Minor tweaks to auto-generated icon file comments
jacobmllr95 Jan 22, 2020
9137b8a
Merge branch 'dev' into icons-stacked
jacobmllr95 Jan 22, 2020
3d05b58
Merge branch 'dev' into icons-stacked
jacobmllr95 Jan 22, 2020
c1199cb
Merge branch 'dev' into icons-stacked
tmorehouse Jan 22, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions docs/content/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ 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}`
// Since there are over 300 icons, we only return `BIcon` and `BIconstack` 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}'
.filter(c => c.component === 'BIconBlank' || !/^BIcon[A-Z]/.test(c.component))
.sort((a, b) => (a.component < b.component ? -1 : a.component > b.component ? 1 : 0))
.map(c => {
c = { ...c }
if (c.component === 'BIconBlank') {
c.component = 'BIcon{IconName}'
// We add a special `srcComponent` to grab the prop `$options` data from
c.srcComponent = 'BIconBlank'
}
return c
})
export { icons }

const referenceContext = require.context('~/markdown/reference', true, /meta.json/)
Expand Down
10 changes: 4 additions & 6 deletions docs/pages/docs/icons/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,10 @@ export default {
computed: {
componentMeta() {
// `docs/content/index.js` massages the list of icon components
// to include only `BIcon` and an example component
const components = this.meta.components
// Add in a special property or grabbing the component props
// as `BIcon{IconName}` doesn't exist
components[1].srcComponent = 'BIconBlank'
return components
// to include only `BIcon`, `BIconstack` and an example component
// The example icon has a special `srcComponent` property that lists
// `BIconBlank` as the component to grab the `$options.props` from
return this.meta.components
},
importMeta() {
return { ...this.meta, slug: 'icons', components: this.componentMeta }
Expand Down
15 changes: 9 additions & 6 deletions scripts/create-icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ const iconsTemplateFn = _template(`// --- BEGIN AUTO-GENERATED FILE ---
// @IconsVersion: <%= version %>
// @Generated: <%= created %>
//
// This file is generated on each build. Do not edit this file.
//
// This file is generated on each build. Do not edit this file!

/*!
* BootstrapVue Icons, generated from Bootstrap Icons <%= version %>
*
Expand Down Expand Up @@ -77,14 +77,16 @@ const pluginTemplateFn = _template(`// --- BEGIN AUTO-GENERATED FILE ---
// @IconsVersion: <%= version %>
// @Generated: <%= created %>
//
// This file is generated on each build. Do not edit this file.
//
// This file is generated on each build. Do not edit this file!

import { pluginFactoryNoConfig } from '../utils/plugins'

// Icon helper component
import { BIcon } from './icon'

// Icon stacking component
import { BIconstack } from './iconstack'

import {
// BootstrapVue custom icons
BIconBlank,
Expand All @@ -105,6 +107,8 @@ export const IconsPlugin = /*#__PURE__*/ pluginFactoryNoConfig({
components: {
// Icon helper component
BIcon,
// Icon stacking component
BIconstack,
// BootstrapVue custom icon components
BIconBlank,
// Bootstrap icon components
Expand All @@ -128,8 +132,7 @@ const typesTemplateFn = _template(`// --- BEGIN AUTO-GENERATED FILE ---
// @IconsVersion: <%= version %>
// @Generated: <%= created %>
//
// This file is generated on each build. Do not edit this file.
//
// This file is generated on each build. Do not edit this file!

import Vue from 'vue'
import { BvComponent } from '../'
Expand Down
67 changes: 66 additions & 1 deletion src/icons/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ icons.

<div>
<!-- Component rendered by docs/pages/docs/icons.index.js -->
<!-- We use a `<div is="...">` to prevent marked loader from mangling the unknown tag-->
<!-- We use a `<div is="...">` to prevent marked loader from mangling the unknown tag -->
<div is="IconsTable"></div>
</div>

Expand Down Expand Up @@ -428,6 +428,71 @@ Shifting is applied after any rotation transforms. As with scaling, backgrounds
affected. If you need to shift the border/background with the icon, use Bootstrap's margin
[spacing utility classes](/docs/reference/utility-classes).

## Stacking icons

<span class="badge badge-info small">v2.3.0+</span>

Combine icons together via the use of the component `<b-iconstack>` and the `stacked` prop on
individual icons (`<b-icon>` or `<b-icon-{icon-name}>`) to create complex icons:

```html
<template>
<div>
<b-iconstack font-scale="5">
<b-icon stacked icon="camera" variant="info" scale="0.75" shift-v="-0.25"></b-icon>
<b-icon stacked icon="circle-slash" variant="danger"></b-icon>
</b-iconstack>

<b-iconstack font-scale="5" rotate="90">
<b-icon stacked icon="chevron-right" shift-h="-3" variant="danger"></b-icon>
<b-icon stacked icon="chevron-right" shift-h="0" variant="success"></b-icon>
<b-icon stacked icon="chevron-right" shift-h="3" variant="primary"></b-icon>
</b-iconstack>

<b-iconstack font-scale="5">
<b-icon stacked icon="circle-fill" variant="info"></b-icon>
<b-icon stacked icon="bell-fill" scale="0.5" variant="white"></b-icon>
<b-icon stacked icon="circle" variant="danger"></b-icon>
</b-iconstack>

<b-iconstack font-scale="5" variant="white">
<b-icon stacked icon="square-fill" variant="dark"></b-icon>
<b-icon stacked icon="arrow-up-short" scale="0.5" shift-v="2.5" shift-h="-2.5"></b-icon>
<b-icon stacked icon="arrow-up-short" scale="0.5" shift-v="2.5" shift-h="2.5" rotate="90"></b-icon>
<b-icon stacked icon="arrow-up-short" scale="0.5" shift-v="-2.5" shift-h="2.5" rotate="180"></b-icon>
<b-icon stacked icon="arrow-up-short" scale="0.5" shift-v="-2.5" shift-h="-2.5" rotate="270"></b-icon>
</b-iconstack>

<b-iconstack font-scale="5">
<b-icon stacked icon="square"></b-icon>
<b-icon stacked icon="check"></b-icon>
</b-iconstack>

<b-iconstack font-scale="5">
<b-icon stacked icon="square"></b-icon>
<b-icon stacked icon="dot" shift-h="-2.25" shift-v="3"></b-icon>
<b-icon stacked icon="dot" shift-h="-2.25"></b-icon>
<b-icon stacked icon="dot" shift-h="-2.25" shift-v="-3"></b-icon>
<b-icon stacked icon="dot" shift-h="2.25" shift-v="3"></b-icon>
<b-icon stacked icon="dot" shift-h="2.25"></b-icon>
<b-icon stacked icon="dot" shift-h="2.25" shift-v="-3"></b-icon>
</b-iconstack>
</div>
</template>

<!-- icons-stacking.vue -->
```

`<b-iconstack>` supports the same `variant`, `font-size`, and transformation props available on
individual icons.

Stacked icon notes:

- Remember to set the `stacked` prop on the inner icon components
- The `font-scale` prop cannot be used on the inner icon components
- The `width` and `height` attributes cannot be applied to the inner icon components
- Stacked icons **cannot** be stacked inside another `<b-iconstack>`

## Using in components

Easily place icons as content in other components.
Expand Down
60 changes: 42 additions & 18 deletions src/icons/helpers/make-icon.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Vue from '../../utils/vue'
import { mergeData } from 'vue-functional-data-merge'
import identity from '../../utils/identity'
import { kebabCase, pascalCase, trim } from '../../utils/string'
import { isUndefinedOrNull } from '../../utils/inspect'
import { toFloat } from '../../utils/number'
import { kebabCase, pascalCase, trim } from '../../utils/string'

// Common icon props (should be cloned/spread before using)
export const commonIconProps = {
Expand Down Expand Up @@ -52,28 +53,32 @@ const baseAttrs = {

// Shared private base component to reduce bundle/runtime size
// @vue/component
const BVIconBase = {
export const BVIconBase = /*#__PURE__*/ Vue.extend({
name: 'BVIconBase',
functional: true,
props: {
content: {
type: String
},
stacked: {
type: Boolean,
default: false
},
...commonIconProps
},
render(h, { data, props }) {
render(h, { data, props, children }) {
const fontScale = Math.max(toFloat(props.fontScale) || 1, 0) || 1
const scale = Math.max(toFloat(props.scale) || 1, 0) || 1
const rotate = toFloat(props.rotate) || 0
const shiftH = toFloat(props.shiftH) || 0
const shiftV = toFloat(props.shiftV) || 0
const flipH = props.flipH
const flipV = props.flipV
// Compute the transforms. Note that order is important as
// SVG transforms are applied in order from left to right
// and we want flipping/scale to occur before rotation.
// Note shifting is applied separately. Assumes that the
// viewbox is `0 0 20 20` (`10 10` is the center)
// Compute the transforms
// Note that order is important as SVG transforms are applied in order from
// left to right and we want flipping/scale to occur before rotation
// Note shifting is applied separately
// Assumes that the viewbox is `0 0 20 20` (`10 10` is the center)
const hasScale = flipH || flipV || scale !== 1
const hasTransforms = hasScale || rotate
const hasShift = shiftH || shiftV
Expand All @@ -84,11 +89,19 @@ const BVIconBase = {
hasTransforms ? 'translate(-10 -10)' : null
].filter(identity)

// Handling stacked icons
const isStacked = props.stacked
const hasContent = !isUndefinedOrNull(props.content)

// We wrap the content in a `<g>` for handling the transforms (except shift)
let $inner = h('g', {
attrs: { transform: transforms.join(' ') || null },
domProps: { innerHTML: props.content || '' }
})
let $inner = h(
'g',
{
attrs: { transform: transforms.join(' ') || null },
domProps: hasContent ? { innerHTML: props.content || '' } : {}
},
children
)

// If needed, we wrap in an additional `<g>` in order to handle the shifting
if (hasShift) {
Expand All @@ -103,28 +116,33 @@ const BVIconBase = {
'svg',
mergeData(
{
staticClass: 'b-icon bi',
class: { [`text-${props.variant}`]: !!props.variant },
attrs: baseAttrs,
style: { fontSize: fontScale === 1 ? null : `${fontScale * 100}%` }
style: isStacked ? {} : { fontSize: fontScale === 1 ? null : `${fontScale * 100}%` }
},
// Merge in user supplied data
data,
// If icon is stacked, null out some attrs
isStacked ? { attrs: { width: null, height: null, role: null, alt: null } } : {},
// These cannot be overridden by users
{
staticClass: 'b-icon bi',
attrs: { xmlns: 'http://www.w3.org/2000/svg', fill: 'currentColor' }
attrs: {
xmlns: isStacked ? null : 'http://www.w3.org/2000/svg',
fill: 'currentColor'
}
}
),
[$inner]
)
}
}
})

/**
* Icon component generator function
*
* @param {string} icon name (minus the leading `BIcon`)
* @param {string} raw innerHTML for SVG
* @param {string} raw `innerHTML` for SVG
* @return {VueComponent}
*/
export const makeIcon = (name, content) => {
Expand All @@ -137,7 +155,13 @@ export const makeIcon = (name, content) => {
return Vue.extend({
name: iconName,
functional: true,
props: { ...commonIconProps },
props: {
...commonIconProps,
stacked: {
type: Boolean,
default: false
}
},
render(h, { data, props }) {
return h(
BVIconBase,
Expand Down
6 changes: 5 additions & 1 deletion src/icons/icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ export const BIcon = /*#__PURE__*/ Vue.extend({
type: String,
default: null
},
...commonIconProps
...commonIconProps,
stacked: {
type: Boolean,
default: false
}
},
render(h, { data, props, parent }) {
const icon = pascalCase(trim(props.icon || '')).replace(RX_ICON_PREFIX, '')
Expand Down
5 changes: 2 additions & 3 deletions src/icons/icons.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// --- BEGIN AUTO-GENERATED FILE ---
//
// @IconsVersion: 1.0.0-alpha2
// @Generated: 2020-01-01T12:00:00.000Z
//
// This file is generated on each build. Do not edit this file.
// @Generated: 2020-01-22T07:06:51.693Z
//
// This file is generated on each build. Do not edit this file!

import Vue from 'vue'
import { BvComponent } from '../'
Expand Down
6 changes: 3 additions & 3 deletions src/icons/icons.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// --- BEGIN AUTO-GENERATED FILE ---
//
// @IconsVersion: 1.0.0-alpha2
// @Generated: 2020-01-01T12:00:00.000Z
//
// This file is generated on each build. Do not edit this file.
// @Generated: 2020-01-22T07:06:51.693Z
//
// This file is generated on each build. Do not edit this file!

/*!
* BootstrapVue Icons, generated from Bootstrap Icons 1.0.0-alpha2
*
Expand Down
17 changes: 17 additions & 0 deletions src/icons/iconstack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Vue from '../utils/vue'
import { mergeData } from 'vue-functional-data-merge'
import { commonIconProps, BVIconBase } from './helpers/make-icon'

// @vue/component
export const BIconstack = /*#__PURE__*/ Vue.extend({
name: 'BIconstack',
functional: true,
props: { ...commonIconProps },
render(h, { data, props, children }) {
return h(
BVIconBase,
mergeData(data, { staticClass: 'b-iconstack', props: { ...props, stacked: false } }),
children
)
}
})
Loading