Skip to content

Commit bc29d87

Browse files
committed
feat(logos): support auto-sizing mode
1 parent 6f78e6f commit bc29d87

File tree

11 files changed

+187
-17
lines changed

11 files changed

+187
-17
lines changed

badge-maker/lib/constants.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const DEFAULT_LOGO_HEIGHT = 14
2+
3+
module.exports = {
4+
DEFAULT_LOGO_HEIGHT,
5+
}

badge-maker/lib/make-badge.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const { normalizeColor, toSvgColor } = require('./color')
44
const badgeRenderers = require('./badge-renderers')
55
const { stripXmlWhitespace } = require('./xml')
6+
const { DEFAULT_LOGO_HEIGHT } = require('./constants')
67

78
/*
89
note: makeBadge() is fairly thinly wrapped so if we are making changes here
@@ -17,6 +18,7 @@ module.exports = function makeBadge({
1718
labelColor,
1819
logo,
1920
logoPosition,
21+
logoSize,
2022
logoWidth,
2123
links = ['', ''],
2224
}) {
@@ -45,7 +47,7 @@ module.exports = function makeBadge({
4547
throw new Error(`Unknown badge style: '${style}'`)
4648
}
4749

48-
logoWidth = +logoWidth || (logo ? 14 : 0)
50+
logoWidth = +logoWidth || (logo ? DEFAULT_LOGO_HEIGHT : 0)
4951

5052
return stripXmlWhitespace(
5153
render({
@@ -55,6 +57,7 @@ module.exports = function makeBadge({
5557
logo,
5658
logoPosition,
5759
logoWidth,
60+
logoSize,
5861
logoPadding: logo && label.length ? 3 : 0,
5962
color: toSvgColor(color),
6063
labelColor: toSvgColor(labelColor),

core/base-service/coalesce-badge.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import {
22
decodeDataUrlFromQueryParam,
33
prepareNamedLogo,
44
} from '../../lib/logos.js'
5-
import { svg2base64 } from '../../lib/svg-helpers.js'
5+
import { svg2base64, getIconSize } from '../../lib/svg-helpers.js'
6+
import { DEFAULT_LOGO_HEIGHT } from '../../badge-maker/lib/constants.js'
67
import coalesce from './coalesce.js'
78
import toArray from './to-array.js'
89

@@ -55,7 +56,9 @@ export default function coalesceBadge(
5556
} = overrides
5657
let {
5758
logoWidth: overrideLogoWidth,
59+
logoHeight: overrideLogoHeight,
5860
logoPosition: overrideLogoPosition,
61+
logoSize: overrideLogoSize,
5962
color: overrideColor,
6063
labelColor: overrideLabelColor,
6164
} = overrides
@@ -76,6 +79,7 @@ export default function coalesceBadge(
7679
overrideLabelColor = `${overrideLabelColor}`
7780
}
7881
overrideLogoWidth = +overrideLogoWidth || undefined
82+
overrideLogoHeight = +overrideLogoHeight || undefined
7983
overrideLogoPosition = +overrideLogoPosition || undefined
8084

8185
const {
@@ -88,6 +92,7 @@ export default function coalesceBadge(
8892
namedLogo: serviceNamedLogo,
8993
logoColor: serviceLogoColor,
9094
logoWidth: serviceLogoWidth,
95+
logoHeight: serviceLogoHeight,
9196
logoPosition: serviceLogoPosition,
9297
link: serviceLink,
9398
cacheSeconds: serviceCacheSeconds,
@@ -119,7 +124,12 @@ export default function coalesceBadge(
119124
style = 'flat'
120125
}
121126

122-
let namedLogo, namedLogoColor, logoWidth, logoPosition, logoSvgBase64
127+
let namedLogo,
128+
namedLogoColor,
129+
logoWidth,
130+
logoHeight,
131+
logoPosition,
132+
logoSvgBase64
123133
if (overrideLogo) {
124134
// `?logo=` could be a named logo or encoded svg.
125135
const overrideLogoSvgBase64 = decodeDataUrlFromQueryParam(overrideLogo)
@@ -134,6 +144,7 @@ export default function coalesceBadge(
134144
// If the logo has been overridden it does not make sense to inherit the
135145
// original width or position.
136146
logoWidth = overrideLogoWidth
147+
logoHeight = overrideLogoHeight
137148
logoPosition = overrideLogoPosition
138149
} else {
139150
if (serviceLogoSvg) {
@@ -146,12 +157,21 @@ export default function coalesceBadge(
146157
namedLogoColor = coalesce(overrideLogoColor, serviceLogoColor)
147158
}
148159
logoWidth = coalesce(overrideLogoWidth, serviceLogoWidth)
160+
logoHeight = coalesce(overrideLogoHeight, serviceLogoHeight)
149161
logoPosition = coalesce(overrideLogoPosition, serviceLogoPosition)
150162
}
151163
if (namedLogo) {
164+
const iconSize = getIconSize(namedLogo.toLowerCase())
165+
166+
if (!logoWidth && iconSize && overrideLogoSize === 'auto') {
167+
logoWidth =
168+
(iconSize.width / iconSize.height) * (logoHeight || DEFAULT_LOGO_HEIGHT)
169+
}
170+
152171
logoSvgBase64 = prepareNamedLogo({
153172
name: namedLogo,
154173
color: namedLogoColor,
174+
size: overrideLogoSize,
155175
style,
156176
})
157177
}
@@ -179,6 +199,7 @@ export default function coalesceBadge(
179199
logo: logoSvgBase64,
180200
logoWidth,
181201
logoPosition,
202+
logoSize: overrideLogoSize,
182203
links: toArray(overrideLink || serviceLink),
183204
cacheLengthSeconds: coalesce(serviceCacheSeconds, defaultCacheSeconds),
184205
}

core/base-service/openapi.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ const globalParamRefs = [
33
{ $ref: '#/components/parameters/style' },
44
{ $ref: '#/components/parameters/logo' },
55
{ $ref: '#/components/parameters/logoColor' },
6+
{ $ref: '#/components/parameters/logoSize' },
7+
{ $ref: '#/components/parameters/logoWidth' },
8+
{ $ref: '#/components/parameters/logoHeight' },
69
{ $ref: '#/components/parameters/label' },
710
{ $ref: '#/components/parameters/labelColor' },
811
{ $ref: '#/components/parameters/color' },
@@ -264,6 +267,37 @@ function category2openapi(category, services) {
264267
},
265268
example: 'violet',
266269
},
270+
logoSize: {
271+
name: 'logoSize',
272+
in: 'query',
273+
required: false,
274+
description:
275+
"Make icons adaptively resize by setting `auto`. It's useful for some wider logos like `amd` and `amg`.",
276+
schema: {
277+
type: 'string',
278+
},
279+
example: 'auto',
280+
},
281+
logoWidth: {
282+
name: 'logoWidth',
283+
in: 'query',
284+
required: false,
285+
description: 'The width of the logo, default to `14`',
286+
schema: {
287+
type: 'string',
288+
},
289+
example: '20',
290+
},
291+
logoHeight: {
292+
name: 'logoHeight',
293+
in: 'query',
294+
required: false,
295+
description: 'The height of the logo, default to `14`',
296+
schema: {
297+
type: 'string',
298+
},
299+
example: '12',
300+
},
267301
label: {
268302
name: 'label',
269303
in: 'query',

core/base-service/openapi.spec.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ const expected = {
170170
{ $ref: '#/components/parameters/style' },
171171
{ $ref: '#/components/parameters/logo' },
172172
{ $ref: '#/components/parameters/logoColor' },
173+
{ $ref: '#/components/parameters/logoSize' },
174+
{ $ref: '#/components/parameters/logoWidth' },
175+
{ $ref: '#/components/parameters/logoHeight' },
173176
{ $ref: '#/components/parameters/label' },
174177
{ $ref: '#/components/parameters/labelColor' },
175178
{ $ref: '#/components/parameters/color' },
@@ -225,6 +228,9 @@ const expected = {
225228
{ $ref: '#/components/parameters/style' },
226229
{ $ref: '#/components/parameters/logo' },
227230
{ $ref: '#/components/parameters/logoColor' },
231+
{ $ref: '#/components/parameters/logoSize' },
232+
{ $ref: '#/components/parameters/logoWidth' },
233+
{ $ref: '#/components/parameters/logoHeight' },
228234
{ $ref: '#/components/parameters/label' },
229235
{ $ref: '#/components/parameters/labelColor' },
230236
{ $ref: '#/components/parameters/color' },
@@ -279,6 +285,9 @@ const expected = {
279285
{ $ref: '#/components/parameters/style' },
280286
{ $ref: '#/components/parameters/logo' },
281287
{ $ref: '#/components/parameters/logoColor' },
288+
{ $ref: '#/components/parameters/logoSize' },
289+
{ $ref: '#/components/parameters/logoWidth' },
290+
{ $ref: '#/components/parameters/logoHeight' },
282291
{ $ref: '#/components/parameters/label' },
283292
{ $ref: '#/components/parameters/labelColor' },
284293
{ $ref: '#/components/parameters/color' },
@@ -325,6 +334,9 @@ const expected = {
325334
{ $ref: '#/components/parameters/style' },
326335
{ $ref: '#/components/parameters/logo' },
327336
{ $ref: '#/components/parameters/logoColor' },
337+
{ $ref: '#/components/parameters/logoSize' },
338+
{ $ref: '#/components/parameters/logoWidth' },
339+
{ $ref: '#/components/parameters/logoHeight' },
328340
{ $ref: '#/components/parameters/label' },
329341
{ $ref: '#/components/parameters/labelColor' },
330342
{ $ref: '#/components/parameters/color' },

lib/load-simple-icons.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as originalSimpleIcons from 'simple-icons/icons'
2-
import { svg2base64 } from './svg-helpers.js'
32

43
function loadSimpleIcons() {
54
const simpleIcons = {}
@@ -16,10 +15,10 @@ function loadSimpleIcons() {
1615
const icon = originalSimpleIcons[key]
1716
const { title, slug, hex } = icon
1817

19-
icon.base64 = {
20-
default: svg2base64(icon.svg.replace('<svg', `<svg fill="#${hex}"`)),
21-
light: svg2base64(icon.svg.replace('<svg', '<svg fill="whitesmoke"')),
22-
dark: svg2base64(icon.svg.replace('<svg', '<svg fill="#333"')),
18+
icon.styles = {
19+
default: icon.svg.replace('<svg', `<svg fill="#${hex}"`),
20+
light: icon.svg.replace('<svg', '<svg fill="whitesmoke"'),
21+
dark: icon.svg.replace('<svg', '<svg fill="#333"'),
2322
}
2423

2524
// There are a few instances where multiple icons have the same title

lib/logos.js

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
normalizeColor,
66
} from '../badge-maker/lib/color.js'
77
import coalesce from '../core/base-service/coalesce.js'
8-
import { svg2base64 } from './svg-helpers.js'
8+
import { svg2base64, getIconSize, resetIconPosition } from './svg-helpers.js'
99
import loadLogos from './load-logos.js'
1010
import loadSimpleIcons from './load-simple-icons.js'
1111
const logos = loadLogos()
@@ -88,25 +88,38 @@ function getSimpleIconStyle({ icon, style }) {
8888
return 'default'
8989
}
9090

91-
function getSimpleIcon({ name, color, style }) {
91+
function getSimpleIcon({ name, color, style, size = 'contain' }) {
9292
const key = name === 'travis' ? 'travis-ci' : name.replace(/ /g, '-')
9393

9494
if (!(key in simpleIcons)) {
9595
return undefined
9696
}
9797

98+
let iconSvg
99+
98100
const svgColor = toSvgColor(color)
99101
if (svgColor) {
100-
return svg2base64(
101-
simpleIcons[key].svg.replace('<svg', `<svg fill="${svgColor}"`)
102-
)
102+
iconSvg = simpleIcons[key].svg.replace('<svg', `<svg fill="${svgColor}"`)
103103
} else {
104104
const iconStyle = getSimpleIconStyle({ icon: simpleIcons[key], style })
105-
return simpleIcons[key].base64[iconStyle]
105+
iconSvg = simpleIcons[key].styles[iconStyle]
106+
}
107+
108+
if (size === 'auto') {
109+
const { width: iconWidth, height: iconHeight } = getIconSize(key)
110+
111+
if (iconWidth > iconHeight) {
112+
const path = resetIconPosition(simpleIcons[key].path)
113+
iconSvg = iconSvg
114+
.replace('viewBox="0 0 24 24"', `viewBox="0 0 24 ${iconHeight}"`)
115+
.replace(/<path d=".*"\/>/, `<path d="${path}"/>`)
116+
}
106117
}
118+
119+
return svg2base64(iconSvg)
107120
}
108121

109-
function prepareNamedLogo({ name, color, style }) {
122+
function prepareNamedLogo({ name, color, style, size }) {
110123
if (typeof name !== 'string') {
111124
return undefined
112125
}
@@ -118,7 +131,8 @@ function prepareNamedLogo({ name, color, style }) {
118131
}
119132

120133
return (
121-
getShieldsIcon({ name, color }) || getSimpleIcon({ name, color, style })
134+
getShieldsIcon({ name, color }) ||
135+
getSimpleIcon({ name, color, style, size })
122136
)
123137
}
124138

@@ -131,6 +145,7 @@ function makeLogo(defaultNamedLogo, overrides) {
131145
name: coalesce(overrides.logo, defaultNamedLogo),
132146
color: overrides.logoColor,
133147
style: overrides.style,
148+
size: overrides.logoSize,
134149
})
135150
}
136151
}

lib/svg-helpers.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,38 @@
1+
import SVGPathCommander from 'svg-path-commander'
2+
import loadSimpleIcons from './load-simple-icons.js'
3+
14
function svg2base64(svg) {
25
return `data:image/svg+xml;base64,${Buffer.from(svg.trim()).toString(
36
'base64'
47
)}`
58
}
69

7-
export { svg2base64 }
10+
function getIconSize(iconKey) {
11+
const simpleIcons = loadSimpleIcons()
12+
13+
if (!(iconKey in simpleIcons)) {
14+
return undefined
15+
}
16+
17+
const {
18+
width,
19+
height,
20+
x: x0,
21+
y: y0,
22+
x2: x1,
23+
y2: y1,
24+
} = SVGPathCommander.getPathBBox(simpleIcons[iconKey].path)
25+
26+
return { width, height, x0, y0, x1, y1 }
27+
}
28+
29+
function resetIconPosition(path) {
30+
const { x: offsetX, y: offsetY } = SVGPathCommander.getPathBBox(path)
31+
const pathReset = new SVGPathCommander(path)
32+
.transform({ translate: [-offsetX, -offsetY] })
33+
.toString()
34+
35+
return pathReset
36+
}
37+
38+
export { svg2base64, getIconSize, resetIconPosition }

0 commit comments

Comments
 (0)