Skip to content
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 22 additions & 0 deletions .github/workflows/generated-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: generated-check

on:
pull_request:

jobs:
generated-check:
name: generated-check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Install
run: npm ci --progress=false
- name: Generate icons
run: npm run generate
- name: Fail on stale generated files
run: |
git add -N .
git diff --exit-code --stat -- icons index.js fonts android/src/main/assets
50 changes: 50 additions & 0 deletions .github/workflows/pr-snapshot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: PR Snapshot Release

on:
pull_request:

concurrency:
group: pr-snapshot-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
snapshot:
name: npm Snapshot Release
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 18
registry-url: "https://npm.pkg.github.com"
scope: "@oolio-group"

- name: Download & Build Icons
run: npm ci --progress=false && npm run generate

- name: Set snapshot version
id: version
run: |
VERSION="0.0.0-$(date +%s)"
npm version --no-git-tag-version "$VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Publish npm snapshot under snapshot dist-tag
run: npm publish --tag snapshot
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Comment snapshot version on PR
run: |
gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --body "📦 Snapshot release published: \`${{ steps.version.outputs.version }}\`

- **npm**: \`\"@oolio-group/react-native-unicons\": \"${{ steps.version.outputs.version }}\"\`"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Binary file added android/src/main/assets/fonts/unicons-line.ttf
Binary file not shown.
116 changes: 75 additions & 41 deletions build/generate.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,97 @@
const path = require('path')
const fs = require('fs-plus')
const cheerio = require('cheerio')
const upperCamelCase = require('uppercamelcase')

const iconsComponentPath = path.join(process.cwd(), 'icons')
const iconsIndexPath = path.join(process.cwd(), 'index.js')
const root = process.cwd()
const iconsComponentPath = path.join(root, 'icons')
const iconsIndexPath = path.join(root, 'index.js')
const uniconsRoot = path.join(root, 'node_modules/@iconscout/unicons')
const uniconsConfig = require('@iconscout/unicons/json/line.json')

// Clear Directories
const FONT_FILE = 'unicons-line.ttf'
const fontSource = path.join(uniconsRoot, 'fonts/line', FONT_FILE)
const fontTargets = [
path.join(root, 'fonts', FONT_FILE),
path.join(root, 'android/src/main/assets/fonts', FONT_FILE)
]

const readCodepoints = font => {
const tableCount = font.readUInt16BE(4)
let cmapOffset
for (let i = 0; i < tableCount; i++) {
const record = 12 + i * 16
if (font.toString('ascii', record, record + 4) === 'cmap') {
cmapOffset = font.readUInt32BE(record + 8)
}
}
if (cmapOffset === undefined) throw new Error(`${FONT_FILE} has no cmap table`)

const codepoints = new Set()
const subtableCount = font.readUInt16BE(cmapOffset + 2)
for (let i = 0; i < subtableCount; i++) {
const offset = cmapOffset + font.readUInt32BE(cmapOffset + 4 + i * 8 + 4)
const format = font.readUInt16BE(offset)
if (format === 4) {
const segCount = font.readUInt16BE(offset + 6) / 2
const ends = offset + 14
const starts = ends + segCount * 2 + 2
for (let s = 0; s < segCount; s++) {
const start = font.readUInt16BE(starts + s * 2)
const end = font.readUInt16BE(ends + s * 2)
for (let c = start; c <= end && c !== 0xffff; c++) codepoints.add(c)
}
} else if (format === 12) {
const groups = font.readUInt32BE(offset + 12)
for (let g = 0; g < groups; g++) {
const group = offset + 16 + g * 12
const start = font.readUInt32BE(group)
const end = font.readUInt32BE(group + 4)
for (let c = start; c <= end; c++) codepoints.add(c)
}
}
}
return codepoints
}

const font = fs.readFileSync(fontSource)
const fontCodepoints = readCodepoints(font)

const missing = uniconsConfig.filter(
icon => !icon.unicode || !fontCodepoints.has(parseInt(icon.unicode, 16))
)
if (missing.length) {
throw new Error(
`${missing.length} icons have no glyph in ${FONT_FILE}: ${missing
.slice(0, 10)
.map(icon => icon.name)
.join(', ')}`
)
}

fontTargets.forEach(target => {
fs.makeTreeSync(path.dirname(target))
fs.writeFileSync(target, font)
})

fs.removeSync(iconsComponentPath)
fs.mkdirSync(iconsComponentPath)

const indexJs = []
const indexJs = ['import "./registerFont";']

uniconsConfig.forEach(icon => {
const baseName = `uil-${icon.name}`
const location = path.join(iconsComponentPath, `${baseName}.js`)
const name = upperCamelCase(baseName)
const svgFile = fs.readFileSync(path.resolve('node_modules/@iconscout/unicons', icon.svg), 'utf-8')

let data = svgFile.replace(/<svg[^>]+>/gi, '').replace(/<\/svg>/gi, '')
// Get Path Content from SVG
const $ = cheerio.load(data, {
xmlMode: true
})
const svgPath = $('path').attr('d')

const template = `import React from "react";
import Svg, { Path } from "react-native-svg";
import PropTypes from "prop-types";

const ${name} = props => {
const { color = "currentColor", size = "24", ...otherProps } = props;
return (
<Svg
width={size}
height={size}
viewBox="0 0 24 24"
fill={color}
{...otherProps}
>
<Path d="${svgPath}" />
</Svg>
);
};

${name}.propTypes = {
color: PropTypes.string,
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};

export default ${name};

const template = `import createIcon from "../createIcon";

export default createIcon("\\u{${icon.unicode.toLowerCase()}}", "${name}");
`
fs.writeFileSync(location, template, 'utf-8')

// Add it to index.js
indexJs.push(`export { default as ${name} } from './icons/${baseName}'`)
})

fs.writeFileSync(iconsIndexPath, indexJs.join('\n'), 'utf-8')

console.log(`Generated ${uniconsConfig.length} icon components.`)
console.log(`Generated ${uniconsConfig.length} icon components using ${FONT_FILE}.`)
53 changes: 53 additions & 0 deletions createIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react";
import { StyleSheet, Text } from "react-native";
import PropTypes from "prop-types";

export const FONT_FAMILY = "unicons-line";

const sizeStyles = {};

const styleForSize = size => {
const key = String(size);
if (!sizeStyles[key]) {
const pixels = Number(size) || 24;
sizeStyles[key] = StyleSheet.create({
icon: {
width: pixels,
height: pixels,
fontSize: pixels,
lineHeight: pixels,
color: "black",
textAlign: "center",
textAlignVertical: "center",
includeFontPadding: false,
fontFamily: FONT_FAMILY
}
}).icon;
}
return sizeStyles[key];
};

const createIcon = (glyph, displayName) => {
const Icon = ({ color, size = 24, style, ...otherProps }) => (
<Text
allowFontScaling={false}
selectable={false}
accessible={false}
importantForAccessibility="no"
{...otherProps}
style={[styleForSize(size), style, color != null && { color }]}
>
{glyph}
</Text>
);

Icon.displayName = displayName;
Icon.propTypes = {
color: PropTypes.string,
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};

return React.memo(Icon);
};

export default createIcon;
Binary file added fonts/unicons-line.ttf
Binary file not shown.
27 changes: 2 additions & 25 deletions icons/uil-0-plus.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,3 @@
import React from "react";
import Svg, { Path } from "react-native-svg";
import PropTypes from "prop-types";
import createIcon from "../createIcon";

const Uil0Plus = props => {
const { color = "currentColor", size = "24", ...otherProps } = props;
return (
<Svg
width={size}
height={size}
viewBox="0 0 24 24"
fill={color}
{...otherProps}
>
<Path d="M18,5h1V6a1,1,0,0,0,2,0V5h1a1,1,0,0,0,0-2H21V2a1,1,0,0,0-2,0V3H18a1,1,0,0,0,0,2ZM12.5,6h-1a3,3,0,0,0-3,3v6a3,3,0,0,0,3,3h1a3,3,0,0,0,3-3V9A3,3,0,0,0,12.5,6Zm1,9a1,1,0,0,1-1,1h-1a1,1,0,0,1-1-1V9a1,1,0,0,1,1-1h1a1,1,0,0,1,1,1Zm8.1-6a1,1,0,0,0-.78,1.18,9,9,0,1,1-7-7,1,1,0,1,0,.4-2A10.8,10.8,0,0,0,12,1,11,11,0,1,0,23,12a10.8,10.8,0,0,0-.22-2.2A1,1,0,0,0,21.6,9Z" />
</Svg>
);
};

Uil0Plus.propTypes = {
color: PropTypes.string,
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};


export default Uil0Plus;
export default createIcon("\u{ebf6}", "Uil0Plus");
27 changes: 2 additions & 25 deletions icons/uil-10-plus.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,3 @@
import React from "react";
import Svg, { Path } from "react-native-svg";
import PropTypes from "prop-types";
import createIcon from "../createIcon";

const Uil10Plus = props => {
const { color = "currentColor", size = "24", ...otherProps } = props;
return (
<Svg
width={size}
height={size}
viewBox="0 0 24 24"
fill={color}
{...otherProps}
>
<Path d="M18,5h1V6a1,1,0,0,0,2,0V5h1a1,1,0,0,0,0-2H21V2a1,1,0,0,0-2,0V3H18a1,1,0,0,0,0,2ZM11,9v6a3,3,0,0,0,3,3h1a3,3,0,0,0,3-3V9a3,3,0,0,0-3-3H14A3,3,0,0,0,11,9Zm5,0v6a1,1,0,0,1-1,1H14a1,1,0,0,1-1-1V9a1,1,0,0,1,1-1h1A1,1,0,0,1,16,9Zm5.6,0a1,1,0,0,0-.78,1.18,9,9,0,1,1-7-7,1,1,0,1,0,.4-2A10.8,10.8,0,0,0,12,1,11,11,0,1,0,23,12a10.8,10.8,0,0,0-.22-2.2A1,1,0,0,0,21.6,9ZM7,7V17a1,1,0,0,0,2,0V7A1,1,0,0,0,7,7Z" />
</Svg>
);
};

Uil10Plus.propTypes = {
color: PropTypes.string,
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};


export default Uil10Plus;
export default createIcon("\u{ebf0}", "Uil10Plus");
27 changes: 2 additions & 25 deletions icons/uil-12-plus.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,3 @@
import React from "react";
import Svg, { Path } from "react-native-svg";
import PropTypes from "prop-types";
import createIcon from "../createIcon";

const Uil12Plus = props => {
const { color = "currentColor", size = "24", ...otherProps } = props;
return (
<Svg
width={size}
height={size}
viewBox="0 0 24 24"
fill={color}
{...otherProps}
>
<Path d="M11,9a1,1,0,0,0,2,0,1,1,0,0,1,1-1h1a1,1,0,0,1,1,1v1a1,1,0,0,1-1,1H14a3,3,0,0,0-3,3v3a1,1,0,0,0,1,1h5a1,1,0,0,0,0-2H13V14a1,1,0,0,1,1-1h1a3,3,0,0,0,3-3V9a3,3,0,0,0-3-3H14A3,3,0,0,0,11,9Zm7-4h1V6a1,1,0,0,0,2,0V5h1a1,1,0,0,0,0-2H21V2a1,1,0,0,0-2,0V3H18a1,1,0,0,0,0,2ZM7,7V17a1,1,0,0,0,2,0V7A1,1,0,0,0,7,7ZM21.6,9a1,1,0,0,0-.78,1.18,9,9,0,1,1-7-7,1,1,0,1,0,.4-2A10.8,10.8,0,0,0,12,1,11,11,0,1,0,23,12a10.8,10.8,0,0,0-.22-2.2A1,1,0,0,0,21.6,9Z" />
</Svg>
);
};

Uil12Plus.propTypes = {
color: PropTypes.string,
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};


export default Uil12Plus;
export default createIcon("\u{ebee}", "Uil12Plus");
26 changes: 2 additions & 24 deletions icons/uil-13-plus.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
import React from "react";
import Svg, { Path } from "react-native-svg";
import PropTypes from "prop-types";
import createIcon from "../createIcon";

const Uil13Plus = props => {
const { color = "currentColor", size = "24", ...otherProps } = props;
return (
<Svg
width={size}
height={size}
viewBox="0 0 24 24"
fill={color}
{...otherProps}
>
<Path d="M18,5h1V6a1,1,0,0,0,2,0V5h1a1,1,0,0,0,0-2H21V2a1,1,0,0,0-2,0V3H18a1,1,0,0,0,0,2ZM11,9a1,1,0,0,0,2,0,1,1,0,0,1,1-1h1a1,1,0,0,1,1,1v1a1,1,0,0,1-1,1h-.5a1,1,0,0,0,0,2H15a1,1,0,0,1,1,1v1a1,1,0,0,1-1,1H14a1,1,0,0,1-1-1,1,1,0,0,0-2,0,3,3,0,0,0,3,3h1a3,3,0,0,0,3-3V14a3,3,0,0,0-.78-2A3,3,0,0,0,18,10V9a3,3,0,0,0-3-3H14A3,3,0,0,0,11,9ZM7,7V17a1,1,0,0,0,2,0V7A1,1,0,0,0,7,7ZM21.6,9a1,1,0,0,0-.78,1.18,9,9,0,1,1-7-7,1,1,0,1,0,.4-2A10.8,10.8,0,0,0,12,1,11,11,0,1,0,23,12a10.8,10.8,0,0,0-.22-2.2A1,1,0,0,0,21.6,9Z" />
</Svg>
);
};

Uil13Plus.propTypes = {
color: PropTypes.string,
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};

export default Uil13Plus;
export default createIcon("\u{ebf2}", "Uil13Plus");
Loading
Loading