├── .editorconfig ├── .eslintrc.yml ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── issue-template.md ├── LICENSE └── workflows │ ├── test-build-deploy.yml │ └── test-pull-request.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.yml ├── .stylelintrc.json ├── README.md ├── package-lock.json ├── package.json ├── src ├── components.d.ts ├── components │ ├── accordion │ │ ├── blaze-accordion-pane.tsx │ │ ├── blaze-accordion.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ └── snapshots.e2e.ts │ ├── address │ │ ├── blaze-address-heading.tsx │ │ ├── blaze-address.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshot.e2e.ts.snap │ │ │ └── snapshot.e2e.ts │ ├── alert │ │ ├── blaze-alert.tsx │ │ ├── blaze-alerts.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ └── snapshots.e2e.ts │ ├── autocomplete │ │ ├── blaze-autocomplete.tsx │ │ ├── interfaces │ │ │ ├── IAutoCompleteItem.ts │ │ │ └── index.ts │ │ ├── readme.md │ │ └── tests │ │ │ └── behaviour.e2e.ts │ ├── avatar │ │ ├── blaze-avatar.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ └── snapshots.e2e.ts │ ├── back-to-top │ │ ├── blaze-back-to-top.tsx │ │ └── readme.md │ ├── badge │ │ ├── blaze-badge.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ └── snapshots.e2e.ts │ ├── calendar │ │ ├── blaze-calendar.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ ├── date-functions.spec.ts │ │ │ └── snapshots.e2e.ts │ ├── card │ │ ├── blaze-card-body.tsx │ │ ├── blaze-card-footer.tsx │ │ ├── blaze-card-header.tsx │ │ ├── blaze-card.tsx │ │ ├── blaze-media-body.tsx │ │ ├── blaze-media-image.tsx │ │ ├── blaze-media-item.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ └── snapshots.e2e.ts │ ├── counter │ │ ├── CountUp.ts │ │ ├── blaze-counter.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ └── snapshots.e2e.ts │ ├── demo │ │ ├── README.md │ │ ├── blaze-demo.tsx │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ └── snapshots.e2e.ts │ ├── divider │ │ ├── blaze-divider.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshot.e2e.ts.snap │ │ │ └── snapshot.e2e.ts │ ├── drawer │ │ ├── blaze-drawer.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ └── snapshots.e2e.ts │ ├── file-upload │ │ ├── blaze-file-upload.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshot.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ └── snapshot.e2e.ts │ ├── image │ │ ├── blaze-image.tsx │ │ ├── readme.md │ │ └── tests │ │ │ └── src.spec.ts │ ├── modal │ │ ├── blaze-modal.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ └── snapshots.e2e.ts │ ├── pagination │ │ ├── blaze-pagination.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ └── snapshots.e2e.ts │ ├── panel │ │ ├── blaze-panel.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ └── snapshots.e2e.ts │ ├── progress │ │ ├── blaze-progress.tsx │ │ ├── progress-bar │ │ │ ├── blaze-progress-bar.tsx │ │ │ └── readme.md │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ └── snapshots.e2e.ts │ ├── sticky │ │ ├── blaze-sticky.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ └── snapshots.e2e.ts │ ├── tabs │ │ ├── blaze-tab.tsx │ │ ├── blaze-tabs.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ └── snapshots.e2e.ts │ ├── tags │ │ ├── blaze-tags.tsx │ │ ├── interfaces │ │ │ ├── IOption.ts │ │ │ └── index.ts │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── input.behaviour.e2e.ts │ │ │ ├── search.behaviour.e2e.ts │ │ │ ├── select.behaviour.e2e.ts │ │ │ └── snapshots.e2e.ts │ ├── timeline │ │ ├── blaze-timeline-item.tsx │ │ ├── blaze-timeline.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ └── snapshots.e2e.ts │ ├── toggle │ │ ├── blaze-toggle.tsx │ │ ├── readme.md │ │ └── tests │ │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ │ ├── behaviour.e2e.ts │ │ │ └── snapshots.e2e.ts │ └── tree │ │ ├── blaze-tree.tsx │ │ ├── readme.md │ │ ├── tests │ │ ├── __snapshots__ │ │ │ └── snapshots.e2e.ts.snap │ │ └── snapshots.e2e.ts │ │ ├── tree-branch │ │ ├── blaze-tree-branch.tsx │ │ └── readme.md │ │ └── tree-leaf │ │ ├── blaze-tree-leaf.tsx │ │ └── readme.md ├── index.html ├── scss │ └── blaze.scss └── test │ ├── index.ts │ └── snap-it.ts ├── stencil.config.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parserOptions: 3 | sourceType: 'module' 4 | ecmaFeatures: 5 | jsx: true 6 | env: 7 | browser: true 8 | node: true 9 | -------------------------------------------------------------------------------- /.github/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 contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at gregory.pratt@me.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, Pull Requests, suggestions or comments about Blaze are all welcome and valued. To start contributing follow these steps: 4 | 5 | ## Step 1 6 | 7 | **Fork the repo!** 8 | 9 | ## Step 2 10 | 11 | Write some code... 12 | 13 | ## Step 3 14 | 15 | **Submit your Pull Request** 16 | 17 | ## Sit back 18 | 19 | At some point your changes will get reviewed, merged in and we'll publish a new version! Yay! 20 | 21 | --- 22 | 23 | #### Code Conduct 24 | 25 | We're all friends! Please try and adhere to our simple rules 26 | 27 | **Be friendly and patient**. 28 | 29 | **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. 30 | 31 | **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we’re a world-wide community, so you might not be communicating in someone else’s primary language. 32 | 33 | **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. 34 | 35 | **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren’t acceptable. 36 | 37 | **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 19 | 20 | **Describe the bug** 21 | A clear and concise description of what the bug is. 22 | 23 | **To Reproduce** 24 | Steps to reproduce the behavior: 25 | 1. Go to '...' 26 | 2. Click on '....' 27 | 3. Scroll down to '....' 28 | 4. See error 29 | 30 | **Expected behavior** 31 | A clear and concise description of what you expected to happen. 32 | 33 | **Screenshots** 34 | If applicable, add screenshots to help explain your problem. 35 | 36 | **Desktop (please complete the following information):** 37 | - OS: [e.g. iOS] 38 | - Browser [e.g. chrome, safari] 39 | - Version [e.g. 22] 40 | 41 | **Smartphone (please complete the following information):** 42 | - Device: [e.g. iPhone6] 43 | - OS: [e.g. iOS8.1] 44 | - Browser [e.g. stock browser, safari] 45 | - Version [e.g. 22] 46 | 47 | **Additional context** 48 | Add any other context about the problem here. 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 19 | 20 | **Is your feature request related to a problem? Please describe.** 21 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 22 | 23 | **Describe the solution you'd like** 24 | A clear and concise description of what you want to happen. 25 | 26 | **Describe alternatives you've considered** 27 | A clear and concise description of any alternative solutions or features you've considered. 28 | 29 | **Additional context** 30 | Add any other context or screenshots about the feature request here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue template 3 | about: For all other issues 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 19 | -------------------------------------------------------------------------------- /.github/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Blaze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /.github/workflows/test-build-deploy.yml: -------------------------------------------------------------------------------- 1 | name: CI Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | name: Test, Build and Deploy 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repo 16 | uses: actions/checkout@master 17 | 18 | - name: Install 19 | run: npm ci 20 | 21 | - name: Test 22 | run: npm test 23 | -------------------------------------------------------------------------------- /.github/workflows/test-pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Test Pull Request 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Test 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout repo 13 | uses: actions/checkout@master 14 | 15 | - name: Install 16 | run: npm ci 17 | 18 | - name: Test 19 | run: npm test 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | www/ 3 | loader/ 4 | 5 | *~ 6 | *.sw[mnpcod] 7 | *.log 8 | *.lock 9 | *.tmp 10 | *.tmp.* 11 | log.txt 12 | *.sublime-project 13 | *.sublime-workspace 14 | 15 | .stencil/ 16 | .idea/ 17 | .vscode/ 18 | .sass-cache/ 19 | .versions/ 20 | node_modules/ 21 | $RECYCLE.BIN/ 22 | 23 | .DS_Store 24 | Thumbs.db 25 | UserInterfaceState.xcuserstate 26 | .env 27 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.7.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | __snapshots__ 3 | dist 4 | www 5 | LICENSE 6 | *.min.* 7 | *.lock 8 | *.log 9 | *.png 10 | *.d.ts 11 | *.html 12 | .* 13 | 14 | _settings.scss 15 | robots.txt 16 | *.md 17 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | arrowParens: 'always' 2 | printWidth: 120 3 | singleQuote: true 4 | trailingComma: 'es5' 5 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "stylelint-config-rational-order", 4 | "stylelint-config-standard-scss" 5 | ], 6 | "plugins": [ 7 | "stylelint-scss" 8 | ], 9 | "rules": { 10 | "at-rule-empty-line-before": null, 11 | "at-rule-no-unknown": null, 12 | "color-function-notation": "modern", 13 | "declaration-empty-line-before": null, 14 | "function-max-empty-lines": 1, 15 | "max-line-length": null, 16 | "no-descending-specificity": null, 17 | "property-no-vendor-prefix": null, 18 | "string-quotes": "single", 19 | "selector-class-pattern": null, 20 | "value-list-max-empty-lines": 1, 21 | "value-keyword-case": null, 22 | "scss/at-mixin-pattern": null, 23 | "scss/at-import-partial-extension": null 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blaze Atoms 2 | 3 | Blaze is a framework-free open source UI toolkit. It provides great structure for building websites quickly with a scalable and maintainable foundation. 4 | 5 | Blaze Atoms is a set of web components powered by Blaze CSS. 6 | 7 | **https://www.blazeui.com** 8 | 9 | ## Ready to Code Dev Environment 10 | 11 | Contribute to Blaze using an online development environment. 12 | 13 | [![Open in Github Dot](https://img.shields.io/badge/code-open%20in%20github-orange?logo=github&style=for-the-badge)](https://github.dev/BlazeSoftware/atoms) 14 | 15 | ## Local Developer Setup 16 | 17 | First of all install [NVM](https://github.com/creationix/nvm#install-script). 18 | 19 | When that is complete run: 20 | 21 | ```cli 22 | $ nvm use 23 | $ npm i 24 | $ npm start 25 | ``` 26 | 27 | ## Installing the JavaScript Components 28 | 29 | Link to it by adding the following to your ``. 30 | 31 | ```html 32 | 33 | ``` 34 | 35 | The `x.x.x` is the specific version of the library, you should use specifc versions to prevent against breaking changes. 36 | 37 | That's it! Start using the components in your HTML. 38 | 39 | ### Integrating with a Framework 40 | 41 | Blaze uses [Stencil](http://stenciljs.com/) to compile our components into Web Components. And since they can be used within frameworks or in standard HTML websites the best resource to read is the [Stencil documentation](https://stenciljs.com/docs/overview) regarding framework integration. 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blaze/atoms", 3 | "version": "13.1.0", 4 | "description": "Open Source UI Toolkit - Atoms", 5 | "author": "Gregory Pratt", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/BlazeSoftware/atoms.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/BlazeSoftware/atoms/issues" 13 | }, 14 | "homepage": "https://www.blazeui.com", 15 | "keywords": [ 16 | "html", 17 | "sass", 18 | "scss", 19 | "css", 20 | "bem", 21 | "itcss", 22 | "bemit", 23 | "ui", 24 | "ux", 25 | "framework", 26 | "components", 27 | "webcomponents", 28 | "stencil", 29 | "stenciljs" 30 | ], 31 | "publishConfig": { 32 | "access": "public" 33 | }, 34 | "main": "dist/index.cjs.js", 35 | "module": "dist/index.js", 36 | "es2015": "dist/esm/index.mjs", 37 | "es2017": "dist/esm/index.mjs", 38 | "types": "dist/types/components.d.ts", 39 | "collection": "dist/collection/collection-manifest.json", 40 | "collection:main": "dist/collection/index.js", 41 | "unpkg": "dist/atoms/atoms.js", 42 | "files": [ 43 | "dist/", 44 | "loader/" 45 | ], 46 | "scripts": { 47 | "build": "stencil build", 48 | "docs": "stencil build --docs", 49 | "start": "stencil build --dev --watch --serve", 50 | "test": "stencil test --spec --e2e", 51 | "test.watch": "stencil test --spec --e2e --watchAll", 52 | "generate": "stencil generate", 53 | "stylelint": "stylelint \"src/**/*.scss\"", 54 | "prettier": "prettier --write './**/*'", 55 | "preversion": "npm run prettier && npm run stylelint && npm test && npm run build", 56 | "postversion": "git push --follow-tags" 57 | }, 58 | "devDependencies": { 59 | "@blaze/css": "12.0.0", 60 | "@stencil/core": "2.17.3", 61 | "@stencil/postcss": "2.1.0", 62 | "@stencil/router": "1.0.1", 63 | "@stencil/sass": "1.5.2", 64 | "@types/cssnano": "5.1.0", 65 | "@types/jest": "27.4.1", 66 | "@types/puppeteer": "5.4.5", 67 | "cssnano": "5.1.13", 68 | "jest": "27.5.1", 69 | "jest-cli": "27.5.1", 70 | "postcss": "8.4.16", 71 | "prettier": "2.7.1", 72 | "puppeteer": "13.5.1", 73 | "stylelint": "14.11.0", 74 | "stylelint-config-rational-order": "0.1.2", 75 | "stylelint-config-standard": "28.0.0", 76 | "stylelint-config-standard-scss": "^5.0.0", 77 | "stylelint-scss": "4.3.0" 78 | }, 79 | "dependencies": {} 80 | } 81 | -------------------------------------------------------------------------------- /src/components/accordion/blaze-accordion-pane.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Event, EventEmitter, Method, Prop, State } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-accordion-pane', 5 | }) 6 | export class AccordionPane { 7 | @State() 8 | _isOpen: boolean = false; 9 | 10 | @Prop() 11 | open: boolean = false; 12 | 13 | @Prop() 14 | header: string; 15 | 16 | @Event({ eventName: 'togglepane' }) 17 | onToggle: EventEmitter; 18 | 19 | componentWillLoad() { 20 | this._isOpen = this.open; 21 | } 22 | 23 | @Method() 24 | async show() { 25 | this._isOpen = true; 26 | } 27 | 28 | @Method() 29 | async close() { 30 | this._isOpen = false; 31 | } 32 | 33 | toggle() { 34 | this._isOpen ? this.close() : this.show(); 35 | this.onToggle.emit(this._isOpen); 36 | } 37 | 38 | @Method() 39 | async isOpen() { 40 | return this._isOpen; 41 | } 42 | 43 | render() { 44 | const isOpenClass = this._isOpen ? 'c-card__control--active' : ''; 45 | 46 | return [ 47 | , 55 |
56 | 57 |
, 58 | ]; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/components/accordion/blaze-accordion.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Element, Event, EventEmitter, Listen } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-accordion', 5 | }) 6 | export class Accordion { 7 | @Element() 8 | private element: HTMLElement; 9 | 10 | @Event({ eventName: 'toggle' }) 11 | onToggle: EventEmitter; 12 | 13 | @Listen('togglepane') 14 | onTogglePane(ev) { 15 | const accordion = this.element.children[0]; 16 | const open = ev.detail; 17 | const pane = ev.target; 18 | const idx = [].indexOf.call(accordion.children, pane); 19 | this.onToggle.emit({ idx, open }); 20 | } 21 | 22 | render() { 23 | return ( 24 |
25 | 26 |
27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/accordion/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-accordion-pane 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | -------- | --------- | ----------- | --------- | ----------- | 10 | | `header` | `header` | | `string` | `undefined` | 11 | | `open` | `open` | | `boolean` | `false` | 12 | 13 | 14 | ## Events 15 | 16 | | Event | Description | Type | 17 | | ------------ | ----------- | ------------------ | 18 | | `togglepane` | | `CustomEvent` | 19 | 20 | 21 | ## Methods 22 | 23 | ### `close() => Promise` 24 | 25 | 26 | 27 | #### Returns 28 | 29 | Type: `Promise` 30 | 31 | 32 | 33 | ### `isOpen() => Promise` 34 | 35 | 36 | 37 | #### Returns 38 | 39 | Type: `Promise` 40 | 41 | 42 | 43 | ### `show() => Promise` 44 | 45 | 46 | 47 | #### Returns 48 | 49 | Type: `Promise` 50 | 51 | 52 | 53 | 54 | ---------------------------------------------- 55 | 56 | *Built with [StencilJS](https://stenciljs.com/)* 57 | -------------------------------------------------------------------------------- /src/components/accordion/tests/__snapshots__/snapshots.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`emtpy 1`] = `"
"`; 4 | 5 | exports[`two headers and two panes 1`] = `"
This is an accordion
"`; 6 | 7 | exports[`two headers and two panes 2`] = `"
This is an accordion
"`; 8 | -------------------------------------------------------------------------------- /src/components/accordion/tests/behaviour.e2e.ts: -------------------------------------------------------------------------------- 1 | import { newE2EPage } from '@stencil/core/testing'; 2 | 3 | const html = ` 4 | 5 | 6 | This is an accordion 7 | 8 | 9 | `; 10 | 11 | describe('accordion', () => { 12 | test('pane opens when clicked on', async () => { 13 | const page = await newE2EPage(); 14 | await page.setContent(html); 15 | 16 | let paneControl = await page.find('button[role=heading]'); 17 | expect(paneControl).not.toHaveClass('c-card__control--active'); 18 | expect(paneControl).toEqualAttribute('aria-expanded', 'false'); 19 | 20 | await paneControl.click(); 21 | await page.waitForChanges(); 22 | 23 | paneControl = await page.find('button[role=heading]'); 24 | expect(paneControl).toHaveClass('c-card__control--active'); 25 | expect(paneControl).toEqualAttribute('aria-expanded', 'true'); 26 | }); 27 | 28 | test('triggers toggle event', async () => { 29 | const page = await newE2EPage(); 30 | await page.setContent(html); 31 | 32 | const accordion = await page.find('blaze-accordion'); 33 | const pane = await accordion.find('blaze-accordion-pane'); 34 | const paneControl = await pane.find('button[role=heading]'); 35 | 36 | const toggle = await accordion.spyOnEvent('toggle'); 37 | 38 | await paneControl.click(); 39 | await page.waitForChanges(); 40 | 41 | expect(toggle).toHaveReceivedEventDetail({ 42 | idx: 0, 43 | open: true, 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/components/accordion/tests/snapshots.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'accordion'; 4 | const snap = snapIt(component); 5 | 6 | snap('emtpy', ``); 7 | snap( 8 | 'two headers and two panes', 9 | ` 10 | 11 | This is an accordion 12 | 13 | ` 14 | ); 15 | snap( 16 | 'two headers and two panes', 17 | ` 18 | 19 | This is an accordion 20 | 21 | ` 22 | ); 23 | -------------------------------------------------------------------------------- /src/components/address/blaze-address-heading.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-address-heading', 5 | }) 6 | export class AddressHeading { 7 | render() { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/address/blaze-address.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-address', 5 | }) 6 | export class Address { 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/address/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-address-heading 2 | 3 | 4 | 5 | 6 | ---------------------------------------------- 7 | 8 | *Built with [StencilJS](https://stenciljs.com/)* 9 | -------------------------------------------------------------------------------- /src/components/address/tests/__snapshots__/snapshot.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`address renders correctly 1`] = `"
Address line one
"`; 4 | 5 | exports[`address renders the address heading 1`] = `"
Address header Address line one
Address line two
Postcode
"`; 6 | -------------------------------------------------------------------------------- /src/components/address/tests/snapshot.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'address'; 4 | 5 | describe(component, () => { 6 | const snap = snapIt(component); 7 | 8 | describe('renders', () => { 9 | snap('correctly', 'Address line one'); 10 | 11 | snap( 12 | 'the address heading', 13 | ` 14 | Address header 15 | Address line one
16 | Address line two
17 | Postcode 18 |
` 19 | ); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/components/alert/blaze-alert.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Event, EventEmitter, Method, Prop, State } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-alert', 5 | }) 6 | export class Alert { 7 | @Prop() 8 | type: string = ''; 9 | 10 | @Prop() 11 | dismissible: boolean = false; 12 | 13 | @Prop() 14 | open: boolean = false; 15 | 16 | @State() 17 | _isOpen: boolean = false; 18 | 19 | @Event({ eventName: 'close' }) 20 | onClose: EventEmitter; 21 | 22 | @Method() 23 | async close() { 24 | this._isOpen = false; 25 | this.onClose.emit(); 26 | } 27 | 28 | @Method() 29 | async show() { 30 | this._isOpen = true; 31 | } 32 | 33 | @Method() 34 | async isOpen() { 35 | return this._isOpen; 36 | } 37 | 38 | componentWillLoad() { 39 | this._isOpen = this.open; 40 | } 41 | 42 | render() { 43 | const isOpenClass = !this._isOpen ? 'u-display-none' : ''; 44 | const typeClass = this.type ? `c-alert--${this.type}` : ''; 45 | 46 | return ( 47 | 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/alert/blaze-alerts.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Prop } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-alerts', 5 | }) 6 | export class Alerts { 7 | @Prop() 8 | position: string; 9 | 10 | render() { 11 | const positionClass = this.position ? `c-alerts--${this.position}` : ''; 12 | 13 | return ( 14 | 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/components/alert/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-alerts 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | ---------- | ---------- | ----------- | -------- | ----------- | 10 | | `position` | `position` | | `string` | `undefined` | 11 | 12 | 13 | ---------------------------------------------- 14 | 15 | *Built with [StencilJS](https://stenciljs.com/)* 16 | -------------------------------------------------------------------------------- /src/components/alert/tests/__snapshots__/snapshots.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`alert renders correct type 1`] = `""`; 4 | 5 | exports[`alert renders without parameters 1`] = `""`; 6 | -------------------------------------------------------------------------------- /src/components/alert/tests/behaviour.e2e.ts: -------------------------------------------------------------------------------- 1 | import { newE2EPage } from '@stencil/core/testing'; 2 | 3 | const html = 'test this!'; 4 | 5 | describe('alert', () => { 6 | test('triggers close event', async () => { 7 | const page = await newE2EPage(); 8 | await page.setContent(html); 9 | 10 | const alert = await page.find('blaze-alert'); 11 | const closeButton = await page.find('button'); 12 | const close = await alert.spyOnEvent('close'); 13 | 14 | await closeButton.click(); 15 | await page.waitForChanges(); 16 | 17 | expect(close).toHaveReceivedEvent(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/components/alert/tests/snapshots.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'alert'; 4 | 5 | describe(component, () => { 6 | const snap = snapIt(component); 7 | 8 | describe('renders', () => { 9 | snap('without parameters', 'test this!'); 10 | snap('correct type', 'test this!'); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/autocomplete/blaze-autocomplete.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Event, EventEmitter, Prop, Method, State, Listen, Element } from '@stencil/core'; 2 | import { IAutoCompleteItem } from './interfaces'; 3 | 4 | @Component({ 5 | tag: 'blaze-autocomplete', 6 | }) 7 | export class AutoComplete { 8 | @Element() 9 | el: HTMLDivElement; 10 | 11 | @Prop() 12 | placeholder: string; 13 | 14 | @Event({ eventName: 'selected' }) 15 | onSelected: EventEmitter; 16 | 17 | @Event({ eventName: 'filter' }) 18 | onFilter: EventEmitter; 19 | 20 | @State() 21 | items: Array = []; 22 | 23 | @State() 24 | selectedItem: IAutoCompleteItem; 25 | 26 | @State() 27 | activeItem: IAutoCompleteItem; 28 | 29 | @State() 30 | _isOpen: boolean; 31 | 32 | @State() 33 | value: string; 34 | 35 | componentDidLoad() { 36 | document.addEventListener('click', (e) => { 37 | e.target !== this.el && !this.el.contains(e.target as any) && this.close(); 38 | }); 39 | } 40 | 41 | @Method() 42 | async setItems(items: Array) { 43 | this.items = items; 44 | this.value ? this.open() : this.close(); 45 | } 46 | 47 | @Method() 48 | async reset() { 49 | this.items = []; 50 | this.value = null; 51 | this.close(); 52 | } 53 | 54 | select(item: IAutoCompleteItem) { 55 | if (item.disabled) return; 56 | this.activeItem = item; 57 | this.selectedItem = item; 58 | this.value = item.text; 59 | this.onSelected.emit(item); 60 | this.close(); 61 | } 62 | 63 | filter(e) { 64 | this.activeItem = null; 65 | this.value = e.target.value; 66 | const query = this.value; 67 | this.onFilter.emit(query); 68 | } 69 | 70 | open() { 71 | if (this.items && this.items.length) { 72 | this._isOpen = true; 73 | } 74 | } 75 | 76 | close() { 77 | this._isOpen = false; 78 | } 79 | 80 | @Listen('keydown') 81 | handleKeyDown(ev: KeyboardEvent) { 82 | let idx = this.items.indexOf(this.activeItem); 83 | 84 | switch (ev.key) { 85 | case 'ArrowDown': { 86 | ev.preventDefault(); 87 | this.open(); 88 | if (idx < this.items.length - 1) { 89 | this.activeItem = this.items[idx + 1]; 90 | } 91 | break; 92 | } 93 | case 'ArrowUp': { 94 | ev.preventDefault(); 95 | this.open(); 96 | if (idx > 0 && this._isOpen) { 97 | this.activeItem = this.items[idx - 1]; 98 | } 99 | break; 100 | } 101 | case 'Enter': { 102 | if (this.activeItem) { 103 | ev.preventDefault(); 104 | this.select(this.activeItem); 105 | } 106 | } 107 | case 'Escape': { 108 | this.close(); 109 | } 110 | } 111 | } 112 | 113 | render() { 114 | return ( 115 |
116 | this.filter(e)} 123 | onFocus={() => this.open()} 124 | onClick={() => this.open()} 125 | /> 126 | {this._isOpen && ( 127 | 137 | )} 138 |
139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/components/autocomplete/interfaces/IAutoCompleteItem.ts: -------------------------------------------------------------------------------- 1 | export default interface IAutoCompleteItem { 2 | text: string; 3 | disabled?: boolean; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/autocomplete/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | import IAutoCompleteItem from './IAutoCompleteItem'; 2 | 3 | export { IAutoCompleteItem }; 4 | -------------------------------------------------------------------------------- /src/components/autocomplete/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-autocomplete 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | ------------- | ------------- | ----------- | -------- | ----------- | 10 | | `placeholder` | `placeholder` | | `string` | `undefined` | 11 | 12 | 13 | ## Events 14 | 15 | | Event | Description | Type | 16 | | ---------- | ----------- | ------------------ | 17 | | `filter` | | `CustomEvent` | 18 | | `selected` | | `CustomEvent` | 19 | 20 | 21 | ## Methods 22 | 23 | ### `reset() => Promise` 24 | 25 | 26 | 27 | #### Returns 28 | 29 | Type: `Promise` 30 | 31 | 32 | 33 | ### `setItems(items: Array) => Promise` 34 | 35 | 36 | 37 | #### Returns 38 | 39 | Type: `Promise` 40 | 41 | 42 | 43 | 44 | ## Dependencies 45 | 46 | ### Used by 47 | 48 | - [blaze-tags](../tags) 49 | 50 | ### Graph 51 | ```mermaid 52 | graph TD; 53 | blaze-tags --> blaze-autocomplete 54 | style blaze-autocomplete fill:#f9f,stroke:#333,stroke-width:4px 55 | ``` 56 | 57 | ---------------------------------------------- 58 | 59 | *Built with [StencilJS](https://stenciljs.com/)* 60 | -------------------------------------------------------------------------------- /src/components/autocomplete/tests/behaviour.e2e.ts: -------------------------------------------------------------------------------- 1 | import { newE2EPage } from '@stencil/core/testing'; 2 | 3 | const html = ''; 4 | 5 | describe('autocomplete', () => { 6 | let page, autocomplete, input; 7 | const items = [ 8 | { 9 | text: 'item 1', 10 | }, 11 | { 12 | text: 'item 2', 13 | }, 14 | { 15 | text: 'item 3', 16 | }, 17 | { 18 | text: 'item 4', 19 | }, 20 | { 21 | text: 'item 5', 22 | }, 23 | ]; 24 | 25 | beforeEach(async () => { 26 | page = await newE2EPage(); 27 | await page.setContent(html); 28 | 29 | autocomplete = await page.find('blaze-autocomplete'); 30 | input = await page.find('input'); 31 | 32 | await autocomplete.callMethod('setItems', items); 33 | await input.click(); 34 | }); 35 | 36 | test('triggers filter event when typing', async () => { 37 | const filter = await autocomplete.spyOnEvent('filter'); 38 | 39 | await input.type('some text'); 40 | 41 | expect(filter).toHaveReceivedEventDetail('some text'); 42 | }); 43 | 44 | describe('menu', () => { 45 | test('opens on click', async () => { 46 | const resultsList = await page.findAll('.c-card__control'); 47 | 48 | expect(resultsList.length).toBe(items.length); 49 | }); 50 | 51 | test('closes on escape', async () => { 52 | await input.press('Escape'); 53 | const resultsList = await page.findAll('.c-card--menu'); 54 | 55 | expect(resultsList.length).toBe(0); 56 | }); 57 | 58 | test('cycles through active items', async () => { 59 | for (let i = 1; i <= items.length; i++) { 60 | await input.press('ArrowDown'); 61 | const item = await page.find(`.c-card__control:nth-child(${i})`); 62 | 63 | expect(item).toHaveClass('c-card__control--active'); 64 | } 65 | }); 66 | 67 | test('triggers select on item click', async () => { 68 | const select = await autocomplete.spyOnEvent('selected'); 69 | const item = await page.find(`.c-card__control:nth-child(3)`); 70 | 71 | await item.click(); 72 | 73 | expect(select).toHaveReceivedEventDetail(items[2]); 74 | }); 75 | 76 | test('triggers select on enter', async () => { 77 | const select = await autocomplete.spyOnEvent('selected'); 78 | await input.press('ArrowDown'); 79 | await input.press('Enter'); 80 | 81 | expect(select).toHaveReceivedEventDetail(items[0]); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /src/components/avatar/blaze-avatar.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Prop } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-avatar', 5 | }) 6 | export class Avatar { 7 | @Prop() 8 | size: string = ''; 9 | 10 | @Prop() 11 | src: string; 12 | 13 | @Prop() 14 | src2: string; 15 | 16 | @Prop() 17 | alt: string; 18 | 19 | @Prop() 20 | alt2: string; 21 | 22 | @Prop() 23 | text: string; 24 | 25 | render() { 26 | if (!this.src && !this.text) return; 27 | 28 | const sizeClass = this.size.length > 0 ? `u-${this.size}` : ''; 29 | 30 | return ( 31 |
32 | {this.src && {this.alt}} 33 | {this.src2 && {this.alt2}} 34 |
35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/avatar/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-avatar 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | -------- | --------- | ----------- | -------- | ----------- | 10 | | `alt` | `alt` | | `string` | `undefined` | 11 | | `alt2` | `alt-2` | | `string` | `undefined` | 12 | | `size` | `size` | | `string` | `''` | 13 | | `src` | `src` | | `string` | `undefined` | 14 | | `src2` | `src-2` | | `string` | `undefined` | 15 | | `text` | `text` | | `string` | `undefined` | 16 | 17 | 18 | ---------------------------------------------- 19 | 20 | *Built with [StencilJS](https://stenciljs.com/)* 21 | -------------------------------------------------------------------------------- /src/components/avatar/tests/__snapshots__/snapshots.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`avatar renders 2 images 1`] = `"
"`; 4 | 5 | exports[`avatar renders an image 1`] = `"
"`; 6 | 7 | exports[`avatar renders correctly sized circles 1`] = `"
"`; 8 | 9 | exports[`avatar renders images, text and sizes it correctly 1`] = `"
"`; 10 | 11 | exports[`avatar renders nothing if no attributes are set 1`] = `""`; 12 | 13 | exports[`avatar renders text inside 1`] = `"
"`; 14 | -------------------------------------------------------------------------------- /src/components/avatar/tests/snapshots.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'avatar'; 4 | 5 | describe(component, () => { 6 | const snap = snapIt(component); 7 | 8 | describe('renders', () => { 9 | snap('nothing if no attributes are set', ''); 10 | snap('text inside', ''); 11 | snap('correctly sized circles', ''); 12 | snap('an image', ''); 13 | snap('2 images', ''); 14 | snap( 15 | 'images, text and sizes it correctly', 16 | '' 17 | ); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/components/back-to-top/blaze-back-to-top.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Event, EventEmitter, Listen, State, Prop } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-back-to-top', 5 | }) 6 | export class BackToTop { 7 | @Event({ eventName: 'backtotop' }) 8 | onBackToTop: EventEmitter; 9 | 10 | @State() 11 | _isOpen: boolean; 12 | 13 | @Prop() 14 | position: string; 15 | 16 | @Listen('scroll', { target: 'document' }) 17 | enable() { 18 | this._isOpen = window.scrollY > window.innerHeight; 19 | } 20 | 21 | goUp() { 22 | window.scrollTo({ top: 0, behavior: 'smooth' }); 23 | this.onBackToTop.emit(); 24 | } 25 | 26 | render() { 27 | const visibleCss = !this._isOpen ? 'u-display-none' : ''; 28 | const positionCss = this.position ? `c-back-to-top--${this.position}` : ''; 29 | 30 | return ( 31 |
32 | 40 |
41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/back-to-top/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-back-to-top 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | ---------- | ---------- | ----------- | -------- | ----------- | 10 | | `position` | `position` | | `string` | `undefined` | 11 | 12 | 13 | ## Events 14 | 15 | | Event | Description | Type | 16 | | ----------- | ----------- | ------------------ | 17 | | `backtotop` | | `CustomEvent` | 18 | 19 | 20 | ---------------------------------------------- 21 | 22 | *Built with [StencilJS](https://stenciljs.com/)* 23 | -------------------------------------------------------------------------------- /src/components/badge/blaze-badge.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Prop } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-badge', 5 | }) 6 | export class Badge { 7 | @Prop() 8 | type: string; 9 | 10 | @Prop() 11 | rounded: boolean; 12 | 13 | @Prop() 14 | ghost: boolean; 15 | 16 | render() { 17 | const typeClass = this.type ? `c-badge--${this.type}` : ''; 18 | const roundedClass = this.rounded ? `c-badge--rounded` : ''; 19 | const ghostClass = this.ghost ? `c-badge--ghost` : ''; 20 | 21 | return ( 22 | 23 | 24 | 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/badge/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-badge 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | --------- | --------- | ----------- | --------- | ----------- | 10 | | `ghost` | `ghost` | | `boolean` | `undefined` | 11 | | `rounded` | `rounded` | | `boolean` | `undefined` | 12 | | `type` | `type` | | `string` | `undefined` | 13 | 14 | 15 | ---------------------------------------------- 16 | 17 | *Built with [StencilJS](https://stenciljs.com/)* 18 | -------------------------------------------------------------------------------- /src/components/badge/tests/__snapshots__/snapshots.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`badge renders appends the color modifier class 1`] = `"brand"`; 4 | 5 | exports[`badge renders handles boolean ghost and boolean rounded 1`] = `"default"`; 6 | 7 | exports[`badge renders handles boolean rounded attribute 1`] = `"default"`; 8 | 9 | exports[`badge renders handles both boolean rounded attribute and type 1`] = `"brand"`; 10 | 11 | exports[`badge renders renders correctly with no props 1`] = `"default"`; 12 | 13 | exports[`badge renders supports all the modifiers 1`] = `"brand"`; 14 | -------------------------------------------------------------------------------- /src/components/badge/tests/snapshots.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'badge'; 4 | 5 | describe(component, () => { 6 | const snap = snapIt(component); 7 | 8 | describe('renders', () => { 9 | snap('renders correctly with no props', 'default'); 10 | snap('appends the color modifier class', 'brand'); 11 | snap('handles boolean rounded attribute', 'default'); 12 | snap('handles both boolean rounded attribute and type', 'brand'); 13 | snap('handles boolean ghost and boolean rounded', 'default'); 14 | snap('supports all the modifiers', 'brand'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/components/calendar/blaze-calendar.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Prop, State, Event } from '@stencil/core'; 2 | import { EventEmitter } from 'events'; 3 | 4 | @Component({ 5 | tag: 'blaze-calendar', 6 | }) 7 | export class Calendar { 8 | @Prop() 9 | date: string; 10 | 11 | @Prop() 12 | type: string; 13 | 14 | @Prop() 15 | multiple: boolean = false; 16 | 17 | @State() 18 | _date: Date = new Date(); 19 | 20 | @State() 21 | _selectedDates: Array = []; 22 | 23 | @Event({ eventName: 'selected' }) 24 | onSelected: EventEmitter; 25 | 26 | days: Array = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; 27 | 28 | months: Array = [ 29 | 'January', 30 | 'February', 31 | 'March', 32 | 'April', 33 | 'May', 34 | 'June', 35 | 'July', 36 | 'August', 37 | 'September', 38 | 'October', 39 | 'November', 40 | 'December', 41 | ]; 42 | 43 | buttonType = ''; 44 | 45 | componentWillLoad() { 46 | const date = this.date || new Date(); 47 | this._date = new Date(date); 48 | this._selectedDates = [...this._selectedDates, this._date]; 49 | this.buttonType = this.type ? `c-button--${this.type}` : ''; 50 | } 51 | 52 | getMonthName() { 53 | return this.months[this._date.getMonth()]; 54 | } 55 | 56 | getFirstDay() { 57 | return new Date(this._date.getFullYear(), this._date.getMonth(), 1).getDay(); 58 | } 59 | 60 | getLastDay() { 61 | return new Date(this._date.getFullYear(), this._date.getMonth() + 1, 0).getDay(); 62 | } 63 | 64 | getTotalDaysInMonth(diff: number = 0) { 65 | return new Date(this._date.getFullYear(), this._date.getMonth() + 1 + diff, 0).getDate(); 66 | } 67 | 68 | selectDate(date) { 69 | if (this._selectedDates.filter((d) => d.toDateString() === date.toDateString()).length) { 70 | // If date already selected remove it 71 | this._selectedDates = this._selectedDates.filter((d) => d.toDateString() !== date.toDateString()); 72 | } else { 73 | // otherwise add it 74 | if (this.multiple) { 75 | this._selectedDates = [...this._selectedDates, date]; 76 | } else { 77 | this._selectedDates = [date]; 78 | } 79 | } 80 | 81 | this.onSelected.emit(this._selectedDates.map((d) => d.toDateString()).toString()); 82 | } 83 | 84 | renderDayButton(date: Date) { 85 | const isToday = date.toDateString() === new Date().toDateString(); 86 | const isSelected = !!this._selectedDates.filter((d) => d.toDateString() === date.toDateString()).length; 87 | 88 | const inMonthClass = date.getMonth() === this._date.getMonth() ? 'c-calendar__date--in-month' : ''; 89 | const selectedClass = isSelected ? `c-calendar__date--selected ${this.buttonType}` : ''; 90 | 91 | return ( 92 | 100 | ); 101 | } 102 | 103 | populateDaysPreviousMonth() { 104 | const days: Array = []; 105 | const totalDaysInPreviousMonth = this.getTotalDaysInMonth(-1); 106 | for (let i = totalDaysInPreviousMonth; i > totalDaysInPreviousMonth - this.getFirstDay(); i--) { 107 | const date = new Date(this._date); 108 | date.setMonth(this._date.getMonth() - 1); 109 | date.setDate(i); 110 | const day = new Date(date); 111 | days.unshift(this.renderDayButton(day)); 112 | } 113 | 114 | return days; 115 | } 116 | 117 | populateDaysCurrentMonth() { 118 | const days: Array = []; 119 | const totalDaysInMonth: number = new Date(this._date.getFullYear(), this._date.getMonth() + 1, 0).getDate(); 120 | for (let i = 1; i <= totalDaysInMonth; i++) { 121 | const date = new Date(this._date); 122 | date.setDate(i); 123 | const day = new Date(date); 124 | days.push(this.renderDayButton(day)); 125 | } 126 | 127 | return days; 128 | } 129 | 130 | populateDaysNextMonth() { 131 | const days: Array = []; 132 | for (let i = 1; i < 7 - this.getLastDay(); i++) { 133 | const date = new Date(this._date); 134 | date.setDate(i); 135 | date.setMonth(this._date.getMonth() + 1); 136 | const day = new Date(date); 137 | days.push(this.renderDayButton(day)); 138 | } 139 | 140 | return days; 141 | } 142 | 143 | navYear(diff) { 144 | const date = new Date(this._date); 145 | date.setFullYear(this._date.getFullYear() + diff); 146 | this._date = new Date(date); 147 | } 148 | 149 | navMonth(diff) { 150 | const date = new Date(this._date); 151 | date.setMonth(this._date.getMonth() + diff); 152 | this._date = new Date(date); 153 | } 154 | 155 | today() { 156 | this.selectDate(new Date()); 157 | this._date = new Date(); 158 | } 159 | 160 | render() { 161 | return ( 162 |
163 | 166 |
{this._date.getFullYear()}
167 | 170 | 171 | 174 |
{this.getMonthName()}
175 | 178 | 179 | {this.days.map((day) => ( 180 |
{day}
181 | ))} 182 | 183 | {this.populateDaysPreviousMonth()} 184 | 185 | {this.populateDaysCurrentMonth()} 186 | 187 | {this.populateDaysNextMonth()} 188 | 189 | 194 |
195 | ); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/components/calendar/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-calendar 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | ---------- | ---------- | ----------- | --------- | ----------- | 10 | | `date` | `date` | | `string` | `undefined` | 11 | | `multiple` | `multiple` | | `boolean` | `false` | 12 | | `type` | `type` | | `string` | `undefined` | 13 | 14 | 15 | ## Events 16 | 17 | | Event | Description | Type | 18 | | ---------- | ----------- | ------------------ | 19 | | `selected` | | `CustomEvent` | 20 | 21 | 22 | ---------------------------------------------- 23 | 24 | *Built with [StencilJS](https://stenciljs.com/)* 25 | -------------------------------------------------------------------------------- /src/components/calendar/tests/__snapshots__/snapshots.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`calendar renders a coloured calendar 1`] = `"
1982
January
Su
Mo
Tu
We
Th
Fr
Sa
"`; 4 | 5 | exports[`calendar renders a default calendar 1`] = `"
1982
January
Su
Mo
Tu
We
Th
Fr
Sa
"`; 6 | -------------------------------------------------------------------------------- /src/components/calendar/tests/behaviour.e2e.ts: -------------------------------------------------------------------------------- 1 | import { newE2EPage } from '@stencil/core/testing'; 2 | 3 | const html = ''; 4 | 5 | describe('calendar', () => { 6 | test('triggers select event', async () => { 7 | const page = await newE2EPage(); 8 | await page.setContent(html); 9 | 10 | const calendar = await page.find('blaze-calendar'); 11 | const control = await calendar.find('.c-calendar__today'); 12 | const select = await calendar.spyOnEvent('selected'); 13 | 14 | await control.click(); 15 | await page.waitForChanges(); 16 | 17 | expect(select).toHaveReceivedEventDetail(new Date().toDateString()); 18 | }); 19 | 20 | test('previous year button', async () => { 21 | const page = await newE2EPage(); 22 | await page.setContent(html); 23 | 24 | const calendar = await page.find('blaze-calendar'); 25 | const control = await calendar.find('.c-calendar__control--prev-year'); 26 | 27 | await control.click(); 28 | await page.waitForChanges(); 29 | 30 | const value = await calendar.find('.c-calendar__header--year'); 31 | expect(value.innerText).toEqual('1981'); 32 | }); 33 | 34 | test('next year button', async () => { 35 | const page = await newE2EPage(); 36 | await page.setContent(html); 37 | 38 | const calendar = await page.find('blaze-calendar'); 39 | const control = await calendar.find('.c-calendar__control--next-year'); 40 | 41 | await control.click(); 42 | await page.waitForChanges(); 43 | 44 | const value = await calendar.find('.c-calendar__header--year'); 45 | expect(value.innerText).toEqual('1983'); 46 | }); 47 | 48 | test('prev month button', async () => { 49 | const page = await newE2EPage(); 50 | await page.setContent(html); 51 | 52 | const calendar = await page.find('blaze-calendar'); 53 | const control = await calendar.find('.c-calendar__control--prev-month'); 54 | 55 | await control.click(); 56 | await page.waitForChanges(); 57 | 58 | const value = await calendar.find('.c-calendar__header--month'); 59 | expect(value.innerText).toEqual('December'); 60 | }); 61 | 62 | test('next month button', async () => { 63 | const page = await newE2EPage(); 64 | await page.setContent(html); 65 | 66 | const calendar = await page.find('blaze-calendar'); 67 | const control = await calendar.find('.c-calendar__control--next-month'); 68 | 69 | await control.click(); 70 | await page.waitForChanges(); 71 | 72 | const value = await calendar.find('.c-calendar__header--month'); 73 | expect(value.innerText).toEqual('February'); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /src/components/calendar/tests/date-functions.spec.ts: -------------------------------------------------------------------------------- 1 | import { Calendar } from '../blaze-calendar'; 2 | 3 | describe('calendar', () => { 4 | let calendar; 5 | 6 | beforeEach(() => { 7 | calendar = new Calendar(); 8 | 9 | calendar.date = '01/14/1982'; 10 | calendar.componentWillLoad(); 11 | }); 12 | 13 | test('get month name', async () => { 14 | expect(calendar.getMonthName()).toBe('January'); 15 | }); 16 | 17 | test('get day of week of first day of month', () => { 18 | expect(calendar.getFirstDay()).toBe(5); // Friday 19 | }); 20 | 21 | test('get day of week of last day of month', () => { 22 | expect(calendar.getLastDay()).toBe(0); // Sunday 23 | }); 24 | 25 | test('get total number of days in month', () => { 26 | expect(calendar.getTotalDaysInMonth()).toBe(31); 27 | }); 28 | 29 | test('get last days of previous month', () => { 30 | expect(calendar.populateDaysPreviousMonth().length).toBe(5); 31 | }); 32 | 33 | test('get first days of next month', () => { 34 | expect(calendar.populateDaysNextMonth().length).toBe(6); 35 | }); 36 | 37 | test('get days of current month', () => { 38 | expect(calendar.populateDaysCurrentMonth().length).toBe(31); 39 | }); 40 | 41 | test('navigate years forward', () => { 42 | calendar.navYear(1); 43 | expect(calendar._date.getFullYear()).toBe(1983); 44 | }); 45 | 46 | test('navigate years backward', () => { 47 | calendar.navYear(-1); 48 | expect(calendar._date.getFullYear()).toBe(1981); 49 | }); 50 | 51 | test('navigate months forward', () => { 52 | calendar.navMonth(1); 53 | expect(calendar._date.getMonth()).toBe(1); 54 | }); 55 | 56 | test('navigate months backward', () => { 57 | calendar.navMonth(-1); 58 | expect(calendar._date.getMonth()).toBe(11); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /src/components/calendar/tests/snapshots.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'calendar'; 4 | 5 | describe(component, () => { 6 | const snap = snapIt(component); 7 | 8 | describe('renders', () => { 9 | snap('a default calendar', ''); 10 | snap('a coloured calendar', ''); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/card/blaze-card-body.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-card-body', 5 | }) 6 | export class CardBody { 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/card/blaze-card-footer.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-card-footer', 5 | }) 6 | export class CardFooter { 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/card/blaze-card-header.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-card-header', 5 | }) 6 | export class CardHeader { 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/card/blaze-card.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-card', 5 | }) 6 | export class Card { 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/card/blaze-media-body.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-media-body', 5 | }) 6 | export class MediaBody { 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/card/blaze-media-image.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Prop } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-media-image', 5 | }) 6 | export class MediaImage { 7 | @Prop() 8 | src: string; 9 | 10 | @Prop() 11 | alt: string; 12 | 13 | render() { 14 | return ( 15 |
16 | {this.alt} 17 |
18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/components/card/blaze-media-item.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-media-item', 5 | }) 6 | export class MediaItem { 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/card/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-media-item 2 | 3 | 4 | 5 | 6 | ---------------------------------------------- 7 | 8 | *Built with [StencilJS](https://stenciljs.com/)* 9 | -------------------------------------------------------------------------------- /src/components/card/tests/__snapshots__/snapshots.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`card renders a card 1`] = `"
"`; 4 | 5 | exports[`card renders all the child elements correctly 1`] = `"

Heading
Sub-heading

Lorem ipsum dolor sit amet, feugiat corpora ex eam. Inciderint eloquentiam sea et.

"`; 6 | 7 | exports[`card renders all the child elements correctly 2`] = ` 8 | "

Title Subtitle

Lorem ipsum dolor sit amet, feugiat corpora ex eam. Lorem ipsum dolor sit amet, feugiat corpora ex eam. Lorem ipsum dolor 9 | sit amet, feugiat corpora ex eam.

" 10 | `; 11 | -------------------------------------------------------------------------------- /src/components/card/tests/snapshots.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'card'; 4 | 5 | describe(component, () => { 6 | const snap = snapIt(component); 7 | 8 | describe('renders', () => { 9 | snap('a card', ''); 10 | snap( 11 | 'all the child elements correctly', 12 | ` 13 | 14 | 15 |

16 | Heading 17 |
Sub-heading
18 |

19 |
20 | 21 |

22 | Lorem ipsum dolor sit amet, feugiat corpora ex eam. Inciderint eloquentiam sea et. 23 |

24 |
25 | 26 |
27 | 28 | 29 |
30 |
31 |
` 32 | ); 33 | snap( 34 | 'all the child elements correctly', 35 | ` 36 | 37 | 38 | 39 |

Title 40 | Subtitle 41 |

42 |

43 | Lorem ipsum dolor sit amet, feugiat corpora ex eam. Lorem ipsum dolor sit amet, feugiat corpora ex eam. Lorem ipsum dolor 44 | sit amet, feugiat corpora ex eam. 45 |

46 |
47 |
48 |
` 49 | ); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /src/components/counter/CountUp.ts: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | countUp.js 4 | by @inorganik 5 | 6 | */ 7 | 8 | // target = id of html element or var of previously selected html element where counting occurs 9 | // startVal = the value you want to begin at 10 | // endVal = the value you want to arrive at 11 | // decimals = number of decimal places, default 0 12 | // duration = duration of animation in seconds, default 2 13 | // options = optional object of options (see below) 14 | 15 | var CountUp = function (target, startVal, endVal, decimals, duration, options) { 16 | var self = this; 17 | self.version = function () { 18 | return '1.9.3'; 19 | }; 20 | 21 | // default options 22 | self.options = { 23 | useEasing: true, // toggle easing 24 | useGrouping: true, // 1,000,000 vs 1000000 25 | separator: ',', // character to use as a separator 26 | decimal: '.', // character to use as a decimal 27 | easingFn: easeOutExpo, // optional custom easing function, default is Robert Penner's easeOutExpo 28 | formattingFn: formatNumber, // optional custom formatting function, default is formatNumber above 29 | prefix: '', // optional text before the result 30 | suffix: '', // optional text after the result 31 | numerals: [], // optionally pass an array of custom numerals for 0-9 32 | }; 33 | 34 | // extend default options with passed options object 35 | if (options && typeof options === 'object') { 36 | for (var key in self.options) { 37 | if (options.hasOwnProperty(key) && options[key] !== null) { 38 | self.options[key] = options[key]; 39 | } 40 | } 41 | } 42 | 43 | if (self.options.separator === '') { 44 | self.options.useGrouping = false; 45 | } else { 46 | // ensure the separator is a string (formatNumber assumes this) 47 | self.options.separator = '' + self.options.separator; 48 | } 49 | 50 | // make sure requestAnimationFrame and cancelAnimationFrame are defined 51 | // polyfill for browsers without native support 52 | // by Opera engineer Erik Möller 53 | var lastTime = 0; 54 | var vendors = ['webkit', 'moz', 'ms', 'o']; 55 | for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 56 | window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 57 | window.cancelAnimationFrame = 58 | window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']; 59 | } 60 | if (!window.requestAnimationFrame) { 61 | window.requestAnimationFrame = function (callback) { 62 | var currTime = new Date().getTime(); 63 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 64 | var id = window.setTimeout(function () { 65 | callback(currTime + timeToCall); 66 | }, timeToCall); 67 | lastTime = currTime + timeToCall; 68 | return id; 69 | }; 70 | } 71 | if (!window.cancelAnimationFrame) { 72 | window.cancelAnimationFrame = function (id) { 73 | clearTimeout(id); 74 | }; 75 | } 76 | 77 | function formatNumber(num) { 78 | var neg = num < 0, 79 | x, 80 | x1, 81 | x2, 82 | x3, 83 | i, 84 | len; 85 | num = Math.abs(num).toFixed(self.decimals); 86 | num += ''; 87 | x = num.split('.'); 88 | x1 = x[0]; 89 | x2 = x.length > 1 ? self.options.decimal + x[1] : ''; 90 | if (self.options.useGrouping) { 91 | x3 = ''; 92 | for (i = 0, len = x1.length; i < len; ++i) { 93 | if (i !== 0 && i % 3 === 0) { 94 | x3 = self.options.separator + x3; 95 | } 96 | x3 = x1[len - i - 1] + x3; 97 | } 98 | x1 = x3; 99 | } 100 | // optional numeral substitution 101 | if (self.options.numerals.length) { 102 | x1 = x1.replace(/[0-9]/g, function (w) { 103 | return self.options.numerals[+w]; 104 | }); 105 | x2 = x2.replace(/[0-9]/g, function (w) { 106 | return self.options.numerals[+w]; 107 | }); 108 | } 109 | return (neg ? '-' : '') + self.options.prefix + x1 + x2 + self.options.suffix; 110 | } 111 | // Robert Penner's easeOutExpo 112 | function easeOutExpo(t, b, c, d) { 113 | return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b; 114 | } 115 | function ensureNumber(n) { 116 | return typeof n === 'number' && !isNaN(n); 117 | } 118 | 119 | self.initialize = function () { 120 | if (self.initialized) return true; 121 | 122 | self.error = ''; 123 | self.d = typeof target === 'string' ? document.getElementById(target) : target; 124 | if (!self.d) { 125 | self.error = '[CountUp] target is null or undefined'; 126 | return false; 127 | } 128 | self.startVal = Number(startVal); 129 | self.endVal = Number(endVal); 130 | // error checks 131 | if (ensureNumber(self.startVal) && ensureNumber(self.endVal)) { 132 | self.decimals = Math.max(0, decimals || 0); 133 | self.dec = Math.pow(10, self.decimals); 134 | self.duration = Number(duration) * 1000 || 2000; 135 | self.countDown = self.startVal > self.endVal; 136 | self.frameVal = self.startVal; 137 | self.initialized = true; 138 | return true; 139 | } else { 140 | self.error = '[CountUp] startVal (' + startVal + ') or endVal (' + endVal + ') is not a number'; 141 | return false; 142 | } 143 | }; 144 | 145 | // Print value to target 146 | self.printValue = function (value) { 147 | var result = self.options.formattingFn(value); 148 | 149 | if (self.d.tagName === 'INPUT') { 150 | this.d.value = result; 151 | } else if (self.d.tagName === 'text' || self.d.tagName === 'tspan') { 152 | this.d.textContent = result; 153 | } else { 154 | this.d.innerHTML = result; 155 | } 156 | }; 157 | 158 | self.count = function (timestamp) { 159 | if (!self.startTime) { 160 | self.startTime = timestamp; 161 | } 162 | 163 | self.timestamp = timestamp; 164 | var progress = timestamp - self.startTime; 165 | self.remaining = self.duration - progress; 166 | 167 | // to ease or not to ease 168 | if (self.options.useEasing) { 169 | if (self.countDown) { 170 | self.frameVal = self.startVal - self.options.easingFn(progress, 0, self.startVal - self.endVal, self.duration); 171 | } else { 172 | self.frameVal = self.options.easingFn(progress, self.startVal, self.endVal - self.startVal, self.duration); 173 | } 174 | } else { 175 | if (self.countDown) { 176 | self.frameVal = self.startVal - (self.startVal - self.endVal) * (progress / self.duration); 177 | } else { 178 | self.frameVal = self.startVal + (self.endVal - self.startVal) * (progress / self.duration); 179 | } 180 | } 181 | 182 | // don't go past endVal since progress can exceed duration in the last frame 183 | if (self.countDown) { 184 | self.frameVal = self.frameVal < self.endVal ? self.endVal : self.frameVal; 185 | } else { 186 | self.frameVal = self.frameVal > self.endVal ? self.endVal : self.frameVal; 187 | } 188 | 189 | // decimal 190 | self.frameVal = Math.round(self.frameVal * self.dec) / self.dec; 191 | 192 | // format and print value 193 | self.printValue(self.frameVal); 194 | 195 | // whether to continue 196 | if (progress < self.duration) { 197 | self.rAF = requestAnimationFrame(self.count); 198 | } else { 199 | if (self.callback) self.callback(); 200 | } 201 | }; 202 | // start your animation 203 | self.start = function (callback) { 204 | if (!self.initialize()) return; 205 | self.callback = callback; 206 | self.rAF = requestAnimationFrame(self.count); 207 | }; 208 | // toggles pause/resume animation 209 | self.pauseResume = function () { 210 | if (!self.paused) { 211 | self.paused = true; 212 | cancelAnimationFrame(self.rAF); 213 | } else { 214 | self.paused = false; 215 | delete self.startTime; 216 | self.duration = self.remaining; 217 | self.startVal = self.frameVal; 218 | requestAnimationFrame(self.count); 219 | } 220 | }; 221 | // reset to startVal so animation can be run again 222 | self.reset = function () { 223 | self.paused = false; 224 | delete self.startTime; 225 | self.initialized = false; 226 | if (self.initialize()) { 227 | cancelAnimationFrame(self.rAF); 228 | self.printValue(self.startVal); 229 | } 230 | }; 231 | // pass a new endVal and start animation 232 | self.update = function (newEndVal) { 233 | if (!self.initialize()) return; 234 | newEndVal = Number(newEndVal); 235 | if (!ensureNumber(newEndVal)) { 236 | self.error = '[CountUp] update() - new endVal is not a number: ' + newEndVal; 237 | return; 238 | } 239 | self.error = ''; 240 | if (newEndVal === self.frameVal) return; 241 | cancelAnimationFrame(self.rAF); 242 | self.paused = false; 243 | delete self.startTime; 244 | self.startVal = self.frameVal; 245 | self.endVal = newEndVal; 246 | self.countDown = self.startVal > self.endVal; 247 | self.rAF = requestAnimationFrame(self.count); 248 | }; 249 | 250 | // format startVal on initialization 251 | if (self.initialize()) self.printValue(self.startVal); 252 | }; 253 | 254 | export default CountUp; 255 | -------------------------------------------------------------------------------- /src/components/counter/blaze-counter.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Element, Event, EventEmitter, Prop, Method } from '@stencil/core'; 2 | import CountUp from './CountUp'; 3 | 4 | @Component({ 5 | tag: 'blaze-counter', 6 | }) 7 | export class Counter { 8 | @Element() 9 | el: HTMLElement; 10 | 11 | @Prop() 12 | autoStart: boolean; 13 | 14 | @Prop() 15 | startValue: number = 0; 16 | 17 | @Prop() 18 | endValue: number; 19 | 20 | @Prop() 21 | decimals: number = 0; 22 | 23 | @Prop() 24 | duration: number = 4; 25 | 26 | @Prop() 27 | delay: number = 500; 28 | 29 | @Prop() 30 | easing: boolean = true; 31 | 32 | @Prop() 33 | grouping: boolean = true; 34 | 35 | @Prop() 36 | separator: string = ','; 37 | 38 | @Prop() 39 | decimal: string = '.'; 40 | 41 | @Event({ eventName: 'finish' }) 42 | onFinish: EventEmitter; 43 | 44 | animation: any; 45 | timer: any; 46 | 47 | componentDidLoad() { 48 | const target = this.el.querySelector('.c-counter__amount'); 49 | this.animation = new CountUp(target, this.startValue, this.endValue, this.decimals, this.duration, { 50 | useEasing: this.easing, 51 | useGrouping: this.grouping, 52 | separator: this.separator, 53 | decimal: this.decimal, 54 | }); 55 | 56 | if (this.autoStart) { 57 | this.start(); 58 | } 59 | } 60 | 61 | @Method() 62 | async start() { 63 | this.timer = setTimeout(() => { 64 | this.animation.start(() => { 65 | this.onFinish.emit(); 66 | }); 67 | }, this.delay); 68 | } 69 | 70 | disconnectedCallback() { 71 | clearTimeout(this.timer); 72 | } 73 | 74 | @Method() 75 | async reset() { 76 | this.animation.reset(); 77 | } 78 | 79 | @Method() 80 | async update(value: number) { 81 | this.animation.update(value); 82 | } 83 | 84 | @Method() 85 | async restart() { 86 | this.animation.reset(); 87 | this.animation.start(); 88 | } 89 | 90 | @Method() 91 | async pauseResume() { 92 | this.animation.pauseResume(); 93 | } 94 | 95 | render() { 96 | return ( 97 |
98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/components/counter/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-counter 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | ------------ | ------------- | ----------- | --------- | ----------- | 10 | | `autoStart` | `auto-start` | | `boolean` | `undefined` | 11 | | `decimal` | `decimal` | | `string` | `'.'` | 12 | | `decimals` | `decimals` | | `number` | `0` | 13 | | `delay` | `delay` | | `number` | `500` | 14 | | `duration` | `duration` | | `number` | `4` | 15 | | `easing` | `easing` | | `boolean` | `true` | 16 | | `endValue` | `end-value` | | `number` | `undefined` | 17 | | `grouping` | `grouping` | | `boolean` | `true` | 18 | | `separator` | `separator` | | `string` | `','` | 19 | | `startValue` | `start-value` | | `number` | `0` | 20 | 21 | 22 | ## Events 23 | 24 | | Event | Description | Type | 25 | | -------- | ----------- | ------------------ | 26 | | `finish` | | `CustomEvent` | 27 | 28 | 29 | ## Methods 30 | 31 | ### `pauseResume() => Promise` 32 | 33 | 34 | 35 | #### Returns 36 | 37 | Type: `Promise` 38 | 39 | 40 | 41 | ### `reset() => Promise` 42 | 43 | 44 | 45 | #### Returns 46 | 47 | Type: `Promise` 48 | 49 | 50 | 51 | ### `restart() => Promise` 52 | 53 | 54 | 55 | #### Returns 56 | 57 | Type: `Promise` 58 | 59 | 60 | 61 | ### `start() => Promise` 62 | 63 | 64 | 65 | #### Returns 66 | 67 | Type: `Promise` 68 | 69 | 70 | 71 | ### `update(value: number) => Promise` 72 | 73 | 74 | 75 | #### Returns 76 | 77 | Type: `Promise` 78 | 79 | 80 | 81 | 82 | ---------------------------------------------- 83 | 84 | *Built with [StencilJS](https://stenciljs.com/)* 85 | -------------------------------------------------------------------------------- /src/components/counter/tests/__snapshots__/snapshots.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`counter renders a default empty counter 1`] = `"
"`; 4 | -------------------------------------------------------------------------------- /src/components/counter/tests/snapshots.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'counter'; 4 | 5 | describe(component, () => { 6 | const snap = snapIt(component); 7 | 8 | describe('renders', () => { 9 | snap('a default empty counter', ``); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /src/components/demo/README.md: -------------------------------------------------------------------------------- 1 | # blaze-demo 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | ---------- | ---------- | ----------- | --------- | ----------- | 10 | | `classes` | `classes` | | `string` | `''` | 11 | | `code` | `code` | | `string` | `undefined` | 12 | | `demo` | `demo` | | `boolean` | `true` | 13 | | `language` | `language` | | `string` | `''` | 14 | 15 | 16 | ---------------------------------------------- 17 | 18 | *Built with [StencilJS](https://stenciljs.com/)* 19 | -------------------------------------------------------------------------------- /src/components/demo/blaze-demo.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Prop, State } from '@stencil/core'; 2 | 3 | declare const hljs: any; 4 | 5 | @Component({ 6 | tag: 'blaze-demo', 7 | }) 8 | export class Demo { 9 | codeRef: any; 10 | 11 | @Prop() 12 | classes: string = ''; 13 | 14 | @Prop() 15 | code: string; 16 | 17 | @Prop() 18 | language: string = ''; 19 | 20 | @Prop() 21 | demo: boolean = true; 22 | 23 | @State() 24 | markup: string; 25 | 26 | componentWillLoad() { 27 | this.markup = this.code; 28 | } 29 | 30 | updateDemo(e) { 31 | this.markup = e.target.innerText; 32 | } 33 | 34 | highlight() { 35 | if (typeof hljs !== 'undefined') { 36 | hljs.highlightElement(this.codeRef); 37 | } 38 | } 39 | 40 | componentDidUpdate() { 41 | this.highlight(); 42 | } 43 | 44 | componentDidLoad() { 45 | this.highlight(); 46 | } 47 | 48 | render() { 49 | return [ 50 | this.demo && ( 51 |
56 | ), 57 |
58 |
59 |            (this.codeRef = ref)}
61 |             aria-hidden
62 |             tabindex="-1"
63 |             class={`u-code u-code--multiline ${this.language}`}
64 |             contenteditable={this.demo}
65 |             onBlur={(e) => this.updateDemo(e)}
66 |           >
67 |             {this.markup}
68 |           
69 |         
70 |
, 71 | ]; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/components/demo/tests/__snapshots__/snapshots.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`demo renders renders a badge component and a code area 1`] = `"YO!\\" class=\\"hydrated\\">
YO!
<blaze-badge type=\\"success\\">YO!</blaze-badge>
"`; 4 | 5 | exports[`demo renders renders correctly with no props 1`] = `"
"`; 6 | -------------------------------------------------------------------------------- /src/components/demo/tests/snapshots.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'demo'; 4 | 5 | describe(component, () => { 6 | const snap = snapIt(component); 7 | 8 | describe('renders', () => { 9 | snap('renders correctly with no props', ''); 10 | snap( 11 | 'renders a badge component and a code area', 12 | `` 13 | ); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/components/divider/blaze-divider.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Element, State, Prop } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-divider', 5 | }) 6 | export class Divider { 7 | @Element() 8 | el: HTMLElement; 9 | 10 | @Prop() 11 | type: string = 'solid'; 12 | 13 | @State() 14 | content: boolean; 15 | 16 | componentWillLoad() { 17 | this.content = !!this.el.innerHTML; 18 | } 19 | 20 | render() { 21 | const typeClass = this.type ? `c-divider--${this.type}` : ''; 22 | return ( 23 |
24 | {this.content && ( 25 | 26 | 27 | 28 | )} 29 |
30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/divider/readme.md: -------------------------------------------------------------------------------- 1 | # blaze-divider 2 | 3 | 4 | 5 | 6 | ## Properties 7 | 8 | | Property | Attribute | Description | Type | Default | 9 | | -------- | --------- | ----------- | -------- | --------- | 10 | | `type` | `type` | | `string` | `'solid'` | 11 | 12 | 13 | ---------------------------------------------- 14 | 15 | *Built with [StencilJS](https://stenciljs.com/)* 16 | -------------------------------------------------------------------------------- /src/components/divider/tests/__snapshots__/snapshot.e2e.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`divider renders content 1`] = `"
Hello, world
"`; 4 | 5 | exports[`divider renders correctly 1`] = `"
"`; 6 | -------------------------------------------------------------------------------- /src/components/divider/tests/snapshot.e2e.ts: -------------------------------------------------------------------------------- 1 | import { snapIt } from '../../../test'; 2 | 3 | const component = 'divider'; 4 | 5 | describe(component, () => { 6 | const snap = snapIt(component); 7 | 8 | describe('renders', () => { 9 | snap('correctly', ''); 10 | snap('content', 'Hello, world'); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/drawer/blaze-drawer.tsx: -------------------------------------------------------------------------------- 1 | import { h, Component, Event, EventEmitter, Prop, Method, State } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'blaze-drawer', 5 | }) 6 | export class Drawer { 7 | @Prop() 8 | open: boolean = false; 9 | 10 | @Prop() 11 | dismissible: boolean = false; 12 | 13 | @Prop() 14 | position: string = 'bottom'; 15 | 16 | @Prop() 17 | shouldClose: () => boolean; 18 | 19 | @State() 20 | _isOpen: boolean = false; 21 | 22 | @Event({ eventName: 'close' }) 23 | onClose: EventEmitter; 24 | 25 | @Method() 26 | async close() { 27 | if (!this.shouldClose || this.shouldClose()) { 28 | this._isOpen = false; 29 | this.onClose.emit(); 30 | } 31 | } 32 | 33 | @Method() 34 | async show() { 35 | this._isOpen = true; 36 | } 37 | 38 | @Method() 39 | async isOpen() { 40 | return this._isOpen; 41 | } 42 | 43 | componentWillLoad() { 44 | this._isOpen = this.open; 45 | } 46 | 47 | dismiss() { 48 | if (this.dismissible) this.close(); 49 | } 50 | 51 | render() { 52 | const drawerIsOpenClass = this._isOpen ? 'o-drawer--visible' : ''; 53 | const overlayIsOpenClass = this._isOpen ? 'c-overlay--visible' : ''; 54 | 55 | return [ 56 |