Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(logos): support auto-size mode #9191

Merged
merged 2 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions badge-maker/lib/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const DEFAULT_LOGO_HEIGHT = 14

module.exports = {
DEFAULT_LOGO_HEIGHT,
}
5 changes: 4 additions & 1 deletion badge-maker/lib/make-badge.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const { normalizeColor, toSvgColor } = require('./color')
const badgeRenderers = require('./badge-renderers')
const { stripXmlWhitespace } = require('./xml')
const { DEFAULT_LOGO_HEIGHT } = require('./constants')

/*
note: makeBadge() is fairly thinly wrapped so if we are making changes here
Expand All @@ -17,6 +18,7 @@ module.exports = function makeBadge({
labelColor,
logo,
logoPosition,
logoSize,
logoWidth,
links = ['', ''],
}) {
Expand Down Expand Up @@ -45,7 +47,7 @@ module.exports = function makeBadge({
throw new Error(`Unknown badge style: '${style}'`)
}

logoWidth = +logoWidth || (logo ? 14 : 0)
logoWidth = +logoWidth || (logo ? DEFAULT_LOGO_HEIGHT : 0)

return stripXmlWhitespace(
render({
Expand All @@ -55,6 +57,7 @@ module.exports = function makeBadge({
logo,
logoPosition,
logoWidth,
logoSize,
logoPadding: logo && label.length ? 3 : 0,
color: toSvgColor(color),
labelColor: toSvgColor(labelColor),
Expand Down
1 change: 1 addition & 0 deletions core/base-service/base.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ describe('BaseService', function () {
namedLogo: undefined,
logo: undefined,
logoWidth: undefined,
logoSize: undefined,
logoPosition: undefined,
links: [],
labelColor: undefined,
Expand Down
22 changes: 20 additions & 2 deletions core/base-service/coalesce-badge.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {
decodeDataUrlFromQueryParam,
prepareNamedLogo,
} from '../../lib/logos.js'
import { svg2base64 } from '../../lib/svg-helpers.js'
import { svg2base64, getIconSize } from '../../lib/svg-helpers.js'
import { DEFAULT_LOGO_HEIGHT } from '../../badge-maker/lib/constants.js'
import coalesce from './coalesce.js'
import toArray from './to-array.js'

Expand Down Expand Up @@ -56,6 +57,7 @@ export default function coalesceBadge(
let {
logoWidth: overrideLogoWidth,
logoPosition: overrideLogoPosition,
logoSize: overrideLogoSize,
color: overrideColor,
labelColor: overrideLabelColor,
} = overrides
Expand Down Expand Up @@ -87,6 +89,7 @@ export default function coalesceBadge(
logoSvg: serviceLogoSvg,
namedLogo: serviceNamedLogo,
logoColor: serviceLogoColor,
logoSize: serviceLogoSize,
logoWidth: serviceLogoWidth,
logoPosition: serviceLogoPosition,
link: serviceLink,
Expand Down Expand Up @@ -119,7 +122,12 @@ export default function coalesceBadge(
style = 'flat'
}

let namedLogo, namedLogoColor, logoWidth, logoPosition, logoSvgBase64
let namedLogo,
namedLogoColor,
logoSize,
logoWidth,
logoPosition,
logoSvgBase64
if (overrideLogo) {
// `?logo=` could be a named logo or encoded svg.
const overrideLogoSvgBase64 = decodeDataUrlFromQueryParam(overrideLogo)
Expand All @@ -133,6 +141,7 @@ export default function coalesceBadge(
}
// If the logo has been overridden it does not make sense to inherit the
// original width or position.
logoSize = overrideLogoSize
logoWidth = overrideLogoWidth
logoPosition = overrideLogoPosition
} else {
Expand All @@ -145,13 +154,21 @@ export default function coalesceBadge(
)
namedLogoColor = coalesce(overrideLogoColor, serviceLogoColor)
}
logoSize = coalesce(overrideLogoSize, serviceLogoSize)
logoWidth = coalesce(overrideLogoWidth, serviceLogoWidth)
logoPosition = coalesce(overrideLogoPosition, serviceLogoPosition)
}
if (namedLogo) {
LitoMore marked this conversation as resolved.
Show resolved Hide resolved
const iconSize = getIconSize(String(namedLogo).toLowerCase())

if (!logoWidth && iconSize && logoSize === 'auto') {
logoWidth = (iconSize.width / iconSize.height) * DEFAULT_LOGO_HEIGHT
}

logoSvgBase64 = prepareNamedLogo({
name: namedLogo,
color: namedLogoColor,
size: logoSize,
style,
})
}
Expand Down Expand Up @@ -179,6 +196,7 @@ export default function coalesceBadge(
logo: logoSvgBase64,
logoWidth,
logoPosition,
logoSize,
links: toArray(overrideLink || serviceLink),
cacheLengthSeconds: coalesce(serviceCacheSeconds, defaultCacheSeconds),
}
Expand Down
14 changes: 14 additions & 0 deletions core/base-service/coalesce-badge.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,20 @@ describe('coalesceBadge', function () {
})
})

describe('Logo size', function () {
it('overrides the logoSize', function () {
expect(coalesceBadge({ logoSize: 'auto' }, {}, {})).to.include({
logoSize: 'auto',
})
})

it('applies the logo size', function () {
expect(
coalesceBadge({}, { namedLogo: 'npm', logoSize: 'auto' }, {}),
).to.include({ logoSize: 'auto' })
})
})

describe('Logo width', function () {
it('overrides the logoWidth', function () {
expect(coalesceBadge({ logoWidth: 20 }, {}, {})).to.include({
Expand Down
12 changes: 12 additions & 0 deletions core/base-service/openapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const globalParamRefs = [
{ $ref: '#/components/parameters/style' },
{ $ref: '#/components/parameters/logo' },
{ $ref: '#/components/parameters/logoColor' },
{ $ref: '#/components/parameters/logoSize' },
{ $ref: '#/components/parameters/label' },
{ $ref: '#/components/parameters/labelColor' },
{ $ref: '#/components/parameters/color' },
Expand Down Expand Up @@ -140,6 +141,17 @@ function category2openapi({ category, services, sort = false }) {
},
example: 'violet',
},
logoSize: {
name: 'logoSize',
in: 'query',
required: false,
description:
'Make icons adaptively resize by setting `auto`. Useful for some wider logos like `amd` and `amg`. Supported for simple-icons logos only.',
schema: {
type: 'string',
},
example: 'auto',
},
label: {
name: 'label',
in: 'query',
Expand Down
13 changes: 13 additions & 0 deletions core/base-service/openapi.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ const expected = {
schema: { type: 'string' },
example: 'violet',
},
logoSize: {
name: 'logoSize',
in: 'query',
required: false,
description:
'Make icons adaptively resize by setting `auto`. Useful for some wider logos like `amd` and `amg`. Supported for simple-icons logos only.',
schema: {
type: 'string',
},
example: 'auto',
},
label: {
name: 'label',
in: 'query',
Expand Down Expand Up @@ -158,6 +169,7 @@ const expected = {
{ $ref: '#/components/parameters/style' },
{ $ref: '#/components/parameters/logo' },
{ $ref: '#/components/parameters/logoColor' },
{ $ref: '#/components/parameters/logoSize' },
{ $ref: '#/components/parameters/label' },
{ $ref: '#/components/parameters/labelColor' },
{ $ref: '#/components/parameters/color' },
Expand Down Expand Up @@ -213,6 +225,7 @@ const expected = {
{ $ref: '#/components/parameters/style' },
{ $ref: '#/components/parameters/logo' },
{ $ref: '#/components/parameters/logoColor' },
{ $ref: '#/components/parameters/logoSize' },
{ $ref: '#/components/parameters/label' },
{ $ref: '#/components/parameters/labelColor' },
{ $ref: '#/components/parameters/color' },
Expand Down
9 changes: 4 additions & 5 deletions lib/load-simple-icons.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as originalSimpleIcons from 'simple-icons/icons'
import { svg2base64 } from './svg-helpers.js'

function loadSimpleIcons() {
const simpleIcons = {}
Expand All @@ -16,10 +15,10 @@ function loadSimpleIcons() {
const icon = originalSimpleIcons[key]
const { title, slug, hex } = icon

icon.base64 = {
default: svg2base64(icon.svg.replace('<svg', `<svg fill="#${hex}"`)),
light: svg2base64(icon.svg.replace('<svg', '<svg fill="whitesmoke"')),
dark: svg2base64(icon.svg.replace('<svg', '<svg fill="#333"')),
icon.styles = {
default: icon.svg.replace('<svg', `<svg fill="#${hex}"`),
light: icon.svg.replace('<svg', '<svg fill="whitesmoke"'),
dark: icon.svg.replace('<svg', '<svg fill="#333"'),
}

// There are a few instances where multiple icons have the same title
Expand Down
2 changes: 1 addition & 1 deletion lib/load-simple-icons.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('loadSimpleIcons', function () {
})

it('prepares three color themes', function () {
expect(simpleIcons.sentry.base64).to.have.all.keys(
expect(simpleIcons.sentry.styles).to.have.all.keys(
'default',
'light',
'dark',
Expand Down
35 changes: 27 additions & 8 deletions lib/logos.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
normalizeColor,
} from '../badge-maker/lib/color.js'
import coalesce from '../core/base-service/coalesce.js'
import { svg2base64 } from './svg-helpers.js'
import { svg2base64, getIconSize, resetIconPosition } from './svg-helpers.js'
import loadLogos from './load-logos.js'
import loadSimpleIcons from './load-simple-icons.js'
const logos = loadLogos()
Expand Down Expand Up @@ -88,25 +88,42 @@ function getSimpleIconStyle({ icon, style }) {
return 'default'
}

function getSimpleIcon({ name, color, style }) {
// The `size` parameter is used to determine whether the icon should be resized
// to fit the badge. If `size` is 'auto', the icon will be resized to fit the
// badge. If `size` is not 'auto', the icon will be displayed at its original.
// https://github.com/badges/shields/pull/9191
function getSimpleIcon({ name, color, style, size }) {
const key = name === 'travis' ? 'travis-ci' : name.replace(/ /g, '-')

if (!(key in simpleIcons)) {
return undefined
}

let iconSvg

const svgColor = toSvgColor(color)
if (svgColor) {
return svg2base64(
simpleIcons[key].svg.replace('<svg', `<svg fill="${svgColor}"`),
)
iconSvg = simpleIcons[key].svg.replace('<svg', `<svg fill="${svgColor}"`)
} else {
const iconStyle = getSimpleIconStyle({ icon: simpleIcons[key], style })
return simpleIcons[key].base64[iconStyle]
iconSvg = simpleIcons[key].styles[iconStyle]
}

if (size === 'auto') {
const { width: iconWidth, height: iconHeight } = getIconSize(key)

if (iconWidth > iconHeight) {
const path = resetIconPosition(simpleIcons[key].path)
iconSvg = iconSvg
.replace('viewBox="0 0 24 24"', `viewBox="0 0 24 ${iconHeight}"`)
.replace(/<path d=".*"\/>/, `<path d="${path}"/>`)
}
}

return svg2base64(iconSvg)
}

function prepareNamedLogo({ name, color, style }) {
function prepareNamedLogo({ name, color, style, size }) {
LitoMore marked this conversation as resolved.
Show resolved Hide resolved
if (typeof name !== 'string') {
return undefined
}
Expand All @@ -118,7 +135,8 @@ function prepareNamedLogo({ name, color, style }) {
}

return (
getShieldsIcon({ name, color }) || getSimpleIcon({ name, color, style })
getShieldsIcon({ name, color }) ||
getSimpleIcon({ name, color, style, size })
)
}

Expand All @@ -131,6 +149,7 @@ function makeLogo(defaultNamedLogo, overrides) {
name: coalesce(overrides.logo, defaultNamedLogo),
color: overrides.logoColor,
style: overrides.style,
size: overrides.logoSize,
})
}
}
Expand Down
8 changes: 8 additions & 0 deletions lib/logos.spec.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 32 additions & 1 deletion lib/svg-helpers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
import SVGPathCommander from 'svg-path-commander'
import loadSimpleIcons from './load-simple-icons.js'

function svg2base64(svg) {
return `data:image/svg+xml;base64,${Buffer.from(svg.trim()).toString(
'base64',
)}`
}

export { svg2base64 }
function getIconSize(iconKey) {
const simpleIcons = loadSimpleIcons()

if (!(iconKey in simpleIcons)) {
return undefined
}

const {
width,
height,
x: x0,
y: y0,
x2: x1,
y2: y1,
} = SVGPathCommander.getPathBBox(simpleIcons[iconKey].path)

return { width, height, x0, y0, x1, y1 }
}

function resetIconPosition(path) {
const { x: offsetX, y: offsetY } = SVGPathCommander.getPathBBox(path)
const pathReset = new SVGPathCommander(path)
.transform({ translate: [-offsetX, -offsetY] })
.toString()

return pathReset
}

export { svg2base64, getIconSize, resetIconPosition }
Loading
Loading