├── .babelrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── ci.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .tool-versions ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DEVELOPMENT.md ├── LICENSE.txt ├── README.md ├── UPGRADING.md ├── bin ├── dev └── setup ├── examples └── vue-awesome-example │ ├── .browserslistrc │ ├── .editorconfig │ ├── .eslintrc.js │ ├── .gitignore │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ └── index.html │ ├── src │ ├── App.vue │ ├── main.ts │ └── shims-vue.d.ts │ ├── tsconfig.json │ └── vue.config.js ├── index.d.ts ├── index.es.js ├── index.js ├── package-lock.json ├── package.json ├── rollup.config.js └── src ├── components ├── FontAwesomeIcon.js ├── FontAwesomeLayers.js ├── FontAwesomeLayersText.js ├── __fixtures__ │ ├── helpers.js │ └── icons.js └── __tests__ │ ├── FontAwesomeIcon.test.js │ ├── FontAwesomeLayers.test.js │ └── FontAwesomeLayersText.test.js ├── converter.js ├── index.js ├── logger.js └── utils.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "node": "current" 8 | } 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Reproducible test case** 14 | Include a URL (codepen.io, jsfiddle.net, Git repository, codesandbox.io, stackblitz.com, etc.) that demonstrates the problem. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Desktop (please complete the following information):** 20 | - Browser [e.g. chrome, safari] 21 | - Version 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | free-solid-svg-icons: [5.x, 6.x] 12 | fontawesome-svg-core: [1.2.x, 6.x] 13 | node-version: [18.x, 20.x, 22.x] 14 | vue: [3.0.x, 3.1.x, 3.2.x, 3.3.x] 15 | 16 | steps: 17 | - uses: actions/checkout@v1 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | - name: v5 install, build, and test 23 | if: ${{ matrix.free-solid-svg-icons }} == '5.x' 24 | run: | 25 | npm install -g npm@8 26 | npm install 27 | npm install --no-save @fortawesome/fontawesome-svg-core@1.2.x @fortawesome/free-solid-svg-icons@5.x vue@${{ matrix.vue }} 28 | npm run build 29 | npm list vue 30 | npm run test 31 | - name: v6 install, build, and test 32 | if: ${{ matrix.free-solid-svg-icons }} == '6.x' 33 | run: | 34 | npm install -g npm@8 35 | npm install 36 | npm install --no-save @fortawesome/fontawesome-svg-core@6.x @fortawesome/free-solid-svg-icons@6.x vue@${{ matrix.vue }} 37 | npm run build 38 | npm list vue 39 | npm run test 40 | env: 41 | CI: true 42 | - name: dist 43 | run: | 44 | npm run dist 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | 4 | # WebStorm / PHPStorm 5 | .idea 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .keep 2 | node_modules 3 | npm-packages -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSameLine": false, 3 | "htmlWhitespaceSensitivity": "css", 4 | "printWidth": 160, 5 | "quoteProps": "consistent", 6 | "semi": false, 7 | "singleAttributePerLine": true, 8 | "singleQuote": true, 9 | "tabWidth": 2, 10 | "trailingComma": "none", 11 | "useTabs": false 12 | } 13 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 22.2.0 2 | python 3.7.5 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [3.0.8](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.8) - 2024-05-21 8 | 9 | ### Changed 10 | 11 | - Updated `package-lock.json` to help fix faililng tests 12 | 13 | --- 14 | 15 | ## [3.0.7](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.7) - 2024-05-21 16 | 17 | ### Changed 18 | 19 | - Updated nodejs version in `.tool-versions` 20 | - Updated Jest configuration for local testing 21 | - Removed an unused import in `FontAwesomeLayers.test.js` 22 | - Updated `node-versions` in `ci.yml` 23 | - Added additional exports in `index.d.ts` 24 | - Updated `README.md` for new Font Awesome Doc link 25 | 26 | --- 27 | 28 | ## [3.0.6](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.6) - 2024-01-29 29 | 30 | ### Changed 31 | 32 | - Added missing exports to TS definition in `index.d.ts` 33 | 34 | --- 35 | 36 | ## [3.0.5](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.5) - 2023-11-06 37 | 38 | ### Changed 39 | 40 | - Removed .tgz file 41 | 42 | --- 43 | 44 | ## [3.0.4](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.4) - 2023-11-06 45 | 46 | ### Added 47 | 48 | - Tests for Sharp Family 49 | - Ability to include a title prop (github issue #[181](https://github.com/FortAwesome/vue-fontawesome/issues/181)) 50 | - Prettier config file 51 | 52 | --- 53 | 54 | ## [3.0.3](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.3) - 2023-01-24 55 | 56 | ### Changed 57 | 58 | - NPM download `@fortawesome/vue-fontawesome-latest` is now set to Vue 3; to download the Vue 2 package you will have to use `@fortawesome/vue-fontawesome-latest-2`, which is described in our [Vue setup docs](https://fontawesome.com/docs/web/use-with/vue/#_3-add-the-vue-component) 59 | - CI workflow updated for testing 60 | - README.md and package.json updated for new contributor 61 | 62 | ### Added 63 | 64 | - Missing TypeScript animations added (github issue #[428](https://github.com/FortAwesome/vue-fontawesome/issues/428)) 65 | - Missing TypeScript sizes added (github issue #[415](https://github.com/FortAwesome/vue-fontawesome/issues/415)) 66 | 67 | --- 68 | 69 | ## [3.0.2](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.2) - 2022-11-07 70 | 71 | ### Changed 72 | 73 | - Vue 3 Composition API TypeScript support (github issue #[276](https://github.com/FortAwesome/vue-fontawesome/issues/276)) 74 | - Updated README.md with a spelling correction, linter fixes, and new contributor 75 | - Updated icon example in App.vue to use `fixed-width` rather than `full-width` 76 | 77 | ### Added 78 | 79 | - Missing tests 80 | 81 | --- 82 | 83 | ## [3.0.1](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.1) - 2022-06-17 84 | 85 | ### Changed 86 | 87 | - README.md Documentation now points to https://fontawesome.com/docs/web/use-with/vue/ 88 | 89 | --- 90 | 91 | ## [3.0.0](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.0) - 2022-06-07 92 | 93 | ### Added 94 | 95 | - New animations for bounce and shake 96 | - Feature to call icons using a string format 97 | 98 | ### Fixed 99 | 100 | - Animations for beat, fade, beat-fade, flash, spin-pulse, and spin-reverse 101 | - Tee-shirt sizes to include 2xs, lg, xl, and 2xl 102 | - Flip animation when used by itself 103 | 104 | --- 105 | 106 | ## [3.0.0-5](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.0-5) - 2021-10-18 107 | 108 | ### Fixed 109 | 110 | - Include 1.3.0-beta versions in peer dependencies 111 | 112 | --- 113 | 114 | ## [3.0.0-4](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.0-4) - 2021-05-23 115 | 116 | ### Fixed 117 | 118 | - Fix reactivity #297 119 | 120 | --- 121 | 122 | ## [3.0.0-3](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.0-3) - 2020-12-09 123 | 124 | ### Fixed 125 | 126 | - Allow for non-RC releases of Vue 3 127 | 128 | --- 129 | 130 | ## [3.0.0-2](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.0-2) - 2020-10-09 131 | 132 | ### Fixed 133 | 134 | - Make Vue as an external dependency #258 #260 135 | 136 | --- 137 | 138 | ## [3.0.0-1](https://github.com/FortAwesome/vue-fontawesome/releases/tag/3.0.0-1) - 2020-09-01 139 | 140 | ### Added 141 | 142 | - Support for Vue 3.x RC's and betas #246 #249 143 | 144 | --- 145 | 146 | See the [CHANGELOG prior to version 3](https://github.com/FortAwesome/vue-fontawesome/blob/2.x/CHANGELOG.md). 147 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [https://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: https://contributor-covenant.org 74 | [version]: https://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | This is the repository for the _official_ Font Awesome Vue 3 component, _initialized_ by the team behind Font Awesome, 2 | but intended to evolve over time with _community_ contributions. 3 | 4 | # Ways to Contribute 5 | 6 | ## Ask a Question 7 | 8 | Trying to figure out how to make it work? Or how to use it in your scenario? 9 | 10 | 1. Review the [README](README.md) 11 | 1. Get familiar with the documentation for the [SVG with JavaScript](https://fontawesome.com/docs/web/setup/host-yourself/svg-js) implementation, 12 | the framework upon which this component is built. Sometimes the answers you need may be there. 13 | 1. Post any remaining questions on [StackOverflow](https://stackoverflow.com/questions/tagged/vue-fontawesome) with the tag `vue-fontawesome`. 14 | 15 | ## Report a Bug 16 | 17 | 1. Create a test case that reproduces the unexpected behavior using [codesandbox.io](https://codesandbox.io) 18 | 1. [Open a new issue with this template](https://github.com/FortAwesome/vue-fontawesome/issues/new?template=bug-report.md), 19 | and be sure to include a link to the reproduction you made with StackBlitz. 20 | 21 | ## Submit a Pull Request 22 | 23 | Add tests if you add code. 24 | 25 | ## Everything Else 26 | 27 | * [Request a feature](https://github.com/FortAwesome/vue-fontawesome/issues/new??title=Feature%20request:feature-name&template=feature-request.md) 28 | * [Request a new icon](https://github.com/FortAwesome/Font-Awesome/issues/new?title=Icon%20request:%20icon-name&template=icon-request.md) 29 | 30 | # Project Goals 31 | 32 | 1. Achieve and maintain feature parity with Font Awesome's [SVG with JavaScript](https://fontawesome.com/docs/web/setup/host-yourself/svg-js) method. 33 | 34 | 1. Keep with best practices in the Angular development community. 35 | 36 | 1. Stay current with major developments in Vue and Vue-CLI 37 | 38 | 1. Maintain a reasonable level of consistency between this component and the other Font Awesome official JavaScript 39 | framework components ([Angular](https://github.com/FortAwesome/angular-fontawesome), [React](https://github.com/FortAwesome/react-fontawesome), [Ember](https://github.com/FortAwesome/ember-fontawesome)) 40 | 41 | 1. Sharing responsibility: The Font Awesome team will continue to be involved in ongoing development, hoping to _propel_ 42 | the project's momentum as we make _our_ contributions, while minimizing any bottle-necking that may happen as we balance 43 | our own priorities across various projects. Ideally, members of the community will enjoy lending a hand to help keep 44 | the project moving forward by responding to issues, answering questions on StackOverflow, reviewing and merging pull 45 | requests, and publishing npm updates. 46 | 47 | # Code of Conduct 48 | 49 | We'll contribute according to the [Code of Conduct](CODE_OF_CONDUCT.md). 50 | 51 | # Wanted: Core Contributors 52 | 53 | We're seeking core contributors to help drive this project. Core contributors: 54 | 1. Share these goals 55 | 1. Demonstrate competence through contributions 56 | 1. Contribute with conduct fitting with our code of conduct 57 | 1. Want to make this project awesome 58 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Developing vue-fontawesome with Vue 3 2 | 3 | ## Tasks 4 | 5 | The following commands are available through `npm run` or `yarn`: 6 | 7 | | Command | Purpose | 8 | | ------- | ------------------------------------------------------- | 9 | | build | Build a development version of the library using Rollup | 10 | | dist | Build a production version of the library using Rollup | 11 | | test | Execute unit tests | 12 | 13 | ## Release this project 14 | 15 | **During pre release, make sure and use `--tag` and `--npm-dist-tag`** 16 | 17 | 1. Update `package.json` and change `version` 18 | 1. Update `README.md` and `package.json`; adding any contributors 19 | 1. Update the `CHANGELOG.md` 20 | 1. `npm publish --tag latest-3` 21 | 1. `npm publish --tag latest-3 --registry https://npm.fontawesome.com` 22 | 1. `git add .` 23 | 1. `git commit -a -m 'Release VERSION'` 24 | 1. `git push` 25 | 1. Create a [new release](https://github.com/FortAwesome/vue-fontawesome/releases/new) with CHANGELOG details 26 | 27 | ## Authenticating with the npm.fontawesome.com registry 28 | 29 | Contributors with authorization to publish to npm.fontawesome.com will receive an invite 30 | from a Font Awesome project owner. 31 | 32 | 1. Respond to the invite in your email 33 | 1. Let the owner know when you've setup your account 34 | 1. Owner will add you to the team 35 | 36 | You can then run: 37 | 38 | ```bash 39 | npm login --registry https://npm.fontawesome.com 40 | ``` 41 | 42 | - The username is the "slug" for your Cloudsmith account. For example mine is "rob-madole". 43 | - Enter the password that you setup just a few minutes ago. 44 | - It says the your email is PUBLIC. Pretty sure that's false since the auth is through Cloudsmith. 45 | - This doesn't overwrite your standard login, just adds to your `~/.npmrc` 46 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Fonticons, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Official Javascript Component 3 | 4 | 5 | # vue-fontawesome (with Vue 3) 6 | 7 | [![npm](https://img.shields.io/npm/v/@fortawesome/vue-fontawesome.svg?style=flat-square)](https://www.npmjs.com/package/@fortawesome/vue-fontawesome) 8 | [![GitHub Actions Status](https://github.com/FortAwesome/vue-fontawesome/workflows/CI/badge.svg)](https://github.com/FortAwesome/vue-fontawesome/actions) 9 | 10 | > Font Awesome Vue 3 component using SVG with JS 11 | 12 | --- 13 | 14 | 15 | 16 | - [Documentation](#documentation) 17 | - [How to Help](#how-to-help) 18 | - [Contributors](#contributors) 19 | - [Releasing this project (only project owners can do this)](#releasing-this-project-only-project-owners-can-do-this) 20 | 21 | 22 | 23 | ## Documentation 24 | 25 | Official documentation is hosted at [docs.fontawesome.com](https://docs.fontawesome.com/) 26 | 27 | Helpful Vue links: 28 | 29 | - [Set Up With Vue](https://docs.fontawesome.com/web/use-with/vue/) 30 | - [Add Icons with Vue](https://docs.fontawesome.com/web/use-with/vue/add-icons) 31 | - [Adding Icon Styling with Vue](https://docs.fontawesome.com/web/use-with/vue/style) 32 | 33 | ## How to Help 34 | 35 | Review the following docs before diving in: 36 | 37 | - [CONTRIBUTING.md](CONTRIBUTING.md) 38 | - [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) 39 | 40 | And then: 41 | 42 | - Check the existing [issues](https://github.com/FortAwesome/vue-fontawesome/issues) and see if you can help! 43 | 44 | ## Contributors 45 | 46 | The following contributors have either helped to start this project, have contributed 47 | code, are actively maintaining it (including documentation), or in other ways 48 | being awesome contributors to this project. **We'd like to take a moment to recognize them.** 49 | 50 | | | Name | GitHub | 51 | | :------------------------------------------------------------: | ----------------- | --------------------------------------------------------- | 52 | | | SirLamer | [@SirLamer](https://github.com/SirLamer) | 53 | | | Liu Xinyu | [@meteorlxy](https://github.com/meteorlxy) | 54 | | | Xaver Schulz | [@schulz3000](https://github.com/schulz3000) | 55 | | | Yannick Ihmels | [@ihmels](https://github.com/ihmels) | 56 | | | btaens | [@btaens](https://github.com/btaens) | 57 | | | David Driscoll | [@david-driscoll](https://github.com/david-driscoll) | 58 | | | Tyranteon | [@tyranteon](https://github.com/tyranteon) | 59 | | | Romain Failla | [@rigma](https://github.com/rigma) | 60 | | | Vinicius Rangel | [@viniciuslrangel](https://github.com/viniciuslrangel) | 61 | | | Okke Tijhuis | [@otijhuis](https://github.com/otijhuis) | 62 | | | Aaron Parker | [@parkeyparker](https://github.com/parkeyparker) | 63 | | | Brandon Mork | [@brandon-mork](https://github.com/brandon-mork) | 64 | | | Michael Cozzolino | [@michaelcozzolino](https://github.com/michaelcozzolino) | 65 | 66 | If we've missed someone (which is quite likely) submit a Pull Request to us and we'll get it resolved. 67 | 68 | The Font Awesome team: 69 | 70 | | | Name | GitHub | 71 | | :--------------------------------------------------------: | -------------- | -------------------------------------------------- | 72 | | | Travis Chase | [@supercodepoet](https://github.com/supercodepoet) | 73 | | | Rob Madole | [@robmadole](https://github.com/robmadole) | 74 | | | Mike Wilkerson | [@mlwilkerson](https://github.com/mlwilkerson) | 75 | | | Brian Talbot | [@talbs](https://github.com/talbs) | 76 | | | Jason Lundien | [@jasonlundien](https://github.com/jasonlundien) | 77 | 78 | ## Releasing this project (only project owners can do this) 79 | 80 | See [DEVELOPMENT.md](DEVELOPMENT.md#release) 81 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrading Guide 2 | 3 | See the [CHANGELOG.md](./CHANGELOG.md) for detailed information about what has changed between versions. 4 | 5 | This guide is useful to figure out what you need to do between breaking changes. 6 | 7 | As always, [submit issues](https://github.com/FortAwesome/vue-fontawesome/issues/new) that you run into with this guide or with these upgrades to us. 8 | 9 | ## 0.0.x to 0.1.0 10 | 11 | ### Renamed packages 12 | 13 | The following packages have been renamed as part of 5.1.0 of Font Awesome. 14 | 15 | _All packages are in the [@fortawesome NPM scope](https://www.npmjs.com/search?q=scope:fortawesome&page=1&ranking=optimal)_ 16 | 17 | | Old package(1) | New package | 18 | |--------------------------|------------------------| 19 | | fontawesome | fontawesome-svg-core | 20 | | fontawesome-free-solid | free-solid-svg-icons | 21 | | fontawesome-free-regular | free-regular-svg-icons | 22 | | fontawesome-free-brands | free-brands-svg-icons | 23 | | fontawesome-pro-solid | pro-solid-svg-icons | 24 | | fontawesome-pro-regular | pro-regular-svg-icons | 25 | | fontawesome-pro-light | pro-light-svg-icons | 26 | 27 | (1) Old packages have now been deprecated. They are still available but will only receive high priority patch release fixes. 28 | 29 | **You'll need to update your package.json file with the renamed packages and new versions.** 30 | 31 | ### No more default imports 32 | 33 | Recently we spent a good deal of time supporting TypeScript to enable us to 34 | create the Angular Font Awesome component. During that adventure we 35 | [were](https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html) 36 | [convinced](https://blog.neufund.org/why-we-have-banned-default-exports-and-you-should-do-the-same-d51fdc2cf2ad) 37 | that we were going to remove default exports from all of our components, 38 | libraries, and packages. This is complete with the umbrella release of `5.1.0` of Font Awesome. 39 | 40 | What does that mean? 41 | 42 | ~~Old way:~~ 43 | 44 | ```javascript 45 | import fontawesome from '@fortawesome/fontawesome' 46 | import solid from '@fortawesome/fontawesome-free-solid' 47 | import faTwitter from '@fortawesome/fontawesome-free-brands/faTwitter' 48 | import FontAwesomeIcon from '@fortawesome/vue-fontawesome' 49 | 50 | fontawesome.library.add(solid, faTwitter) 51 | ``` 52 | 53 | New way: 54 | 55 | ```javascript 56 | import { library } from '@fortawesome/fontawesome-svg-core' 57 | import { fas } from '@fortawesome/free-solid-svg-icons' 58 | import { faTwitter } from '@fortawesome/free-brands-svg-icons' 59 | import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' 60 | 61 | library.add(fas, faTwitter) 62 | ``` 63 | 64 | This is also a valid way to import icons that works if your tool does not support tree shaking: 65 | 66 | ```javascript 67 | import { faTwitter } from '@fortawesome/free-brands-svg-icons/faTwitter' 68 | ``` 69 | 70 | ### Improved support for tree shaking 71 | 72 | Tree shaking is now functional by default and no additional configuration is required to make it work. 73 | 74 | The `shakable.es.js` module has been removed and is no longer needed. 75 | 76 | If you've previously configured tree shaking by modifying your webpack or rollup you can safely remove these. 77 | 78 | **We recommend that you check your bundle size after upgrading an ensure that file sizes are as you would expect.** 79 | 80 | ```javascript 81 | module.exports = { 82 | // ... 83 | resolve: { 84 | alias: { 85 | '@fortawesome/fontawesome-free-solid$': '@fortawesome/fontawesome-free-solid/shakable.es.js' 86 | } 87 | } 88 | } 89 | ``` 90 | 91 | ```javascript 92 | const alias = require('rollup-plugin-alias') 93 | 94 | rollup({ 95 | // ... 96 | plugins: [ 97 | alias({ 98 | '@fortawesome/fontawesome-free-solid': 'node_modules/@fortawesome/fontawesome-free-solid/shakable.es.js' 99 | }) 100 | ] 101 | }) 102 | ``` 103 | 104 | ### Mixed modes with automatic replacement of `` tags to `` 105 | 106 | If you were previously relying on Font Awesome to replace any `` tags in 107 | your page or app with `` you'll need to explicitly control that now. 108 | 109 | ```javascript 110 | import { dom } from '@fortawesome/fontawesome-svg-core' 111 | 112 | dom.watch() // This will kick of the replacement of i tags and configure a MutationObserver 113 | ``` 114 | -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | yarn watch -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd example/webpack 4 | yarn install 5 | cd - 6 | 7 | yarn install 8 | yarn build 9 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | charset = utf-8 3 | indent_style = space 4 | indent_size = 2 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-essential', 8 | '@vue/standard', 9 | '@vue/typescript/recommended' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2020 13 | }, 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-awesome-example", 3 | "version": "2.0.0", 4 | "description": "A vue-awesome example project", 5 | "private": true, 6 | "scripts": { 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "@fortawesome/fontawesome-svg-core": "^1.2.30", 13 | "@fortawesome/free-brands-svg-icons": "^5.14.0", 14 | "@fortawesome/free-regular-svg-icons": "^5.14.0", 15 | "@fortawesome/free-solid-svg-icons": "^5.14.0", 16 | "@fortawesome/vue-fontawesome": "file:../..", 17 | "core-js": "^3.6.5", 18 | "vue": "^3.0.0-0" 19 | }, 20 | "devDependencies": { 21 | "@typescript-eslint/eslint-plugin": "^2.33.0", 22 | "@typescript-eslint/parser": "^2.33.0", 23 | "@vue/cli-plugin-babel": "~4.5.0", 24 | "@vue/cli-plugin-eslint": "~4.5.0", 25 | "@vue/cli-plugin-typescript": "~4.5.0", 26 | "@vue/cli-service": "~5.0.8", 27 | "@vue/compiler-sfc": "^3.0.0-0", 28 | "@vue/eslint-config-standard": "^5.1.2", 29 | "@vue/eslint-config-typescript": "^5.0.2", 30 | "eslint": "^6.7.2", 31 | "eslint-plugin-import": "^2.20.2", 32 | "eslint-plugin-node": "^11.1.0", 33 | "eslint-plugin-promise": "^4.2.1", 34 | "eslint-plugin-standard": "^4.0.0", 35 | "eslint-plugin-vue": "^7.0.0-0", 36 | "typescript": "~3.9.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FortAwesome/vue-fontawesome/7e7b9a1984765ffb62bf553de4c3f3760810858d/examples/vue-awesome-example/public/favicon.ico -------------------------------------------------------------------------------- /examples/vue-awesome-example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= htmlWebpackPlugin.options.title %> 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/src/App.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 68 | 69 | 75 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { library } from '@fortawesome/fontawesome-svg-core' 3 | import { faCoffee, faChild, faCircle, faArchive, faEnvelope } from '@fortawesome/free-solid-svg-icons' 4 | import { faComment } from '@fortawesome/free-regular-svg-icons' 5 | import { faTwitter } from '@fortawesome/free-brands-svg-icons' 6 | import App from './App.vue' 7 | 8 | library.add( 9 | faCoffee, 10 | faChild, 11 | faCircle, 12 | faArchive, 13 | faEnvelope, 14 | faComment, 15 | faTwitter 16 | ) 17 | 18 | 19 | createApp(App).mount('#app') 20 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { defineComponent } from 'vue' 3 | const component: ReturnType 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "webpack-env" 16 | ], 17 | "paths": { 18 | "@/*": [ 19 | "src/*" 20 | ] 21 | }, 22 | "lib": [ 23 | "esnext", 24 | "dom", 25 | "dom.iterable", 26 | "scripthost" 27 | ] 28 | }, 29 | "include": [ 30 | "src/**/*.ts", 31 | "src/**/*.tsx", 32 | "src/**/*.vue", 33 | "tests/**/*.ts", 34 | "tests/**/*.tsx" 35 | ], 36 | "exclude": [ 37 | "node_modules" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /examples/vue-awesome-example/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | lintOnSave: false 3 | } 4 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { IconDefinition } from '@fortawesome/fontawesome-svg-core' 2 | import { DefineComponent } from 'vue' 3 | 4 | interface FontAwesomeIconProps { 5 | border?: boolean 6 | fixedWidth?: boolean 7 | flip?: 'horizontal' | 'vertical' | 'both' 8 | icon: object | Array | string | IconDefinition 9 | mask?: object | Array | string 10 | maskId?: object | Array | string 11 | listItem?: boolean 12 | pull?: 'right' | 'left' 13 | pulse?: boolean 14 | rotation?: 90 | 180 | 270 | '90' | '180' | '270' 15 | swapOpacity?: boolean 16 | size?: '2xs' | 'xs' | 'sm' | 'lg' | 'xl' | '2xl' | '1x' | '2x' | '3x' | '4x' | '5x' | '6x' | '7x' | '8x' | '9x' | '10x' 17 | spin?: boolean 18 | transform?: object | string 19 | symbol?: boolean | string 20 | title?: string 21 | titleId?: string 22 | inverse?: boolean 23 | bounce?: boolean 24 | shake?: boolean 25 | beat?: boolean 26 | fade?: boolean 27 | beatFade?: boolean 28 | spinPulse?: boolean 29 | spinReverse?: boolean 30 | } 31 | 32 | interface FontAwesomeLayersProps { 33 | fixedWidth?: boolean 34 | } 35 | 36 | interface FontAwesomeLayersTextProps { 37 | value: string | number 38 | transform?: object | string 39 | counter?: boolean 40 | position?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' 41 | } 42 | 43 | declare const FontAwesomeIcon: DefineComponent 44 | declare const FontAwesomeLayers: DefineComponent 45 | declare const FontAwesomeLayersText: DefineComponent 46 | 47 | export { FontAwesomeIcon, FontAwesomeIconProps, FontAwesomeLayers, FontAwesomeLayersProps, FontAwesomeLayersText, FontAwesomeLayersTextProps } 48 | -------------------------------------------------------------------------------- /index.es.js: -------------------------------------------------------------------------------- 1 | import { parse, icon, config, text } from '@fortawesome/fontawesome-svg-core'; 2 | import { h, defineComponent, computed, watch } from 'vue'; 3 | 4 | function ownKeys(e, r) { 5 | var t = Object.keys(e); 6 | if (Object.getOwnPropertySymbols) { 7 | var o = Object.getOwnPropertySymbols(e); 8 | r && (o = o.filter(function (r) { 9 | return Object.getOwnPropertyDescriptor(e, r).enumerable; 10 | })), t.push.apply(t, o); 11 | } 12 | return t; 13 | } 14 | function _objectSpread2(e) { 15 | for (var r = 1; r < arguments.length; r++) { 16 | var t = null != arguments[r] ? arguments[r] : {}; 17 | r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { 18 | _defineProperty(e, r, t[r]); 19 | }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { 20 | Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); 21 | }); 22 | } 23 | return e; 24 | } 25 | function _toPrimitive(t, r) { 26 | if ("object" != typeof t || !t) return t; 27 | var e = t[Symbol.toPrimitive]; 28 | if (void 0 !== e) { 29 | var i = e.call(t, r || "default"); 30 | if ("object" != typeof i) return i; 31 | throw new TypeError("@@toPrimitive must return a primitive value."); 32 | } 33 | return ("string" === r ? String : Number)(t); 34 | } 35 | function _toPropertyKey(t) { 36 | var i = _toPrimitive(t, "string"); 37 | return "symbol" == typeof i ? i : i + ""; 38 | } 39 | function _typeof(o) { 40 | "@babel/helpers - typeof"; 41 | 42 | return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { 43 | return typeof o; 44 | } : function (o) { 45 | return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; 46 | }, _typeof(o); 47 | } 48 | function _defineProperty(obj, key, value) { 49 | key = _toPropertyKey(key); 50 | if (key in obj) { 51 | Object.defineProperty(obj, key, { 52 | value: value, 53 | enumerable: true, 54 | configurable: true, 55 | writable: true 56 | }); 57 | } else { 58 | obj[key] = value; 59 | } 60 | return obj; 61 | } 62 | function _objectWithoutPropertiesLoose(source, excluded) { 63 | if (source == null) return {}; 64 | var target = {}; 65 | for (var key in source) { 66 | if (Object.prototype.hasOwnProperty.call(source, key)) { 67 | if (excluded.indexOf(key) >= 0) continue; 68 | target[key] = source[key]; 69 | } 70 | } 71 | return target; 72 | } 73 | function _objectWithoutProperties(source, excluded) { 74 | if (source == null) return {}; 75 | var target = _objectWithoutPropertiesLoose(source, excluded); 76 | var key, i; 77 | if (Object.getOwnPropertySymbols) { 78 | var sourceSymbolKeys = Object.getOwnPropertySymbols(source); 79 | for (i = 0; i < sourceSymbolKeys.length; i++) { 80 | key = sourceSymbolKeys[i]; 81 | if (excluded.indexOf(key) >= 0) continue; 82 | if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; 83 | target[key] = source[key]; 84 | } 85 | } 86 | return target; 87 | } 88 | function _toConsumableArray(arr) { 89 | return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); 90 | } 91 | function _arrayWithoutHoles(arr) { 92 | if (Array.isArray(arr)) return _arrayLikeToArray(arr); 93 | } 94 | function _iterableToArray(iter) { 95 | if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); 96 | } 97 | function _unsupportedIterableToArray(o, minLen) { 98 | if (!o) return; 99 | if (typeof o === "string") return _arrayLikeToArray(o, minLen); 100 | var n = Object.prototype.toString.call(o).slice(8, -1); 101 | if (n === "Object" && o.constructor) n = o.constructor.name; 102 | if (n === "Map" || n === "Set") return Array.from(o); 103 | if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); 104 | } 105 | function _arrayLikeToArray(arr, len) { 106 | if (len == null || len > arr.length) len = arr.length; 107 | for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; 108 | return arr2; 109 | } 110 | function _nonIterableSpread() { 111 | throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); 112 | } 113 | 114 | var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; 115 | 116 | var humps$1 = {exports: {}}; 117 | 118 | (function (module) { 119 | (function(global) { 120 | 121 | var _processKeys = function(convert, obj, options) { 122 | if(!_isObject(obj) || _isDate(obj) || _isRegExp(obj) || _isBoolean(obj) || _isFunction(obj)) { 123 | return obj; 124 | } 125 | 126 | var output, 127 | i = 0, 128 | l = 0; 129 | 130 | if(_isArray(obj)) { 131 | output = []; 132 | for(l=obj.length; i} classes The class list to convert. 276 | * @returns {Object} 277 | */ 278 | function classToObject(classes) { 279 | return classes.split(/\s+/).reduce(function (output, className) { 280 | output[className] = true; 281 | return output; 282 | }, {}); 283 | } 284 | 285 | /** 286 | * Converts a FontAwesome abstract element of an icon into a Vue VNode. 287 | * @param {AbstractElement | String} abstractElement The element to convert. 288 | * @param {Object} props The user-defined props. 289 | * @param {Object} attrs The user-defined native HTML attributes. 290 | * @returns {VNode} 291 | */ 292 | function convert(abstractElement) { 293 | var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 294 | var attrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 295 | // If the abstract element is a string, we'll just return a string render function 296 | if (typeof abstractElement === 'string') { 297 | return abstractElement; 298 | } 299 | 300 | // Converting abstract element children into Vue VNodes 301 | var children = (abstractElement.children || []).map(function (child) { 302 | return convert(child); 303 | }); 304 | 305 | // Converting abstract element attributes into valid Vue format 306 | var mixins = Object.keys(abstractElement.attributes || {}).reduce(function (mixins, key) { 307 | var value = abstractElement.attributes[key]; 308 | switch (key) { 309 | case 'class': 310 | mixins.class = classToObject(value); 311 | break; 312 | case 'style': 313 | mixins.style = styleToObject(value); 314 | break; 315 | default: 316 | mixins.attrs[key] = value; 317 | } 318 | return mixins; 319 | }, { 320 | attrs: {}, 321 | class: {}, 322 | style: {} 323 | }); 324 | 325 | // Now, we'll return the VNode 326 | attrs.class; 327 | var _attrs$style = attrs.style, 328 | aStyle = _attrs$style === void 0 ? {} : _attrs$style, 329 | otherAttrs = _objectWithoutProperties(attrs, _excluded); 330 | return h(abstractElement.tag, _objectSpread2(_objectSpread2(_objectSpread2({}, props), {}, { 331 | class: mixins.class, 332 | style: _objectSpread2(_objectSpread2({}, mixins.style), aStyle) 333 | }, mixins.attrs), otherAttrs), children); 334 | } 335 | 336 | var PRODUCTION = false; 337 | try { 338 | PRODUCTION = process.env.NODE_ENV === 'production'; 339 | } catch (e) {} 340 | function log () { 341 | if (!PRODUCTION && console && typeof console.error === 'function') { 342 | var _console; 343 | (_console = console).error.apply(_console, arguments); 344 | } 345 | } 346 | 347 | function objectWithKey(key, value) { 348 | return Array.isArray(value) && value.length > 0 || !Array.isArray(value) && value ? _defineProperty({}, key, value) : {}; 349 | } 350 | function classList(props) { 351 | var _classes; 352 | var classes = (_classes = { 353 | 'fa-spin': props.spin, 354 | 'fa-pulse': props.pulse, 355 | 'fa-fw': props.fixedWidth, 356 | 'fa-border': props.border, 357 | 'fa-li': props.listItem, 358 | 'fa-inverse': props.inverse, 359 | 'fa-flip': props.flip === true, 360 | 'fa-flip-horizontal': props.flip === 'horizontal' || props.flip === 'both', 361 | 'fa-flip-vertical': props.flip === 'vertical' || props.flip === 'both' 362 | }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_classes, "fa-".concat(props.size), props.size !== null), "fa-rotate-".concat(props.rotation), props.rotation !== null), "fa-pull-".concat(props.pull), props.pull !== null), 'fa-swap-opacity', props.swapOpacity), 'fa-bounce', props.bounce), 'fa-shake', props.shake), 'fa-beat', props.beat), 'fa-fade', props.fade), 'fa-beat-fade', props.beatFade), 'fa-flash', props.flash), _defineProperty(_defineProperty(_classes, 'fa-spin-pulse', props.spinPulse), 'fa-spin-reverse', props.spinReverse)); 363 | return Object.keys(classes).map(function (key) { 364 | return classes[key] ? key : null; 365 | }).filter(function (key) { 366 | return key; 367 | }); 368 | } 369 | 370 | function normalizeIconArgs(icon) { 371 | if (icon && _typeof(icon) === 'object' && icon.prefix && icon.iconName && icon.icon) { 372 | return icon; 373 | } 374 | if (parse.icon) { 375 | return parse.icon(icon); 376 | } 377 | if (icon === null) { 378 | return null; 379 | } 380 | if (_typeof(icon) === 'object' && icon.prefix && icon.iconName) { 381 | return icon; 382 | } 383 | if (Array.isArray(icon) && icon.length === 2) { 384 | return { 385 | prefix: icon[0], 386 | iconName: icon[1] 387 | }; 388 | } 389 | if (typeof icon === 'string') { 390 | return { 391 | prefix: 'fas', 392 | iconName: icon 393 | }; 394 | } 395 | } 396 | var FontAwesomeIcon = defineComponent({ 397 | name: 'FontAwesomeIcon', 398 | props: { 399 | border: { 400 | type: Boolean, 401 | default: false 402 | }, 403 | fixedWidth: { 404 | type: Boolean, 405 | default: false 406 | }, 407 | flip: { 408 | type: [Boolean, String], 409 | default: false, 410 | validator: function validator(value) { 411 | return [true, false, 'horizontal', 'vertical', 'both'].indexOf(value) > -1; 412 | } 413 | }, 414 | icon: { 415 | type: [Object, Array, String], 416 | required: true 417 | }, 418 | mask: { 419 | type: [Object, Array, String], 420 | default: null 421 | }, 422 | maskId: { 423 | type: String, 424 | default: null 425 | }, 426 | listItem: { 427 | type: Boolean, 428 | default: false 429 | }, 430 | pull: { 431 | type: String, 432 | default: null, 433 | validator: function validator(value) { 434 | return ['right', 'left'].indexOf(value) > -1; 435 | } 436 | }, 437 | pulse: { 438 | type: Boolean, 439 | default: false 440 | }, 441 | rotation: { 442 | type: [String, Number], 443 | default: null, 444 | validator: function validator(value) { 445 | return [90, 180, 270].indexOf(Number.parseInt(value, 10)) > -1; 446 | } 447 | }, 448 | swapOpacity: { 449 | type: Boolean, 450 | default: false 451 | }, 452 | size: { 453 | type: String, 454 | default: null, 455 | validator: function validator(value) { 456 | return ['2xs', 'xs', 'sm', 'lg', 'xl', '2xl', '1x', '2x', '3x', '4x', '5x', '6x', '7x', '8x', '9x', '10x'].indexOf(value) > -1; 457 | } 458 | }, 459 | spin: { 460 | type: Boolean, 461 | default: false 462 | }, 463 | transform: { 464 | type: [String, Object], 465 | default: null 466 | }, 467 | symbol: { 468 | type: [Boolean, String], 469 | default: false 470 | }, 471 | title: { 472 | type: String, 473 | default: null 474 | }, 475 | titleId: { 476 | type: String, 477 | default: null 478 | }, 479 | inverse: { 480 | type: Boolean, 481 | default: false 482 | }, 483 | bounce: { 484 | type: Boolean, 485 | default: false 486 | }, 487 | shake: { 488 | type: Boolean, 489 | default: false 490 | }, 491 | beat: { 492 | type: Boolean, 493 | default: false 494 | }, 495 | fade: { 496 | type: Boolean, 497 | default: false 498 | }, 499 | beatFade: { 500 | type: Boolean, 501 | default: false 502 | }, 503 | flash: { 504 | type: Boolean, 505 | default: false 506 | }, 507 | spinPulse: { 508 | type: Boolean, 509 | default: false 510 | }, 511 | spinReverse: { 512 | type: Boolean, 513 | default: false 514 | } 515 | }, 516 | setup: function setup(props, _ref) { 517 | var attrs = _ref.attrs; 518 | var icon$1 = computed(function () { 519 | return normalizeIconArgs(props.icon); 520 | }); 521 | var classes = computed(function () { 522 | return objectWithKey('classes', classList(props)); 523 | }); 524 | var transform = computed(function () { 525 | return objectWithKey('transform', typeof props.transform === 'string' ? parse.transform(props.transform) : props.transform); 526 | }); 527 | var mask = computed(function () { 528 | return objectWithKey('mask', normalizeIconArgs(props.mask)); 529 | }); 530 | var renderedIcon = computed(function () { 531 | return icon(icon$1.value, _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, classes.value), transform.value), mask.value), {}, { 532 | symbol: props.symbol, 533 | title: props.title, 534 | titleId: props.titleId, 535 | maskId: props.maskId 536 | })); 537 | }); 538 | watch(renderedIcon, function (value) { 539 | if (!value) { 540 | return log('Could not find one or more icon(s)', icon$1.value, mask.value); 541 | } 542 | }, { 543 | immediate: true 544 | }); 545 | var vnode = computed(function () { 546 | return renderedIcon.value ? convert(renderedIcon.value.abstract[0], {}, attrs) : null; 547 | }); 548 | return function () { 549 | return vnode.value; 550 | }; 551 | } 552 | }); 553 | 554 | var FontAwesomeLayers = defineComponent({ 555 | name: 'FontAwesomeLayers', 556 | props: { 557 | fixedWidth: { 558 | type: Boolean, 559 | default: false 560 | } 561 | }, 562 | setup: function setup(props, _ref) { 563 | var slots = _ref.slots; 564 | var familyPrefix = config.familyPrefix; 565 | var className = computed(function () { 566 | return ["".concat(familyPrefix, "-layers")].concat(_toConsumableArray(props.fixedWidth ? ["".concat(familyPrefix, "-fw")] : [])); 567 | }); 568 | return function () { 569 | return h('div', { 570 | class: className.value 571 | }, slots.default ? slots.default() : []); 572 | }; 573 | } 574 | }); 575 | 576 | var FontAwesomeLayersText = defineComponent({ 577 | name: 'FontAwesomeLayersText', 578 | props: { 579 | value: { 580 | type: [String, Number], 581 | default: '' 582 | }, 583 | transform: { 584 | type: [String, Object], 585 | default: null 586 | }, 587 | counter: { 588 | type: Boolean, 589 | default: false 590 | }, 591 | position: { 592 | type: String, 593 | default: null, 594 | validator: function validator(value) { 595 | return ['bottom-left', 'bottom-right', 'top-left', 'top-right'].indexOf(value) > -1; 596 | } 597 | } 598 | }, 599 | setup: function setup(props, _ref) { 600 | var attrs = _ref.attrs; 601 | var familyPrefix = config.familyPrefix; 602 | var classes = computed(function () { 603 | return objectWithKey('classes', [].concat(_toConsumableArray(props.counter ? ["".concat(familyPrefix, "-layers-counter")] : []), _toConsumableArray(props.position ? ["".concat(familyPrefix, "-layers-").concat(props.position)] : []))); 604 | }); 605 | var transform = computed(function () { 606 | return objectWithKey('transform', typeof props.transform === 'string' ? parse.transform(props.transform) : props.transform); 607 | }); 608 | var abstractElement = computed(function () { 609 | var _text = text(props.value.toString(), _objectSpread2(_objectSpread2({}, transform.value), classes.value)), 610 | abstract = _text.abstract; 611 | if (props.counter) { 612 | abstract[0].attributes.class = abstract[0].attributes.class.replace('fa-layers-text', ''); 613 | } 614 | return abstract[0]; 615 | }); 616 | var vnode = computed(function () { 617 | return convert(abstractElement.value, {}, attrs); 618 | }); 619 | return function () { 620 | return vnode.value; 621 | }; 622 | } 623 | }); 624 | 625 | export { FontAwesomeIcon, FontAwesomeLayers, FontAwesomeLayersText }; 626 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fortawesome/fontawesome-svg-core'), require('vue')) : 3 | typeof define === 'function' && define.amd ? define(['exports', '@fortawesome/fontawesome-svg-core', 'vue'], factory) : 4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["vue-fontawesome"] = {}, global.FontAwesome, global.vue)); 5 | })(this, (function (exports, fontawesomeSvgCore, vue) { 'use strict'; 6 | 7 | function ownKeys(e, r) { 8 | var t = Object.keys(e); 9 | if (Object.getOwnPropertySymbols) { 10 | var o = Object.getOwnPropertySymbols(e); 11 | r && (o = o.filter(function (r) { 12 | return Object.getOwnPropertyDescriptor(e, r).enumerable; 13 | })), t.push.apply(t, o); 14 | } 15 | return t; 16 | } 17 | function _objectSpread2(e) { 18 | for (var r = 1; r < arguments.length; r++) { 19 | var t = null != arguments[r] ? arguments[r] : {}; 20 | r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { 21 | _defineProperty(e, r, t[r]); 22 | }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { 23 | Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); 24 | }); 25 | } 26 | return e; 27 | } 28 | function _toPrimitive(t, r) { 29 | if ("object" != typeof t || !t) return t; 30 | var e = t[Symbol.toPrimitive]; 31 | if (void 0 !== e) { 32 | var i = e.call(t, r || "default"); 33 | if ("object" != typeof i) return i; 34 | throw new TypeError("@@toPrimitive must return a primitive value."); 35 | } 36 | return ("string" === r ? String : Number)(t); 37 | } 38 | function _toPropertyKey(t) { 39 | var i = _toPrimitive(t, "string"); 40 | return "symbol" == typeof i ? i : i + ""; 41 | } 42 | function _typeof(o) { 43 | "@babel/helpers - typeof"; 44 | 45 | return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { 46 | return typeof o; 47 | } : function (o) { 48 | return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; 49 | }, _typeof(o); 50 | } 51 | function _defineProperty(obj, key, value) { 52 | key = _toPropertyKey(key); 53 | if (key in obj) { 54 | Object.defineProperty(obj, key, { 55 | value: value, 56 | enumerable: true, 57 | configurable: true, 58 | writable: true 59 | }); 60 | } else { 61 | obj[key] = value; 62 | } 63 | return obj; 64 | } 65 | function _objectWithoutPropertiesLoose(source, excluded) { 66 | if (source == null) return {}; 67 | var target = {}; 68 | for (var key in source) { 69 | if (Object.prototype.hasOwnProperty.call(source, key)) { 70 | if (excluded.indexOf(key) >= 0) continue; 71 | target[key] = source[key]; 72 | } 73 | } 74 | return target; 75 | } 76 | function _objectWithoutProperties(source, excluded) { 77 | if (source == null) return {}; 78 | var target = _objectWithoutPropertiesLoose(source, excluded); 79 | var key, i; 80 | if (Object.getOwnPropertySymbols) { 81 | var sourceSymbolKeys = Object.getOwnPropertySymbols(source); 82 | for (i = 0; i < sourceSymbolKeys.length; i++) { 83 | key = sourceSymbolKeys[i]; 84 | if (excluded.indexOf(key) >= 0) continue; 85 | if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; 86 | target[key] = source[key]; 87 | } 88 | } 89 | return target; 90 | } 91 | function _toConsumableArray(arr) { 92 | return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); 93 | } 94 | function _arrayWithoutHoles(arr) { 95 | if (Array.isArray(arr)) return _arrayLikeToArray(arr); 96 | } 97 | function _iterableToArray(iter) { 98 | if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); 99 | } 100 | function _unsupportedIterableToArray(o, minLen) { 101 | if (!o) return; 102 | if (typeof o === "string") return _arrayLikeToArray(o, minLen); 103 | var n = Object.prototype.toString.call(o).slice(8, -1); 104 | if (n === "Object" && o.constructor) n = o.constructor.name; 105 | if (n === "Map" || n === "Set") return Array.from(o); 106 | if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); 107 | } 108 | function _arrayLikeToArray(arr, len) { 109 | if (len == null || len > arr.length) len = arr.length; 110 | for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; 111 | return arr2; 112 | } 113 | function _nonIterableSpread() { 114 | throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); 115 | } 116 | 117 | var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; 118 | 119 | var humps$1 = {exports: {}}; 120 | 121 | (function (module) { 122 | (function(global) { 123 | 124 | var _processKeys = function(convert, obj, options) { 125 | if(!_isObject(obj) || _isDate(obj) || _isRegExp(obj) || _isBoolean(obj) || _isFunction(obj)) { 126 | return obj; 127 | } 128 | 129 | var output, 130 | i = 0, 131 | l = 0; 132 | 133 | if(_isArray(obj)) { 134 | output = []; 135 | for(l=obj.length; i} classes The class list to convert. 279 | * @returns {Object} 280 | */ 281 | function classToObject(classes) { 282 | return classes.split(/\s+/).reduce(function (output, className) { 283 | output[className] = true; 284 | return output; 285 | }, {}); 286 | } 287 | 288 | /** 289 | * Converts a FontAwesome abstract element of an icon into a Vue VNode. 290 | * @param {AbstractElement | String} abstractElement The element to convert. 291 | * @param {Object} props The user-defined props. 292 | * @param {Object} attrs The user-defined native HTML attributes. 293 | * @returns {VNode} 294 | */ 295 | function convert(abstractElement) { 296 | var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 297 | var attrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 298 | // If the abstract element is a string, we'll just return a string render function 299 | if (typeof abstractElement === 'string') { 300 | return abstractElement; 301 | } 302 | 303 | // Converting abstract element children into Vue VNodes 304 | var children = (abstractElement.children || []).map(function (child) { 305 | return convert(child); 306 | }); 307 | 308 | // Converting abstract element attributes into valid Vue format 309 | var mixins = Object.keys(abstractElement.attributes || {}).reduce(function (mixins, key) { 310 | var value = abstractElement.attributes[key]; 311 | switch (key) { 312 | case 'class': 313 | mixins.class = classToObject(value); 314 | break; 315 | case 'style': 316 | mixins.style = styleToObject(value); 317 | break; 318 | default: 319 | mixins.attrs[key] = value; 320 | } 321 | return mixins; 322 | }, { 323 | attrs: {}, 324 | class: {}, 325 | style: {} 326 | }); 327 | 328 | // Now, we'll return the VNode 329 | attrs.class; 330 | var _attrs$style = attrs.style, 331 | aStyle = _attrs$style === void 0 ? {} : _attrs$style, 332 | otherAttrs = _objectWithoutProperties(attrs, _excluded); 333 | return vue.h(abstractElement.tag, _objectSpread2(_objectSpread2(_objectSpread2({}, props), {}, { 334 | class: mixins.class, 335 | style: _objectSpread2(_objectSpread2({}, mixins.style), aStyle) 336 | }, mixins.attrs), otherAttrs), children); 337 | } 338 | 339 | var PRODUCTION = false; 340 | try { 341 | PRODUCTION = process.env.NODE_ENV === 'production'; 342 | } catch (e) {} 343 | function log () { 344 | if (!PRODUCTION && console && typeof console.error === 'function') { 345 | var _console; 346 | (_console = console).error.apply(_console, arguments); 347 | } 348 | } 349 | 350 | function objectWithKey(key, value) { 351 | return Array.isArray(value) && value.length > 0 || !Array.isArray(value) && value ? _defineProperty({}, key, value) : {}; 352 | } 353 | function classList(props) { 354 | var _classes; 355 | var classes = (_classes = { 356 | 'fa-spin': props.spin, 357 | 'fa-pulse': props.pulse, 358 | 'fa-fw': props.fixedWidth, 359 | 'fa-border': props.border, 360 | 'fa-li': props.listItem, 361 | 'fa-inverse': props.inverse, 362 | 'fa-flip': props.flip === true, 363 | 'fa-flip-horizontal': props.flip === 'horizontal' || props.flip === 'both', 364 | 'fa-flip-vertical': props.flip === 'vertical' || props.flip === 'both' 365 | }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_classes, "fa-".concat(props.size), props.size !== null), "fa-rotate-".concat(props.rotation), props.rotation !== null), "fa-pull-".concat(props.pull), props.pull !== null), 'fa-swap-opacity', props.swapOpacity), 'fa-bounce', props.bounce), 'fa-shake', props.shake), 'fa-beat', props.beat), 'fa-fade', props.fade), 'fa-beat-fade', props.beatFade), 'fa-flash', props.flash), _defineProperty(_defineProperty(_classes, 'fa-spin-pulse', props.spinPulse), 'fa-spin-reverse', props.spinReverse)); 366 | return Object.keys(classes).map(function (key) { 367 | return classes[key] ? key : null; 368 | }).filter(function (key) { 369 | return key; 370 | }); 371 | } 372 | 373 | function normalizeIconArgs(icon) { 374 | if (icon && _typeof(icon) === 'object' && icon.prefix && icon.iconName && icon.icon) { 375 | return icon; 376 | } 377 | if (fontawesomeSvgCore.parse.icon) { 378 | return fontawesomeSvgCore.parse.icon(icon); 379 | } 380 | if (icon === null) { 381 | return null; 382 | } 383 | if (_typeof(icon) === 'object' && icon.prefix && icon.iconName) { 384 | return icon; 385 | } 386 | if (Array.isArray(icon) && icon.length === 2) { 387 | return { 388 | prefix: icon[0], 389 | iconName: icon[1] 390 | }; 391 | } 392 | if (typeof icon === 'string') { 393 | return { 394 | prefix: 'fas', 395 | iconName: icon 396 | }; 397 | } 398 | } 399 | var FontAwesomeIcon = vue.defineComponent({ 400 | name: 'FontAwesomeIcon', 401 | props: { 402 | border: { 403 | type: Boolean, 404 | default: false 405 | }, 406 | fixedWidth: { 407 | type: Boolean, 408 | default: false 409 | }, 410 | flip: { 411 | type: [Boolean, String], 412 | default: false, 413 | validator: function validator(value) { 414 | return [true, false, 'horizontal', 'vertical', 'both'].indexOf(value) > -1; 415 | } 416 | }, 417 | icon: { 418 | type: [Object, Array, String], 419 | required: true 420 | }, 421 | mask: { 422 | type: [Object, Array, String], 423 | default: null 424 | }, 425 | maskId: { 426 | type: String, 427 | default: null 428 | }, 429 | listItem: { 430 | type: Boolean, 431 | default: false 432 | }, 433 | pull: { 434 | type: String, 435 | default: null, 436 | validator: function validator(value) { 437 | return ['right', 'left'].indexOf(value) > -1; 438 | } 439 | }, 440 | pulse: { 441 | type: Boolean, 442 | default: false 443 | }, 444 | rotation: { 445 | type: [String, Number], 446 | default: null, 447 | validator: function validator(value) { 448 | return [90, 180, 270].indexOf(Number.parseInt(value, 10)) > -1; 449 | } 450 | }, 451 | swapOpacity: { 452 | type: Boolean, 453 | default: false 454 | }, 455 | size: { 456 | type: String, 457 | default: null, 458 | validator: function validator(value) { 459 | return ['2xs', 'xs', 'sm', 'lg', 'xl', '2xl', '1x', '2x', '3x', '4x', '5x', '6x', '7x', '8x', '9x', '10x'].indexOf(value) > -1; 460 | } 461 | }, 462 | spin: { 463 | type: Boolean, 464 | default: false 465 | }, 466 | transform: { 467 | type: [String, Object], 468 | default: null 469 | }, 470 | symbol: { 471 | type: [Boolean, String], 472 | default: false 473 | }, 474 | title: { 475 | type: String, 476 | default: null 477 | }, 478 | titleId: { 479 | type: String, 480 | default: null 481 | }, 482 | inverse: { 483 | type: Boolean, 484 | default: false 485 | }, 486 | bounce: { 487 | type: Boolean, 488 | default: false 489 | }, 490 | shake: { 491 | type: Boolean, 492 | default: false 493 | }, 494 | beat: { 495 | type: Boolean, 496 | default: false 497 | }, 498 | fade: { 499 | type: Boolean, 500 | default: false 501 | }, 502 | beatFade: { 503 | type: Boolean, 504 | default: false 505 | }, 506 | flash: { 507 | type: Boolean, 508 | default: false 509 | }, 510 | spinPulse: { 511 | type: Boolean, 512 | default: false 513 | }, 514 | spinReverse: { 515 | type: Boolean, 516 | default: false 517 | } 518 | }, 519 | setup: function setup(props, _ref) { 520 | var attrs = _ref.attrs; 521 | var icon = vue.computed(function () { 522 | return normalizeIconArgs(props.icon); 523 | }); 524 | var classes = vue.computed(function () { 525 | return objectWithKey('classes', classList(props)); 526 | }); 527 | var transform = vue.computed(function () { 528 | return objectWithKey('transform', typeof props.transform === 'string' ? fontawesomeSvgCore.parse.transform(props.transform) : props.transform); 529 | }); 530 | var mask = vue.computed(function () { 531 | return objectWithKey('mask', normalizeIconArgs(props.mask)); 532 | }); 533 | var renderedIcon = vue.computed(function () { 534 | return fontawesomeSvgCore.icon(icon.value, _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, classes.value), transform.value), mask.value), {}, { 535 | symbol: props.symbol, 536 | title: props.title, 537 | titleId: props.titleId, 538 | maskId: props.maskId 539 | })); 540 | }); 541 | vue.watch(renderedIcon, function (value) { 542 | if (!value) { 543 | return log('Could not find one or more icon(s)', icon.value, mask.value); 544 | } 545 | }, { 546 | immediate: true 547 | }); 548 | var vnode = vue.computed(function () { 549 | return renderedIcon.value ? convert(renderedIcon.value.abstract[0], {}, attrs) : null; 550 | }); 551 | return function () { 552 | return vnode.value; 553 | }; 554 | } 555 | }); 556 | 557 | var FontAwesomeLayers = vue.defineComponent({ 558 | name: 'FontAwesomeLayers', 559 | props: { 560 | fixedWidth: { 561 | type: Boolean, 562 | default: false 563 | } 564 | }, 565 | setup: function setup(props, _ref) { 566 | var slots = _ref.slots; 567 | var familyPrefix = fontawesomeSvgCore.config.familyPrefix; 568 | var className = vue.computed(function () { 569 | return ["".concat(familyPrefix, "-layers")].concat(_toConsumableArray(props.fixedWidth ? ["".concat(familyPrefix, "-fw")] : [])); 570 | }); 571 | return function () { 572 | return vue.h('div', { 573 | class: className.value 574 | }, slots.default ? slots.default() : []); 575 | }; 576 | } 577 | }); 578 | 579 | var FontAwesomeLayersText = vue.defineComponent({ 580 | name: 'FontAwesomeLayersText', 581 | props: { 582 | value: { 583 | type: [String, Number], 584 | default: '' 585 | }, 586 | transform: { 587 | type: [String, Object], 588 | default: null 589 | }, 590 | counter: { 591 | type: Boolean, 592 | default: false 593 | }, 594 | position: { 595 | type: String, 596 | default: null, 597 | validator: function validator(value) { 598 | return ['bottom-left', 'bottom-right', 'top-left', 'top-right'].indexOf(value) > -1; 599 | } 600 | } 601 | }, 602 | setup: function setup(props, _ref) { 603 | var attrs = _ref.attrs; 604 | var familyPrefix = fontawesomeSvgCore.config.familyPrefix; 605 | var classes = vue.computed(function () { 606 | return objectWithKey('classes', [].concat(_toConsumableArray(props.counter ? ["".concat(familyPrefix, "-layers-counter")] : []), _toConsumableArray(props.position ? ["".concat(familyPrefix, "-layers-").concat(props.position)] : []))); 607 | }); 608 | var transform = vue.computed(function () { 609 | return objectWithKey('transform', typeof props.transform === 'string' ? fontawesomeSvgCore.parse.transform(props.transform) : props.transform); 610 | }); 611 | var abstractElement = vue.computed(function () { 612 | var _text = fontawesomeSvgCore.text(props.value.toString(), _objectSpread2(_objectSpread2({}, transform.value), classes.value)), 613 | abstract = _text.abstract; 614 | if (props.counter) { 615 | abstract[0].attributes.class = abstract[0].attributes.class.replace('fa-layers-text', ''); 616 | } 617 | return abstract[0]; 618 | }); 619 | var vnode = vue.computed(function () { 620 | return convert(abstractElement.value, {}, attrs); 621 | }); 622 | return function () { 623 | return vnode.value; 624 | }; 625 | } 626 | }); 627 | 628 | exports.FontAwesomeIcon = FontAwesomeIcon; 629 | exports.FontAwesomeLayers = FontAwesomeLayers; 630 | exports.FontAwesomeLayersText = FontAwesomeLayersText; 631 | 632 | Object.defineProperty(exports, '__esModule', { value: true }); 633 | 634 | })); 635 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fortawesome/vue-fontawesome", 3 | "description": "Official Vue component for Font Awesome 6", 4 | "version": "3.0.8", 5 | "main": "index.js", 6 | "files": [ 7 | "README.md", 8 | "LICENSE.txt", 9 | "UPGRADING.md", 10 | "CHANGELOG.md", 11 | "index.d.ts", 12 | "index.es.js", 13 | "index.js", 14 | "src/components/**.js", 15 | "src/**.js" 16 | ], 17 | "jest": { 18 | "verbose": true, 19 | "testEnvironmentOptions": { 20 | "customExportConditions": [ 21 | "node", 22 | "node-addons" 23 | ] 24 | } 25 | }, 26 | "module": "index.es.js", 27 | "jsnext:main": "index.es.js", 28 | "types": "index.d.ts", 29 | "homepage": "https://github.com/FortAwesome/vue-fontawesome", 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/FortAwesome/vue-fontawesome.git" 33 | }, 34 | "contributors": [ 35 | "Aaron Parker ", 36 | "Brandon Mork ", 37 | "btaens ", 38 | "David Driscoll ", 39 | "Liu Xinyu ", 40 | "Michael Cozzolino ", 41 | "Okke Tijhuis ", 42 | "Romain Failla ", 43 | "SirLamer ", 44 | "Tyranteon ", 45 | "Vinicius Rangel ", 46 | "Xaver Schulz ", 47 | "Yannick Ihmels ", 48 | "Brian Talbot ", 49 | "Jason Lundien ", 50 | "Mike Wilkerson ", 51 | "Rob Madole ", 52 | "Travis Chase " 53 | ], 54 | "license": "MIT", 55 | "scripts": { 56 | "build": "rollup -c rollup.config.js", 57 | "dist": "cross-env NODE_ENV=production npm run build", 58 | "test": "jest --silent", 59 | "prepack": "npm run dist" 60 | }, 61 | "lint-staged": { 62 | "README.md": [ 63 | "markdown-toc -i", 64 | "git add" 65 | ] 66 | }, 67 | "peerDependencies": { 68 | "@fortawesome/fontawesome-svg-core": "~1 || ~6", 69 | "vue": ">= 3.0.0 < 4" 70 | }, 71 | "devDependencies": { 72 | "@babel/core": "^7.18.2", 73 | "@babel/plugin-external-helpers": "^7.17.12", 74 | "@babel/plugin-proposal-class-properties": "^7.17.12", 75 | "@babel/plugin-proposal-json-strings": "^7.17.12", 76 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 77 | "@babel/plugin-syntax-import-meta": "^7.10.4", 78 | "@babel/preset-env": "^7.18.2", 79 | "@fortawesome/fontawesome-svg-core": "~6", 80 | "@fortawesome/free-solid-svg-icons": "^6", 81 | "@rollup/plugin-babel": "^5.3.1", 82 | "@rollup/plugin-commonjs": "^22.0.0", 83 | "@rollup/plugin-node-resolve": "^13.3.0", 84 | "@vue/test-utils": "^2.0.0-beta.2", 85 | "babel-core": "^7.0.0-bridge.0", 86 | "babel-jest": "^28.1.1", 87 | "concurrently": "^7.2.1", 88 | "cross-env": "^7.0.3", 89 | "humps": "^2.0.1", 90 | "husky": "^8.0.1", 91 | "install": "^0.13.0", 92 | "jest": "^28.1.1", 93 | "jest-environment-jsdom": "^28.1.1", 94 | "lint-staged": "^13.0.0", 95 | "markdown-toc": "^1.2.0", 96 | "npm": "^10.2.2", 97 | "prettier": "^3.0.3", 98 | "rollup": "^2.75.6", 99 | "vue": "^3" 100 | }, 101 | "husky": { 102 | "hooks": { 103 | "pre-commit": "lint-staged" 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve' 2 | import commonJs from '@rollup/plugin-commonjs' 3 | import babel from '@rollup/plugin-babel' 4 | 5 | const name = 'vue-fontawesome' 6 | const globals = { 7 | '@fortawesome/fontawesome-svg-core': 'FontAwesome' 8 | } 9 | 10 | export default { 11 | external: [ 12 | '@fortawesome/fontawesome-svg-core', 13 | 'vue', 14 | ], 15 | input: 'src/index.js', 16 | output: [ 17 | { 18 | name, 19 | globals, 20 | format: 'umd', 21 | exports: 'named', 22 | file: 'index.js', 23 | }, 24 | { 25 | name, 26 | globals, 27 | format: 'es', 28 | file: 'index.es.js' 29 | } 30 | ], 31 | plugins: [ 32 | resolve({ 33 | jsnext: true, 34 | main: true 35 | }), 36 | commonJs({ 37 | include: 'node_modules/**' 38 | }), 39 | babel({ 40 | babelrc: false, 41 | presets: [ 42 | ['@babel/preset-env', { 43 | debug: true, 44 | targets: {"browsers": ["> 1%", "last 2 versions", "ie > 9"]}, 45 | modules: false 46 | }] 47 | ], 48 | exclude: 'node_modules/**' 49 | }) 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /src/components/FontAwesomeIcon.js: -------------------------------------------------------------------------------- 1 | import { parse as faParse, icon as faIcon } from '@fortawesome/fontawesome-svg-core' 2 | import { defineComponent, computed, watch } from 'vue' 3 | import convert from '../converter' 4 | import log from '../logger' 5 | import { objectWithKey, classList } from '../utils' 6 | 7 | function normalizeIconArgs(icon) { 8 | if (icon && typeof icon === 'object' && icon.prefix && icon.iconName && icon.icon) { 9 | return icon 10 | } 11 | 12 | if (faParse.icon) { 13 | return faParse.icon(icon) 14 | } 15 | 16 | if (icon === null) { 17 | return null 18 | } 19 | 20 | if (typeof icon === 'object' && icon.prefix && icon.iconName) { 21 | return icon 22 | } 23 | 24 | if (Array.isArray(icon) && icon.length === 2) { 25 | return { prefix: icon[0], iconName: icon[1] } 26 | } 27 | 28 | if (typeof icon === 'string') { 29 | return { prefix: 'fas', iconName: icon } 30 | } 31 | } 32 | 33 | export default defineComponent({ 34 | name: 'FontAwesomeIcon', 35 | 36 | props: { 37 | border: { 38 | type: Boolean, 39 | default: false 40 | }, 41 | fixedWidth: { 42 | type: Boolean, 43 | default: false 44 | }, 45 | flip: { 46 | type: [Boolean, String], 47 | default: false, 48 | validator: (value) => [true, false, 'horizontal', 'vertical', 'both'].indexOf(value) > -1 49 | }, 50 | icon: { 51 | type: [Object, Array, String], 52 | required: true 53 | }, 54 | mask: { 55 | type: [Object, Array, String], 56 | default: null 57 | }, 58 | maskId: { 59 | type: String, 60 | default: null 61 | }, 62 | listItem: { 63 | type: Boolean, 64 | default: false 65 | }, 66 | pull: { 67 | type: String, 68 | default: null, 69 | validator: (value) => ['right', 'left'].indexOf(value) > -1 70 | }, 71 | pulse: { 72 | type: Boolean, 73 | default: false 74 | }, 75 | rotation: { 76 | type: [String, Number], 77 | default: null, 78 | validator: (value) => [90, 180, 270].indexOf(Number.parseInt(value, 10)) > -1 79 | }, 80 | swapOpacity: { 81 | type: Boolean, 82 | default: false 83 | }, 84 | size: { 85 | type: String, 86 | default: null, 87 | validator: (value) => ['2xs', 'xs', 'sm', 'lg', 'xl', '2xl', '1x', '2x', '3x', '4x', '5x', '6x', '7x', '8x', '9x', '10x'].indexOf(value) > -1 88 | }, 89 | spin: { 90 | type: Boolean, 91 | default: false 92 | }, 93 | transform: { 94 | type: [String, Object], 95 | default: null 96 | }, 97 | symbol: { 98 | type: [Boolean, String], 99 | default: false 100 | }, 101 | title: { 102 | type: String, 103 | default: null 104 | }, 105 | titleId: { 106 | type: String, 107 | default: null 108 | }, 109 | inverse: { 110 | type: Boolean, 111 | default: false 112 | }, 113 | bounce: { 114 | type: Boolean, 115 | default: false 116 | }, 117 | shake: { 118 | type: Boolean, 119 | default: false 120 | }, 121 | beat: { 122 | type: Boolean, 123 | default: false 124 | }, 125 | fade: { 126 | type: Boolean, 127 | default: false 128 | }, 129 | beatFade: { 130 | type: Boolean, 131 | default: false 132 | }, 133 | flash: { 134 | type: Boolean, 135 | default: false 136 | }, 137 | spinPulse: { 138 | type: Boolean, 139 | default: false 140 | }, 141 | spinReverse: { 142 | type: Boolean, 143 | default: false 144 | } 145 | }, 146 | 147 | setup(props, { attrs }) { 148 | const icon = computed(() => normalizeIconArgs(props.icon)) 149 | const classes = computed(() => objectWithKey('classes', classList(props))) 150 | const transform = computed(() => objectWithKey('transform', typeof props.transform === 'string' ? faParse.transform(props.transform) : props.transform)) 151 | const mask = computed(() => objectWithKey('mask', normalizeIconArgs(props.mask))) 152 | 153 | const renderedIcon = computed(() => 154 | faIcon(icon.value, { 155 | ...classes.value, 156 | ...transform.value, 157 | ...mask.value, 158 | symbol: props.symbol, 159 | title: props.title, 160 | titleId: props.titleId, 161 | maskId: props.maskId 162 | }) 163 | ) 164 | 165 | watch( 166 | renderedIcon, 167 | (value) => { 168 | if (!value) { 169 | return log('Could not find one or more icon(s)', icon.value, mask.value) 170 | } 171 | }, 172 | { immediate: true } 173 | ) 174 | 175 | const vnode = computed(() => (renderedIcon.value ? convert(renderedIcon.value.abstract[0], {}, attrs) : null)) 176 | return () => vnode.value 177 | } 178 | }) 179 | -------------------------------------------------------------------------------- /src/components/FontAwesomeLayers.js: -------------------------------------------------------------------------------- 1 | import { config } from '@fortawesome/fontawesome-svg-core' 2 | import { defineComponent, h, computed } from 'vue' 3 | 4 | export default defineComponent({ 5 | name: 'FontAwesomeLayers', 6 | 7 | props: { 8 | fixedWidth: { 9 | type: Boolean, 10 | default: false 11 | } 12 | }, 13 | 14 | setup (props, { slots }) { 15 | const { familyPrefix } = config 16 | 17 | const className = computed(() => [ 18 | `${familyPrefix}-layers`, 19 | ...(props.fixedWidth ? [`${familyPrefix}-fw`] : []) 20 | ]) 21 | 22 | return () => h('div', { class: className.value }, slots.default ? slots.default() : []) 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /src/components/FontAwesomeLayersText.js: -------------------------------------------------------------------------------- 1 | import { config, parse, text } from '@fortawesome/fontawesome-svg-core' 2 | import { defineComponent, computed } from 'vue' 3 | import convert from '../converter' 4 | import { objectWithKey } from '../utils' 5 | 6 | export default defineComponent({ 7 | name: 'FontAwesomeLayersText', 8 | 9 | props: { 10 | value: { 11 | type: [String, Number], 12 | default: '' 13 | }, 14 | transform: { 15 | type: [String, Object], 16 | default: null 17 | }, 18 | counter: { 19 | type: Boolean, 20 | default: false 21 | }, 22 | position: { 23 | type: String, 24 | default: null, 25 | validator: (value) => ['bottom-left', 'bottom-right', 'top-left', 'top-right'].indexOf(value) > -1 26 | }, 27 | }, 28 | 29 | setup (props, { attrs }) { 30 | const { familyPrefix } = config 31 | 32 | const classes = computed(() => objectWithKey('classes', [ 33 | ...(props.counter ? [`${familyPrefix}-layers-counter`] : []), 34 | ...(props.position ? [`${familyPrefix}-layers-${props.position}`] : []) 35 | ])) 36 | const transform = computed(() => objectWithKey('transform', 37 | typeof props.transform === 'string' ? parse.transform(props.transform) : props.transform)) 38 | const abstractElement = computed(() => { 39 | const { abstract } = text(props.value.toString(), { ...transform.value, ...classes.value }) 40 | if (props.counter) { 41 | abstract[0].attributes.class = abstract[0].attributes.class.replace('fa-layers-text', '') 42 | } 43 | return abstract[0] 44 | }) 45 | 46 | const vnode = computed(() => convert(abstractElement.value, {}, attrs)) 47 | return () => vnode.value 48 | } 49 | }) 50 | -------------------------------------------------------------------------------- /src/components/__fixtures__/helpers.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import FontAwesomeIcon from '../FontAwesomeIcon' 3 | import { parse } from '@fortawesome/fontawesome-svg-core' 4 | 5 | export function compileAndMount(definition, props = {}) { 6 | return mount(definition, { props }) 7 | } 8 | 9 | export function mountFromProps(props = {}) { 10 | return mount(FontAwesomeIcon, { props }) 11 | } 12 | 13 | export function coreHasFeature(feature) { 14 | if (feature === REFERENCE_ICON_BY_STYLE || feature === ICON_ALIASES || feature === REFERENCE_ICON_USING_STRING || feature === REFERENCE_ICON_USING_FAMILY) { 15 | return parse.icon 16 | } 17 | } 18 | 19 | export const REFERENCE_ICON_BY_STYLE = 0x00 20 | export const ICON_ALIASES = 0x01 21 | export const REFERENCE_ICON_USING_STRING = 0x02 22 | export const REFERENCE_ICON_USING_FAMILY = 0x03 23 | -------------------------------------------------------------------------------- /src/components/__fixtures__/icons.js: -------------------------------------------------------------------------------- 1 | export const faCoffee = { 2 | prefix: 'fas', 3 | iconName: 'coffee', 4 | icon: [640, 512, [], 'f001', '...'] 5 | } 6 | 7 | export const faCircle = { 8 | prefix: 'fas', 9 | iconName: 'circle', 10 | icon: [640, 512, [], 'f002', '...'] 11 | } 12 | 13 | export const faAlien = { 14 | prefix: 'fad', 15 | iconName: 'alien', 16 | icon: [640, 512, [], 'f003', '...'] 17 | } 18 | 19 | export const faDog = { 20 | prefix: 'fass', 21 | iconName: 'dog', 22 | icon: [640, 512, [], 'f200', '...'] 23 | } 24 | -------------------------------------------------------------------------------- /src/components/__tests__/FontAwesomeIcon.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | 5 | import { library } from '@fortawesome/fontawesome-svg-core' 6 | import { faClose, faUser } from '@fortawesome/free-solid-svg-icons' 7 | import { faCoffee, faCircle, faAlien, faDog } from '../__fixtures__/icons' 8 | import { 9 | compileAndMount, 10 | mountFromProps, 11 | coreHasFeature, 12 | REFERENCE_ICON_USING_STRING, 13 | REFERENCE_ICON_BY_STYLE, 14 | REFERENCE_ICON_USING_FAMILY, 15 | ICON_ALIASES 16 | } from '../__fixtures__/helpers' 17 | import FontAwesomeIcon from '../FontAwesomeIcon' 18 | 19 | beforeEach(() => { 20 | library.add(faCoffee, faCircle, faAlien, faDog) 21 | }) 22 | 23 | afterEach(() => { 24 | library.reset() 25 | }) 26 | 27 | describe('icon title prop', () => { 28 | test('checks title attribute is null when title property is NOT set', () => { 29 | const wrapper = mountFromProps({ 30 | icon: faCoffee 31 | }) 32 | 33 | expect(wrapper.element.getAttribute('aria-labelledby')).toBeFalsy() 34 | expect(wrapper.element.querySelector('title')).toBeFalsy() 35 | }) 36 | 37 | test('checks title attributes when title property is set', () => { 38 | const wrapper = mountFromProps({ 39 | icon: faCoffee, 40 | title: 'Drink your caf', 41 | titleId: 'caf-1138' 42 | }) 43 | 44 | expect(wrapper.element.getAttribute('aria-labelledby')).toBe('svg-inline--fa-title-caf-1138') 45 | expect(wrapper.element.querySelector('title').textContent).toBe('Drink your caf') 46 | expect(wrapper.element.querySelector('title').id).toBe('svg-inline--fa-title-caf-1138') 47 | }) 48 | }) 49 | 50 | describe('icons are showing', () => { 51 | test('using array format, short prefix and short icon name', () => { 52 | const wrapper = mountFromProps({ icon: ['fas', 'coffee'] }) 53 | 54 | expect(wrapper.element.tagName).toBe('svg') 55 | expect(wrapper.element.classList.contains('fa-coffee')).toBeTruthy() 56 | }) 57 | 58 | if (coreHasFeature(REFERENCE_ICON_BY_STYLE)) { 59 | test('using array format, short prefix and long icon name', () => { 60 | const wrapper = mountFromProps({ icon: ['fas', 'fa-coffee'] }) 61 | 62 | expect(wrapper.element.tagName).toBe('svg') 63 | expect(wrapper.element.classList.contains('fa-coffee')).toBeTruthy() 64 | }) 65 | 66 | test('using array format, long prefix and long icon name', () => { 67 | const wrapper = mountFromProps({ icon: ['fa-solid', 'fa-coffee'] }) 68 | 69 | expect(wrapper.element.tagName).toBe('svg') 70 | expect(wrapper.element.classList.contains('fa-coffee')).toBeTruthy() 71 | }) 72 | 73 | test('using array format, long prefix and short icon name', () => { 74 | const wrapper = mountFromProps({ icon: ['fa-duotone', 'alien'] }) 75 | 76 | expect(wrapper.element.tagName).toBe('svg') 77 | expect(wrapper.element.classList.contains('fa-alien')).toBeTruthy() 78 | }) 79 | } 80 | 81 | if (coreHasFeature(REFERENCE_ICON_USING_STRING)) { 82 | test('using string format, with long prefix and long icon name', () => { 83 | const wrapper = mountFromProps({ icon: 'fa-duotone fa-alien' }) 84 | 85 | expect(wrapper.element.tagName).toBe('svg') 86 | expect(wrapper.element.classList.contains('fa-alien')).toBeTruthy() 87 | }) 88 | 89 | test('using string format, with short prefix and long icon name', () => { 90 | const wrapper = mountFromProps({ icon: 'fad fa-alien' }) 91 | 92 | expect(wrapper.element.tagName).toBe('svg') 93 | expect(wrapper.element.classList.contains('fa-alien')).toBeTruthy() 94 | }) 95 | } 96 | 97 | test('using string format, icon name only', () => { 98 | const wrapper = mountFromProps({ icon: 'coffee' }) 99 | 100 | expect(wrapper.element.tagName).toBe('svg') 101 | expect(wrapper.element.classList.contains('fa-coffee')).toBeTruthy() 102 | }) 103 | 104 | test('missing icon', () => { 105 | const wrapper = mountFromProps({ icon: ['fas', 'noicon'] }) 106 | 107 | expect(wrapper.element.tagName).toBeFalsy() 108 | }) 109 | 110 | test('using iconDefinition', () => { 111 | const wrapper = mountFromProps({ icon: faCoffee }) 112 | 113 | expect(wrapper.element.tagName).toBe('svg') 114 | expect(wrapper.element.classList.contains('fa-coffee')).toBeTruthy() 115 | }) 116 | }) 117 | 118 | describe('unrelated Vue data options', () => { 119 | test('with extra static class', () => { 120 | const wrapper = compileAndMount({ 121 | template: '', 122 | data() { 123 | return { icon: faCoffee } 124 | }, 125 | components: { 126 | FontAwesomeIcon 127 | } 128 | }) 129 | 130 | expect(wrapper.element.classList.contains('extra')).toBeTruthy() 131 | }) 132 | 133 | test('with extra bound class', () => { 134 | const wrapper = compileAndMount({ 135 | template: ``, 136 | data() { 137 | return { icon: faCoffee } 138 | }, 139 | components: { 140 | FontAwesomeIcon 141 | } 142 | }) 143 | 144 | expect(wrapper.element.classList.contains('extra1')).toBeTruthy() 145 | expect(wrapper.element.classList.contains('extra2')).toBeTruthy() 146 | }) 147 | 148 | test('with extra style', () => { 149 | const wrapper = compileAndMount({ 150 | template: ``, 151 | data() { 152 | return { icon: faCoffee } 153 | }, 154 | components: { 155 | FontAwesomeIcon 156 | } 157 | }) 158 | 159 | expect(wrapper.element.style.getPropertyValue('font-size')).toBe('42px') 160 | }) 161 | 162 | test('with extra DOM property', () => { 163 | const wrapper = compileAndMount({ 164 | template: ``, 165 | data() { 166 | return { icon: faCoffee } 167 | }, 168 | components: { 169 | FontAwesomeIcon 170 | } 171 | }) 172 | 173 | expect(wrapper.element.getAttribute('rel')).toBe('local') 174 | }) 175 | 176 | test('with listener', async () => { 177 | let hasBeenClicked = false 178 | 179 | const wrapper = compileAndMount({ 180 | template: '', 181 | data() { 182 | return { icon: faCoffee } 183 | }, 184 | methods: { 185 | clicked() { 186 | hasBeenClicked = true 187 | } 188 | }, 189 | components: { 190 | FontAwesomeIcon 191 | } 192 | }) 193 | 194 | expect(hasBeenClicked).toBeFalsy() 195 | await wrapper.trigger('click') 196 | expect(hasBeenClicked).toBeTruthy() 197 | }) 198 | }) 199 | 200 | test('using border', () => { 201 | const wrapper = mountFromProps({ icon: faCoffee, border: true }) 202 | 203 | expect(wrapper.element.classList.contains('fa-border')).toBeTruthy() 204 | }) 205 | 206 | test('using fixedWidth', () => { 207 | const wrapper = mountFromProps({ icon: faCoffee, fixedWidth: true }) 208 | 209 | expect(wrapper.element.classList.contains('fa-fw')).toBeTruthy() 210 | }) 211 | 212 | describe('using flip', () => { 213 | test('flip', () => { 214 | const wrapper = mountFromProps({ icon: faCoffee, flip: true }) 215 | 216 | expect(wrapper.element.classList.contains('fa-flip')).toBeTruthy() 217 | expect(wrapper.element.classList.contains('fa-flip-vertical')).toBeFalsy() 218 | expect(wrapper.element.classList.contains('fa-flip-horizontal')).toBeFalsy() 219 | expect(wrapper.element.classList.contains('fa-flip-both')).toBeFalsy() 220 | }) 221 | 222 | test('horizontal', () => { 223 | const wrapper = mountFromProps({ icon: faCoffee, flip: 'horizontal' }) 224 | 225 | expect(wrapper.element.classList.contains('fa-flip-horizontal')).toBeTruthy() 226 | expect(wrapper.element.classList.contains('fa-flip-vertical')).toBeFalsy() 227 | expect(wrapper.element.classList.contains('fa-flip-both')).toBeFalsy() 228 | expect(wrapper.element.classList.contains('fa-flip')).toBeFalsy() 229 | }) 230 | 231 | test('vertical', () => { 232 | const wrapper = mountFromProps({ icon: faCoffee, flip: 'vertical' }) 233 | 234 | expect(wrapper.element.classList.contains('fa-flip-vertical')).toBeTruthy() 235 | expect(wrapper.element.classList.contains('fa-flip-horizontal')).toBeFalsy() 236 | expect(wrapper.element.classList.contains('fa-flip-both')).toBeFalsy() 237 | expect(wrapper.element.classList.contains('fa-flip')).toBeFalsy() 238 | }) 239 | 240 | test('both', () => { 241 | const wrapper = mountFromProps({ icon: faCoffee, flip: 'both' }) 242 | 243 | expect(wrapper.element.classList.contains('fa-flip-horizontal')).toBeTruthy() 244 | expect(wrapper.element.classList.contains('fa-flip-vertical')).toBeTruthy() 245 | expect(wrapper.element.classList.contains('fa-flip')).toBeFalsy() 246 | }) 247 | }) 248 | 249 | test('using listItem', () => { 250 | const wrapper = mountFromProps({ icon: faCoffee, listItem: true }) 251 | 252 | expect(wrapper.element.classList.contains('fa-li')).toBeTruthy() 253 | }) 254 | 255 | describe('using pull', () => { 256 | test('right', () => { 257 | const wrapper = mountFromProps({ icon: faCoffee, pull: 'right' }) 258 | 259 | expect(wrapper.element.classList.contains('fa-pull-right')).toBeTruthy() 260 | }) 261 | 262 | test('left', () => { 263 | const wrapper = mountFromProps({ icon: faCoffee, pull: 'left' }) 264 | 265 | expect(wrapper.element.classList.contains('fa-pull-left')).toBeTruthy() 266 | }) 267 | }) 268 | 269 | test('using pulse', () => { 270 | const wrapper = mountFromProps({ icon: faCoffee, pulse: true }) 271 | 272 | expect(wrapper.element.classList.contains('fa-pulse')).toBeTruthy() 273 | }) 274 | 275 | describe('using rotation', () => { 276 | test('90', () => { 277 | const wrapper = mountFromProps({ icon: faCoffee, rotation: 90 }) 278 | 279 | expect(wrapper.element.classList.contains('fa-rotate-90')).toBeTruthy() 280 | }) 281 | 282 | test('180', () => { 283 | const wrapper = mountFromProps({ icon: faCoffee, rotation: 180 }) 284 | 285 | expect(wrapper.element.classList.contains('fa-rotate-180')).toBeTruthy() 286 | }) 287 | 288 | test('270', () => { 289 | const wrapper = mountFromProps({ icon: faCoffee, rotation: 270 }) 290 | 291 | expect(wrapper.element.classList.contains('fa-rotate-270')).toBeTruthy() 292 | }) 293 | 294 | test('as a string', () => { 295 | const wrapper = mountFromProps({ icon: faCoffee, rotation: '90' }) 296 | 297 | expect(wrapper.element.classList.contains('fa-rotate-90')).toBeTruthy() 298 | }) 299 | }) 300 | 301 | test('swap opacity', () => { 302 | const wrapper = mountFromProps({ icon: faCoffee, swapOpacity: true }) 303 | 304 | expect(wrapper.element.classList.contains('fa-swap-opacity')).toBeTruthy() 305 | }) 306 | 307 | test('using size', () => { 308 | ;['2xs', 'xs', 'sm', 'lg', 'xl', '2xl', '1x', '2x', '3x', '4x', '5x', '6x', '7x', '8x', '9x', '10x'].forEach((size) => { 309 | const wrapper = mountFromProps({ icon: faCoffee, size: size }) 310 | 311 | expect(wrapper.element.classList.contains(`fa-${size}`)).toBeTruthy() 312 | }) 313 | }) 314 | 315 | test('using spin', () => { 316 | const wrapper = mountFromProps({ icon: faCoffee, spin: true }) 317 | 318 | expect(wrapper.element.classList.contains('fa-spin')).toBeTruthy() 319 | }) 320 | 321 | test('using inverse', () => { 322 | const wrapper = mountFromProps({ icon: faCoffee, inverse: true }) 323 | 324 | expect(wrapper.element.classList.contains('fa-inverse')).toBeTruthy() 325 | }) 326 | 327 | describe('using transform', () => { 328 | test('string', () => { 329 | const wrapper = mountFromProps({ 330 | icon: faCoffee, 331 | transform: 'grow-40 left-4 rotate-15' 332 | }) 333 | 334 | expect(wrapper.element).toBeTruthy() 335 | }) 336 | 337 | test('object', () => { 338 | const wrapper = mountFromProps({ 339 | icon: faCoffee, 340 | transform: { 341 | flipX: false, 342 | flipY: false, 343 | rotate: 15, 344 | size: 56, 345 | x: -4, 346 | y: 0 347 | } 348 | }) 349 | 350 | expect(wrapper.element).toBeTruthy() 351 | }) 352 | }) 353 | 354 | describe('mask', () => { 355 | test('will add icon', () => { 356 | const wrapper = mountFromProps({ icon: faCoffee, mask: faCircle }) 357 | 358 | expect(wrapper.element.innerHTML).toMatch(/clipPath/) 359 | }) 360 | }) 361 | 362 | describe('symbol', () => { 363 | test('will not create a symbol', () => { 364 | const wrapper = mountFromProps({ icon: faCoffee }) 365 | 366 | expect(wrapper.element.style.getPropertyValue('display')).toBe('') 367 | }) 368 | 369 | test('will create a symbol', () => { 370 | const wrapper = mountFromProps({ icon: faCoffee, symbol: 'coffee-icon' }) 371 | 372 | expect(wrapper.element.style.getPropertyValue('display')).toBe('none') 373 | expect(wrapper.element.children[0].tagName).toBe('symbol') 374 | }) 375 | }) 376 | 377 | describe('title', () => { 378 | test('using title', () => { 379 | const wrapper = mountFromProps({ icon: faCoffee, title: 'Coffee' }) 380 | 381 | expect(wrapper.element.getElementsByTagName('title')[0].innerHTML).toBe('Coffee') 382 | }) 383 | 384 | test('not using title', () => { 385 | const wrapper = mountFromProps({ icon: faCoffee }) 386 | 387 | expect(wrapper.element.getElementsByTagName('title').length).toBe(0) 388 | }) 389 | }) 390 | 391 | describe('reactivity', () => { 392 | test('changing props should update the element', async () => { 393 | const wrapper = mountFromProps({ icon: faCoffee, title: 'Coffee' }) 394 | 395 | expect(wrapper.element.classList.contains('fa-coffee')).toBeTruthy() 396 | expect(wrapper.element.getElementsByTagName('title')[0].innerHTML).toBe('Coffee') 397 | 398 | await wrapper.setProps({ icon: faCircle, title: 'Circle' }) 399 | 400 | expect(wrapper.element.classList.contains('fa-circle')).toBeTruthy() 401 | expect(wrapper.element.getElementsByTagName('title')[0].innerHTML).toBe('Circle') 402 | }) 403 | }) 404 | 405 | describe('using bounce', () => { 406 | test('bounce', () => { 407 | const wrapper = mountFromProps({ icon: faCoffee, bounce: true }) 408 | 409 | expect(wrapper.element.classList.contains('fa-bounce')).toBeTruthy() 410 | }) 411 | }) 412 | 413 | describe('using shake', () => { 414 | test('shake', () => { 415 | const wrapper = mountFromProps({ icon: faCoffee, shake: true }) 416 | 417 | expect(wrapper.element.classList.contains('fa-shake')).toBeTruthy() 418 | }) 419 | }) 420 | 421 | describe('using beat', () => { 422 | test('beat', () => { 423 | const wrapper = mountFromProps({ icon: faCoffee, beat: true }) 424 | 425 | expect(wrapper.element.classList.contains('fa-beat')).toBeTruthy() 426 | }) 427 | }) 428 | 429 | describe('using fade', () => { 430 | test('fade', () => { 431 | const wrapper = mountFromProps({ icon: faCoffee, fade: true }) 432 | 433 | expect(wrapper.element.classList.contains('fa-fade')).toBeTruthy() 434 | }) 435 | }) 436 | 437 | describe('using beat-fade', () => { 438 | test('beat-fade', () => { 439 | const wrapper = mountFromProps({ icon: faCoffee, beatFade: true }) 440 | 441 | expect(wrapper.element.classList.contains('fa-beat-fade')).toBeTruthy() 442 | }) 443 | }) 444 | 445 | describe('using flash', () => { 446 | test('flash', () => { 447 | const wrapper = mountFromProps({ icon: faCoffee, flash: true }) 448 | 449 | expect(wrapper.element.classList.contains('fa-flash')).toBeTruthy() 450 | }) 451 | }) 452 | 453 | describe('using spin-pulse', () => { 454 | test('spin-pulse', () => { 455 | const wrapper = mountFromProps({ icon: faCoffee, spinPulse: true }) 456 | 457 | expect(wrapper.element.classList.contains('fa-spin-pulse')).toBeTruthy() 458 | }) 459 | }) 460 | 461 | describe('using spin-reverse', () => { 462 | test('spin-reverse', () => { 463 | const wrapper = mountFromProps({ icon: faCoffee, spinReverse: true }) 464 | 465 | expect(wrapper.element.classList.contains('fa-spin-reverse')).toBeTruthy() 466 | }) 467 | }) 468 | 469 | test('using imported object from svg icons package', () => { 470 | const wrapper = mountFromProps({ icon: faUser }) 471 | 472 | expect(wrapper.element.tagName).toBe('svg') 473 | }) 474 | 475 | if (coreHasFeature(ICON_ALIASES)) { 476 | test('find a free-solid-svg-icon with array format', () => { 477 | library.reset() 478 | library.add(faClose) 479 | const wrapper = mountFromProps({ icon: ['fas', 'xmark'] }) 480 | 481 | expect(wrapper.element.tagName).toBe('svg') 482 | expect(wrapper.element.classList.contains('fa-xmark')).toBeTruthy() 483 | }) 484 | 485 | test('find a free-solid-svg-icon that is an alias', () => { 486 | library.reset() 487 | library.add(faClose) 488 | const wrapper = mountFromProps({ icon: ['fas', 'close'] }) 489 | 490 | expect(wrapper.element.tagName).toBe('svg') 491 | expect(wrapper.element.classList.contains('fa-xmark')).toBeTruthy() 492 | }) 493 | } 494 | 495 | describe('using a family', () => { 496 | if (coreHasFeature(REFERENCE_ICON_USING_FAMILY)) { 497 | test('will find a sharp solid icon using array format, short prefix, and short icon name', () => { 498 | const wrapper = mountFromProps({ icon: ['fass', 'dog'] }) 499 | 500 | expect(wrapper.element.tagName).toBe('svg') 501 | expect(wrapper.element.classList.contains('fa-dog')).toBeTruthy() 502 | }) 503 | 504 | test('will find a sharp solid icon using array format, short prefix, and long fa-icon name', () => { 505 | const wrapper = mountFromProps({ icon: ['fass', 'fa-dog'] }) 506 | 507 | expect(wrapper.element.tagName).toBe('svg') 508 | expect(wrapper.element.classList.contains('fa-dog')).toBeTruthy() 509 | }) 510 | 511 | test('will find a sharp solid icon using string format, short prefix, and long fa-icon name', () => { 512 | const wrapper = mountFromProps({ icon: 'fass fa-dog' }) 513 | 514 | expect(wrapper.element.tagName).toBe('svg') 515 | expect(wrapper.element.classList.contains('fa-dog')).toBeTruthy() 516 | }) 517 | 518 | test('will default to a sharp solid icon using string format, long prefix, and long fa-icon name', () => { 519 | const wrapper = mountFromProps({ icon: 'fa-sharp fa-dog' }) 520 | 521 | expect(wrapper.element.tagName).toBe('svg') 522 | expect(wrapper.element.classList.contains('fa-dog')).toBeTruthy() 523 | }) 524 | 525 | test('will find a sharp solid icon using string format, long prefix, long style, and long fa-icon name', () => { 526 | const wrapper = mountFromProps({ icon: 'fa-sharp fa-solid fa-dog' }) 527 | 528 | expect(wrapper.element.tagName).toBe('svg') 529 | expect(wrapper.element.classList.contains('fa-dog')).toBeTruthy() 530 | }) 531 | } 532 | }) 533 | -------------------------------------------------------------------------------- /src/components/__tests__/FontAwesomeLayers.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | 5 | import { library } from '@fortawesome/fontawesome-svg-core' 6 | import { faCoffee, faCircle } from '../__fixtures__/icons' 7 | import { compileAndMount } from '../__fixtures__/helpers' 8 | import FontAwesomeLayers from '../FontAwesomeLayers' 9 | 10 | beforeEach(() => { 11 | library.add(faCoffee, faCircle) 12 | }) 13 | 14 | test('empty layers', () => { 15 | const wrapper = compileAndMount({ 16 | template: '', 17 | components: { 18 | FontAwesomeLayers 19 | } 20 | }) 21 | 22 | expect(wrapper.element.children.length).toBe(0) 23 | }) 24 | 25 | test('empty layers', () => { 26 | const wrapper = compileAndMount({ 27 | template: '', 28 | components: { 29 | FontAwesomeLayers 30 | } 31 | }) 32 | 33 | expect(wrapper.element.children.length).toBe(2) 34 | }) 35 | 36 | describe('class handling', () => { 37 | test('extra static', () => { 38 | const wrapper = compileAndMount({ 39 | template: '', 40 | components: { 41 | FontAwesomeLayers 42 | } 43 | }) 44 | 45 | expect(wrapper.element.classList.contains('extra')).toBeTruthy() 46 | expect(wrapper.element.classList.contains('fa-layers')).toBeTruthy() 47 | }) 48 | 49 | test('extra bound', () => { 50 | const wrapper = compileAndMount({ 51 | template: ``, 52 | components: { 53 | FontAwesomeLayers 54 | } 55 | }) 56 | 57 | expect(wrapper.element.getAttribute('class')).toBe('fa-layers extra') 58 | }) 59 | 60 | test('fixed width', () => { 61 | const wrapper = compileAndMount({ 62 | template: '', 63 | components: { 64 | FontAwesomeLayers 65 | } 66 | }) 67 | 68 | expect(wrapper.element.getAttribute('class')).toBe('fa-layers fa-fw') 69 | }) 70 | }) 71 | 72 | describe('reactivity', () => { 73 | test('changing props should update the element', async () => { 74 | const wrapper = compileAndMount({ 75 | template: '', 76 | components: { 77 | FontAwesomeLayers 78 | } 79 | }) 80 | 81 | expect(wrapper.element.getAttribute('class')).toBe('fa-layers fa-fw') 82 | 83 | await wrapper.setProps({ fixedWidth: false }) 84 | 85 | expect(wrapper.element.getAttribute('class')).toBe('fa-layers') 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /src/components/__tests__/FontAwesomeLayersText.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | 5 | import { compileAndMount } from '../__fixtures__/helpers' 6 | import FontAwesomeLayersText from '../FontAwesomeLayersText' 7 | 8 | test('empty', () => { 9 | const wrapper = compileAndMount({ 10 | template: '', 11 | components: { 12 | FontAwesomeLayersText 13 | } 14 | }) 15 | 16 | expect(wrapper.element.tagName).toBe('SPAN') 17 | }) 18 | 19 | test('simple text', () => { 20 | const wrapper = compileAndMount({ 21 | template: '', 22 | components: { 23 | FontAwesomeLayersText 24 | } 25 | }) 26 | 27 | expect(wrapper.element.getAttribute('class')).toBe('fa-layers-text') 28 | expect(wrapper.element.innerHTML).toBe('Test') 29 | }) 30 | 31 | test('accept number for value', () => { 32 | const wrapper = compileAndMount({ 33 | template: '', 34 | components: { 35 | FontAwesomeLayersText 36 | } 37 | }) 38 | 39 | expect(wrapper.element.getAttribute('class')).toBe('fa-layers-text') 40 | expect(wrapper.element.innerHTML).toBe('42') 41 | }) 42 | 43 | describe('transform', () => { 44 | test('string', () => { 45 | const wrapper = compileAndMount({ 46 | template: '', 47 | components: { 48 | FontAwesomeLayersText 49 | } 50 | }) 51 | 52 | // It appears the jsdom doesn't set the transform for this, not sure why 53 | expect(wrapper.element) 54 | }) 55 | }) 56 | 57 | describe('counter', () => { 58 | test('simple', () => { 59 | const wrapper = compileAndMount({ 60 | template: '', 61 | components: { 62 | FontAwesomeLayersText 63 | } 64 | }) 65 | 66 | expect(wrapper.element.getAttribute('class')).toBe('fa-layers-counter') 67 | expect(wrapper.element.getAttribute('class')).not.toBe('fa-layers-text') 68 | expect(wrapper.element.innerHTML).toBe('42') 69 | }) 70 | 71 | test('position', () => { 72 | const wrapper = compileAndMount({ 73 | template: '', 74 | components: { 75 | FontAwesomeLayersText 76 | } 77 | }) 78 | 79 | expect(wrapper.element.getAttribute('class')).toBe('fa-layers-counter fa-layers-bottom-right') 80 | }) 81 | }) 82 | 83 | describe('reactivity', () => { 84 | test('changing props should update the element', async () => { 85 | const wrapper = compileAndMount({ 86 | template: '', 87 | components: { 88 | FontAwesomeLayersText 89 | } 90 | }) 91 | 92 | expect(wrapper.element.innerHTML).toBe('42') 93 | 94 | await wrapper.setProps({ value: 43 }) 95 | 96 | expect(wrapper.element.innerHTML).toBe('43') 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /src/converter.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue' 2 | import humps from 'humps' 3 | 4 | /** 5 | * Converts a CSS style into a plain Javascript object. 6 | * @param {String} style The style to converts into a plain Javascript object. 7 | * @returns {Object} 8 | */ 9 | function styleToObject (style) { 10 | return style.split(';') 11 | .map(s => s.trim()) 12 | .filter(s => s) 13 | .reduce( 14 | (output, pair) => { 15 | const idx = pair.indexOf(':') 16 | const prop = humps.camelize(pair.slice(0, idx)) 17 | const value = pair.slice(idx + 1).trim() 18 | 19 | output[prop] = value 20 | return output 21 | }, 22 | {} 23 | ) 24 | } 25 | 26 | /** 27 | * Converts a CSS class list into a plain Javascript object. 28 | * @param {Array} classes The class list to convert. 29 | * @returns {Object} 30 | */ 31 | function classToObject (classes) { 32 | return classes.split(/\s+/) 33 | .reduce( 34 | (output, className) => { 35 | output[className] = true 36 | return output 37 | }, 38 | {} 39 | ) 40 | } 41 | 42 | /** 43 | * Combines collections of style classes into one collection 44 | * @param {Array} collections The collections to combine. 45 | * @returns {Array} 46 | */ 47 | function combineClassObjects (...collections) { 48 | const finalSet = new Set([]) 49 | 50 | collections.forEach(set => { 51 | if (Array.isArray(set)) { 52 | finalSet.forEach(className => finalSet.add(className)) 53 | } else { 54 | finalSet.add(set) 55 | } 56 | }) 57 | 58 | return Array.from(finalSet) 59 | } 60 | 61 | /** 62 | * Converts a FontAwesome abstract element of an icon into a Vue VNode. 63 | * @param {AbstractElement | String} abstractElement The element to convert. 64 | * @param {Object} props The user-defined props. 65 | * @param {Object} attrs The user-defined native HTML attributes. 66 | * @returns {VNode} 67 | */ 68 | export default function convert (abstractElement, props = {}, attrs = {}) { 69 | // If the abstract element is a string, we'll just return a string render function 70 | if (typeof abstractElement === 'string') { 71 | return abstractElement 72 | } 73 | 74 | // Converting abstract element children into Vue VNodes 75 | const children = (abstractElement.children || []) 76 | .map(child => convert(child)) 77 | 78 | // Converting abstract element attributes into valid Vue format 79 | const mixins = Object.keys(abstractElement.attributes || {}) 80 | .reduce( 81 | (mixins, key) => { 82 | const value = abstractElement.attributes[key] 83 | 84 | switch (key) { 85 | case 'class': 86 | mixins.class = classToObject(value) 87 | break 88 | case 'style': 89 | mixins.style = styleToObject(value) 90 | break 91 | default: 92 | mixins.attrs[key] = value 93 | } 94 | 95 | return mixins 96 | }, 97 | { 98 | attrs: {}, 99 | class: {}, 100 | style: {} 101 | } 102 | ) 103 | 104 | // Now, we'll return the VNode 105 | const { class: _aClass = {}, style: aStyle = {}, ...otherAttrs } = attrs 106 | 107 | return h( 108 | abstractElement.tag, 109 | { 110 | ...props, 111 | class: mixins.class, 112 | style: { ...mixins.style, ...aStyle }, 113 | ...mixins.attrs, 114 | ...otherAttrs 115 | }, 116 | children 117 | ) 118 | } 119 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default as FontAwesomeIcon } from './components/FontAwesomeIcon' 2 | export { default as FontAwesomeLayers } from './components/FontAwesomeLayers' 3 | export { default as FontAwesomeLayersText } from './components/FontAwesomeLayersText' 4 | -------------------------------------------------------------------------------- /src/logger.js: -------------------------------------------------------------------------------- 1 | let PRODUCTION = false 2 | 3 | try { 4 | PRODUCTION = process.env.NODE_ENV === 'production' 5 | } catch (e) { } 6 | 7 | export default function (...args) { 8 | if (!PRODUCTION && console && typeof console.error === 'function') { 9 | console.error(...args) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | export function objectWithKey (key, value) { 2 | return ((Array.isArray(value) && value.length > 0) || (!Array.isArray(value) && value)) ? {[key]: value} : {} 3 | } 4 | 5 | export function classList (props) { 6 | let classes = { 7 | 'fa-spin': props.spin, 8 | 'fa-pulse': props.pulse, 9 | 'fa-fw': props.fixedWidth, 10 | 'fa-border': props.border, 11 | 'fa-li': props.listItem, 12 | 'fa-inverse': props.inverse, 13 | 'fa-flip': props.flip === true, 14 | 'fa-flip-horizontal': props.flip === 'horizontal' || props.flip === 'both', 15 | 'fa-flip-vertical': props.flip === 'vertical' || props.flip === 'both', 16 | [`fa-${props.size}`]: props.size !== null, 17 | [`fa-rotate-${props.rotation}`]: props.rotation !== null, 18 | [`fa-pull-${props.pull}`]: props.pull !== null, 19 | 'fa-swap-opacity': props.swapOpacity, 20 | 'fa-bounce': props.bounce, 21 | 'fa-shake': props.shake, 22 | 'fa-beat': props.beat, 23 | 'fa-fade': props.fade, 24 | 'fa-beat-fade': props.beatFade, 25 | 'fa-flash': props.flash, 26 | 'fa-spin-pulse': props.spinPulse, 27 | 'fa-spin-reverse': props.spinReverse 28 | } 29 | 30 | return Object.keys(classes) 31 | .map(key => classes[key] ? key : null) 32 | .filter(key => key) 33 | } 34 | 35 | export function addStaticClass(to, what) { 36 | const val = (to || '').length === 0 ? [] : [to] 37 | 38 | return val.concat(what).join(' ') 39 | } 40 | --------------------------------------------------------------------------------