├── .babelrc ├── .eslintignore ├── .eslintrc.json ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── --bug-report.md │ ├── -feature-request.md │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .hound.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── changelog.md ├── dist ├── alert.js ├── bulma.js ├── dropdown.js ├── file.js ├── message.js ├── modal.js ├── navbar.js ├── notification.js ├── panel.js ├── panelTabs.js └── tabs.js ├── package-lock.json ├── package.json ├── src ├── ConfigBag.js ├── Data.js ├── bulma.js ├── core.js ├── dismissableComponent.js ├── plugin.js └── plugins │ ├── alert.js │ ├── dropdown.js │ ├── file.js │ ├── message.js │ ├── modal.js │ ├── navbar.js │ ├── notification.js │ ├── panelTabs.js │ └── tabs.js ├── supporters.md ├── tests ├── alert.js ├── core.js ├── helpers.js └── test-runner.html ├── webpack.config.js ├── yarn-error.log └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"], 3 | "plugins": ["transform-object-rest-spread"] 4 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | docs/ 3 | scripts/ 4 | webpack.config.js -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "sourceType": "module", 9 | "ecmaFeatures": { 10 | "experimentalObjectRestSpread": true 11 | } 12 | }, 13 | "rules": { 14 | "indent": [ 15 | "error", 16 | 4 17 | ], 18 | "linebreak-style": [ 19 | "error", 20 | "unix" 21 | ], 22 | "quotes": [ 23 | "error", 24 | "single" 25 | ], 26 | "semi": [ 27 | "error", 28 | "always" 29 | ], 30 | "no-unused-vars": [ 31 | "warn", 32 | "all" 33 | ] 34 | } 35 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: VizuaaLOG 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41EBug report" 3 | about: Create a bug report to help us improve 4 | title: '' 5 | labels: bug, is-unconfirmed 6 | assignees: VizuaaLOG 7 | 8 | --- 9 | 10 | **Versions** 11 | BulmaJS: 12 | Browser: 13 | Operating system: 14 | 15 | **Describe the bug** 16 | A clear and concise description of what the bug is. 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/-feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "✨Feature request" 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: is-feature 6 | assignees: VizuaaLOG 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/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 | **Versions** 11 | BulmaJS: 12 | Browser: 13 | Operating system: 14 | 15 | **Describe the bug** 16 | A clear and concise description of what the bug is. 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | mix-manifest.json 3 | .DS_Store 4 | index.html 5 | .idea -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | eslint: 2 | enabled: true 3 | config_file: .eslintrc.json 4 | ignore_file: .eslintignore -------------------------------------------------------------------------------- /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 tom@tomerbe.co.uk. 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 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | If you would like to contribute a plugin, fix a bug or fix some horrible typo in the docs please submit a PR if you can, alternativly create an issue! Please note, while the code base doesn't follow any strict style, please try to ensure your PRs follow the general convention you see (there may be times the convention isn't followed, if you see this submit an issue/PR and slap me on the wrist :)). Any PR/issue at this time will not be rejected but may have some alternation to the code. 2 | 3 | If you are submitting a plugin, please also submit the relevant documentation. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Thomas Erbe 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 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **IMPORTANT: Please do not create a Pull Request without creating an issue first.** 2 | 3 | *Any change needs to be discussed before proceeding. Failure to do so may result in the rejection of the pull request.* 4 | 5 | Please provide enough information so that others can review your pull request: 6 | 7 | 8 | 9 | Explain the **details** for making this change. What existing problem does the pull request solve? 10 | 11 | 12 | 13 | **Test plan (required)** 14 | 15 | Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. 16 | 17 | 18 | 19 | **Code formatting** 20 | 21 | Please ensure that running `npm run lint` passes. Please correct any issues. 22 | 23 | **Closing issues** 24 | 25 | Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such). 26 | 27 | **Please delete all comments here before submitting your PR, ensuring you have followed the guidelines.** 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BulmaJS 2 | ======== 3 | ![npm](https://img.shields.io/npm/v/@vizuaalog/bulmajs) 4 | ![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/VizuaaLOG/BulmaJS) 5 | [![GitHub license](https://img.shields.io/github/license/VizuaaLOG/BulmaJS)](https://github.com/VizuaaLOG/BulmaJS/blob/master/LICENSE) 6 | 7 | This is an unofficial JavaScript extension for the [Bulma CSS framework](http://bulma.io). 8 | 9 | **BulmaJS is still in development, you can keep track of current, pending, completed and planned tasks on the official [Trello board](https://trello.com/b/XS93oQNi/bulmajs).** 10 | 11 | **tl;dr** 12 | GIMME THE DOCS! [Well here you go](https://bulmajs.tomerbe.co.uk/). 13 | 14 | Bulma is an excellent CSS framework and the perfect alternative to Bootstrap. One of the great things about Bulma is that it's **JUST** CSS; there is no JavaScript included. This makes it *super* lightweight. Some components *do* require JavaScript however, and that is where BulmaJS comes in! 15 | 16 | BulmaJS is written in ES6 and provides a plugin based system for adding interaction to the CSS components. BulmaJS is an ongoing project. As new components are added to Bulma, if they require JavaScript then they will also be added to BulmaJS. We are not limited to the core components though. If you have a plugin you would like incorporated, please submit a PR. If you have written a CSS extension to Bulma and would like some JS written, please submit an issue and we may write the JS functionality for you. 17 | 18 | # Contributing 19 | If you would like to contribute a plugin, fix a bug or fix some horrible typo in the docs, then please submit a PR if you can! Alternatively, create an issue. Please note, while the code base doesn't follow any strict style, try to ensure your PRs follow the general convention that you see. There may be times the convention isn't followed, so if you see this submit an issue/PR and slap me on the wrist :). Any PR/issue at this time will not be rejected but may have some alteration to the code. 20 | 21 | If you are submitting a plugin, please also submit the relevant documentation. 22 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # 0.12.2 2 | + **Bug** [131](https://github.com/VizuaaLOG/BulmaJS/issues/131) Navbar will now detect if window is defined before trying to attach sticky events 3 | + **Bug** [138](https://github.com/VizuaaLOG/BulmaJS/issues/138) Do not assume the navbar-burger element always exists 4 | 5 | # 0.12.1 6 | + **Bug** [120](https://github.com/VizuaaLOG/BulmaJS/issues/120) Panel tabs will now properly take into account `is-active`. On `init` this will become the starting tab 7 | 8 | # 0.12.0 9 | + **Feature** [107](https://github.com/VizuaaLOG/BulmaJS/issues/107) Add support for the navbar dropdown 10 | + **Feature** [94](https://github.com/VizuaaLOG/BulmaJS/issues/94) Add a new global `onLoaded` callback that can be defined within the `window.bulmaOptions` object. This is called once the library has been loaded and the `DOMContentLoaded` event has been fired. 11 | + **Feature** [102](https://github.com/VizuaaLOG/BulmaJS/issues/102) Add support for panel tabs. 12 | + **Tweak** [95](https://github.com/VizuaaLOG/BulmaJS/issues/95) The `parseDocument` method will now check the given context against the internal plugins 13 | + **Refactor** Add a `Bulma.getGlobalConfig(key: string, default: mixed)` method to the core. This allows easy access to properties within the `window.bulmaOptions` object and accepts a default as it's second parameter, this is `null` by default. 14 | 15 | # 0.11.0 16 | + **Feature** Add a `getFilename` method to the file plugin. 17 | + **Feature** Add a `setActive` method to the tabs plugin. This allows for programatically changing the active tab. 18 | + **Feature** Add the ability to enable / disable a navbar's hide on scroll behavior at any time with `enableHideOnScroll` and `disableHideOnScroll` 19 | + **Feature** Add the ability to enable / disable a navbar's sticky behaviour at any time with `enableSticky` and `disableSticky`. 20 | + **Feature** Allow a plugin instance to be grabbed from an existing element. For example `Bulma('.navbar').data('navbar')` will return the navbar plugin instance associated with the element. 21 | + **Feature** [#71](https://github.com/VizuaaLOG/BulmaJS/issues/71) The modal plugin can have it's HTML content loaded via an AJAX request using the new `bodyUrl` option. 22 | + **Bug** [#71](https://github.com/VizuaaLOG/BulmaJS/issues/71) Fixed a typo within the Alert plugin that checks if the alert should be destroyed 23 | + **Refactor** Adjust how options are internally handled, renaming variables to `config` and implementing a `ConfigBag` helper class. 24 | + **Refactor** Remove the create method in favour of using the plugin's constructor method. 25 | + **Refactor** Rewrite the API so that you can select an element `Bulma(selector)` and then call the relevant plugin. The core will then automatically pss the selector element as the root, or create an empty div as the root if nothing is provided. 26 | 27 | # 0.10.4 28 | + **Bug** [#100](https://github.com/VizuaaLOG/BulmaJS/issues/100) Fix the `npm run production` script failing 29 | 30 | # 0.10.3 31 | + **Bug** Change the default hide offset value to null rather than 0 [@luksurious](https://github.com/luksurious) [Pull Request](https://github.com/VizuaaLOG/BulmaJS/pull/91). 32 | 33 | # 0.10.2 34 | + **Bug** [#90](https://github.com/VizuaaLOG/BulmaJS/issues/90) Hiding navbar on scrolls creates empty space, not reappearing as expected [@luksurious](https://github.com/luksurious) [Pull Request](https://github.com/VizuaaLOG/BulmaJS/pull/91). 35 | 36 | # 0.10.1 37 | + **Bug** `Backported from master` [#88](https://github.com/VizuaaLOG/BulmaJS/issues/88) Ensure the keyup event handler is removed then a Modal is destroyed. Thanks to [@luksurious](https://github.com/luksurious) [Pull Request](https://github.com/VizuaaLOG/BulmaJS/pull/89). 38 | 39 | # 0.10.0 40 | + **Feature** Brand new documentation! To improve the ease of updating the docs the documentation has been ported to a new static site build, [Jigsaw](https://jigsaw.tighten.co/). As part of this it's also had a freshen up, there's still some things that needs tweaking but we're mostly there. The docs have also been moved out of the main repository, and are now in a [BulmaJS-Docs](https://github.com/VizuaaLOG/BulmaJS-Docs) repository. You can access the new docs by visiting [bulmajs.tomerbe.co.uk](https://bulmajs.tomerbe.co.uk). If you would still like to use the pre 0.10 docs you can do so by visiting [https://vizuaalog.github.io/BulmaJS/](https://vizuaalog.github.io/BulmaJS/); 41 | + **Feature** [#70](https://github.com/VizuaaLOG/BulmaJS/issues/70) The automatic document parsing can be disabled using the `bulmaOptions` object. 42 | + **Tweak** [#70](https://github.com/VizuaaLOG/BulmaJS/issues/66) The `traverseDOM` method now accepts an optional parameter specifying an HTMLElement to use as the scope when parsing the document. 43 | + **Tweak** The `destroyOnConfirm` and `destroyOnCancel` options on the alert plugin have been moved to the buttons object and renamed to `destroy`. 44 | + **Tweak** The `closeOnConfirm` and `closeOnCancel` options on the alert plugin have been moved to the buttons object and renamed to `close`. 45 | + **Tweak** The `onConfirm` property on the alert object has been moved to the `confirm` object and renamed to `onClick`. 46 | + **Tweak** The `onCancel` property on the alert object has been moved to the `cancel` object and renamed to `onClick`. 47 | 48 | # 0.9.1 49 | + **Feature** [#66](https://github.com/VizuaaLOG/BulmaJS/issues/66) The alert plugin now has `closeOnCancel`, `closeOnConfirm`, `destroyOnCancel` and `destroyOnConfirm` options. Thanks to [@alexcanana](https://github.com/alexcanana). 50 | + **Bug** [#69](https://github.com/VizuaaLOG/BulmaJS/issues/69) The modal plugin now correctly handles the `is-clipped` class. Thanks to [@alexcanana](https://github.com/alexcanana). 51 | 52 | # 0.9.0 53 | + **Feature** [#64](https://github.com/VizuaaLOG/BulmaJS/issues/64) The alert plugin now has a `showHeader` option. This defaults to `true`, setting it to `false` will hide the alert's header. 54 | + **Feature** [#64](https://github.com/VizuaaLOG/BulmaJS/issues/64) The alert plugin now has `destroyOnConfirm` and `destroyOnCancel` options. These both default to `true` setting them to `false` will prevent the alert from destroying itself when the relevant button is clicked. 55 | + **Feature** [#64](https://github.com/VizuaaLOG/BulmaJS/issues/64) The alert plugin now has additional options for buttons. Supplying an object will allow you to also provide additional classes for each button. More details in the alert documentation. 56 | + **Tweak** [#65](https://github.com/VizuaaLOG/BulmaJS/issues/65) The alert callbacks now include an `event` argument. Thanks to [@alexcanana](https://github.com/alexcanana). 57 | + **Bug** Fixed a bug that caused `.modal` and `modal` classes to be applied to a modal 58 | 59 | # 0.8.1 60 | + **Bug** [63](https://github.com/VizuaaLOG/BulmaJS/issues/63) Use a helper `each` method instead of `forEach`. This is to add support for IE11. 61 | 62 | # 0.8.0 63 | + **Feature** [#47](https://github.com/VizuaaLOG/BulmaJS/issues/47) Implement an alert box. This extends the modal plugin and uses the card style. The type of alert box can be specified, adjusting the colours used. 64 | + **Tweak** [#61](https://github.com/VizuaaLOG/BulmaJS/pull/61) Remove the assumed window assignment, if you rely on this then you'll need to ensure you add this to you applications JS. 65 | + **Tweak BREAKING** The modal's `type` attribute has been renamed to `style`. This value is changing the 'style' of the modal, and so this better reflects the option. 66 | + **Bug** [#61](https://github.com/VizuaaLOG/BulmaJS/pull/61) Add a default export for the full BulmaJS bundle. 67 | + **Deprecated** Wikiki's Accordion plugin has been Deprecated from the core and will be removed in the 1.0 release. If you are using Wikiki's Accordion extension then it's recommended to use the official JS integration for it. 68 | + **Deprecated** Wikiki's Calendar plugin has been Deprecated from the core and will be removed from the 1.0 release. If you are using Wikiki's Calendar extension then it's recommended to use the official JS integration for it. 69 | 70 | # 0.7.0 71 | + **Feature** [#27](https://github.com/VizuaaLOG/BulmaJS/issues/27) Modals can now be closed by pressing the 72 | `escape` key. 73 | + **Feature** [#27](https://github.com/VizuaaLOG/BulmaJS/issues/27) Modals can now be closed by clicking outside of an open modal. 74 | + **Feature** [#27](https://github.com/VizuaaLOG/BulmaJS/issues/27) When a modal is opening the `is-clipped` class is applied to the `body` element. 75 | + **Feature** Added a new `findElement` helper method to the BulmaJS core. This method will allow plugins to easily normalise an arugment that needs to be either an HTMLElement or a query selector string. 76 | + **Feature** [#27](https://github.com/VizuaaLOG/BulmaJS/issues/27) Rewrite the modal plugin, providing a new functionality, options and a full Javascript API. 77 | + **Feature** [#50](https://github.com/VizuaaLOG/BulmaJS/issues/50) Allow multiple plugins to be attached to a single class. 78 | + **Feature** [#39](https://github.com/VizuaaLOG/BulmaJS/issues/39) Implement a uniformed plugin interface 79 | + **Feature** [#40](https://github.com/VizuaaLOG/BulmaJS/issues/49) Add the ability for the navbar to be sticky at a certain threshold. Also add the ability for the navbar to hide on scroll down and reappear on scroll up. Thanks to [rdhar](https://github.com/rdhar) for the suggestion! 80 | + **Tweak** [#51](https://github.com/VizuaaLOG/BulmaJS/pull/51) Thanks to [apecell](https://github.com/apecell) the file plugin now supports drag and drop. 81 | + **Bug** [#27](https://github.com/VizuaaLOG/BulmaJS/issues/27) `closeButton` event is now correctly removed when destroying a modal. 82 | + **Bug** [#53](https://github.com/VizuaaLOG/BulmaJS/issues/53) The tabs plugin now ensures it only grabs the ul/li element that is a direct child of the `tabs-content` element. 83 | + **Bug** [#54](https://github.com/VizuaaLOG/BulmaJS/issues/54) Fix an issue where plugins were sometimes attached twice. 84 | 85 | # 0.6.0 86 | The reason for this being a large version jump is due to the complete overhaul of the documentation. While this doesn't change the functionality of the code, I feel like this is a large enough change to the project to warrant more than a 'patch' version bump. 87 | 88 | + [#36](https://github.com/VizuaaLOG/BulmaJS/issues/36) The navbar burger button now has the `is-active` class toggled. 89 | + [#22](https://github.com/VizuaaLOG/BulmaJS/issues/22) Documentation has had a redesign and a complete rewrite. If you find a typo please blame my keyboard ( :) ) and file an issue or submit a pull request. 90 | 91 | # 0.5.0 92 | + [#38](https://github.com/VizuaaLOG/BulmaJS/pull/38) Adjust how plugins are initialised by using classes instead. Data attributes can still be used for customising the plugins behaviour, if supported. (closes [#20](https://github.com/VizuaaLOG/BulmaJS/issues/20)) 93 | + [43b64cd](https://github.com/VizuaaLOG/BulmaJS/commit/43b64cdea58fe6b512ce95c69172889d75b68179) Add a new option to the tabs plugin that allows tabs to be changed when the user hovers over the tab link. On mobile this will revert back to the click/tap handler due to the lack of a hover event being called. (closes [#35](https://github.com/VizuaaLOG/BulmaJS/issues/35)) 94 | 95 | # 0.4.0 96 | + [#28](https://github.com/VizuaaLOG/BulmaJS/pull/28) Add the option to disable the modal being closable 97 | + [#31](https://github.com/VizuaaLOG/BulmaJS/pull/31) Add automated linting to the CI process (closes [#20](https://github.com/VizuaaLOG/BulmaJS/issues/29)) 98 | 99 | # 0.3.1 100 | + This changelog was added 101 | + Fix Wikiki's CSS files not being loaded on the docs 102 | + Publish to NPM 103 | -------------------------------------------------------------------------------- /dist/alert.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Bulma",[],t):"object"==typeof exports?exports.Bulma=t():e.Bulma=t()}(window,(function(){return function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=10)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o,r=n(2),i=(o=r)&&o.__esModule?o:{default:o};function a(e){return this instanceof a?e instanceof a?e:(e instanceof HTMLElement?this._elem=e:this._elem=document.querySelector(e),e||(this._elem=document.createElement("div")),this._elem.hasOwnProperty(a.id)||(this._elem[a.id]=i.default.uid++),this):new a(e)}a.VERSION="0.12.1",a.id="bulma-"+(new Date).getTime(),a.cache=new i.default,a.plugins={},a.create=function(e,t){if(!e||!a.plugins.hasOwnProperty(e))throw new Error("[BulmaJS] A plugin with the key '"+e+"' has not been registered.");return new a.plugins[e].handler(t)},a.registerPlugin=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(!e)throw new Error("[BulmaJS] Key attribute is required.");a.plugins[e]={priority:n,handler:t},a.prototype[e]=function(t){return new a.plugins[e].handler(t,this)}},a.parseDocument=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=Object.keys(a.plugins).sort((function(e,t){return a.plugins[e].priority1&&void 0!==arguments[1]?arguments[1]:document,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"div",o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],r=t.querySelector(e);if(!r){0===o.length&&(o=e.split(".").filter((function(e){return e})));var i=a.createElement(n,o);return t.appendChild(i),i}return r},a.each=function(e,t){var n=void 0;for(n=0;n=200&&o.status<400?t(a._stripScripts(o.responseText)):n()},o.onerror=function(){return n()},o.send()}))},a._stripScripts=function(e){var t=document.createElement("div");t.innerHTML=e;for(var n=t.getElementsByTagName("script"),o=n.length;o--;)n[o].parentNode.removeChild(n[o]);return t.innerHTML.replace(/ +/g," ")},a.getGlobalConfig=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return window.hasOwnProperty("bulmaOptions")&&window.bulmaOptions.hasOwnProperty(e)?window.bulmaOptions[e]:t},a.prototype.data=function(e,t){return t?(a.cache.set(this._elem[a.id],e,t),this):a.cache.get(this._elem[a.id],e)},a.prototype.destroyData=function(){return a.cache.destroy(this._elem[a.id]),this},a.prototype.getElement=function(){return this._elem},document.addEventListener("DOMContentLoaded",(function(){a.getGlobalConfig("autoParseDocument",!0)&&a.parseDocument(),a.getGlobalConfig("onLoaded")&&a.getGlobalConfig("onLoaded")()})),t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},n=arguments[1];if(u(this,e),t.root=n instanceof a.default?n._elem:n,this.config=new i.default(o({},this.constructor.defaultConfig(),t)),!n&&!this.config.has("parent"))throw new Error("A plugin requires a root and/or a parent.");this.parent=this.config.get("parent",t.root?t.root.parentNode:null),this._events={}}return r(e,null,[{key:"defaultConfig",value:function(){return{}}}]),r(e,[{key:"on",value:function(e,t){this._events.hasOwnProperty(e)||(this._events[e]=[]),this._events[e].push(t)}},{key:"trigger",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this._events.hasOwnProperty(e))for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];if(i(this,e),"object"!==(void 0===t?"undefined":o(t)))throw new TypeError("initialConfig must be of type object.");this._items=t}return r(e,[{key:"set",value:function(e,t){if(!e||!t)throw new Error("A key and value must be provided when setting a new option.");this._items[e]=t}},{key:"has",value:function(e){if(!e)throw new Error("A key must be provided.");return this._items.hasOwnProperty(e)&&this._items[e]}},{key:"get",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return t&&!this.has(e)?"function"==typeof t?t():t:this._items[e]}}]),e}();t.default=a},,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Modal=void 0;var o=function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:0;if(!e)throw new Error("[BulmaJS] Key attribute is required.");u.plugins[e]={priority:n,handler:t},u.prototype[e]=function(t){return new u.plugins[e].handler(t,this)}},u.parseDocument=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=Object.keys(u.plugins).sort((function(e,t){return u.plugins[e].priority1&&void 0!==arguments[1]?arguments[1]:document,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"div",r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],o=t.querySelector(e);if(!o){0===r.length&&(r=e.split(".").filter((function(e){return e})));var i=u.createElement(n,r);return t.appendChild(i),i}return o},u.each=function(e,t){var n=void 0;for(n=0;n=200&&r.status<400?t(u._stripScripts(r.responseText)):n()},r.onerror=function(){return n()},r.send()}))},u._stripScripts=function(e){var t=document.createElement("div");t.innerHTML=e;for(var n=t.getElementsByTagName("script"),r=n.length;r--;)n[r].parentNode.removeChild(n[r]);return t.innerHTML.replace(/ +/g," ")},u.getGlobalConfig=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return window.hasOwnProperty("bulmaOptions")&&window.bulmaOptions.hasOwnProperty(e)?window.bulmaOptions[e]:t},u.prototype.data=function(e,t){return t?(u.cache.set(this._elem[u.id],e,t),this):u.cache.get(this._elem[u.id],e)},u.prototype.destroyData=function(){return u.cache.destroy(this._elem[u.id]),this},u.prototype.getElement=function(){return this._elem},document.addEventListener("DOMContentLoaded",(function(){u.getGlobalConfig("autoParseDocument",!0)&&u.parseDocument(),u.getGlobalConfig("onLoaded")&&u.getGlobalConfig("onLoaded")()})),t.default=u},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},n=arguments[1];if(l(this,e),t.root=n instanceof u.default?n._elem:n,this.config=new i.default(r({},this.constructor.defaultConfig(),t)),!n&&!this.config.has("parent"))throw new Error("A plugin requires a root and/or a parent.");this.parent=this.config.get("parent",t.root?t.root.parentNode:null),this._events={}}return o(e,null,[{key:"defaultConfig",value:function(){return{}}}]),o(e,[{key:"on",value:function(e,t){this._events.hasOwnProperty(e)||(this._events[e]=[]),this._events[e].push(t)}},{key:"trigger",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this._events.hasOwnProperty(e))for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];if(i(this,e),"object"!==(void 0===t?"undefined":r(t)))throw new TypeError("initialConfig must be of type object.");this._items=t}return o(e,[{key:"set",value:function(e,t){if(!e||!t)throw new Error("A key and value must be provided when setting a new option.");this._items[e]=t}},{key:"has",value:function(e){if(!e)throw new Error("A key must be provided.");return this._items.hasOwnProperty(e)&&this._items[e]}},{key:"get",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return t&&!this.has(e)?"function"==typeof t?t():t:this._items[e]}}]),e}();t.default=u},,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Dropdown=void 0;var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:0;if(!e)throw new Error("[BulmaJS] Key attribute is required.");a.plugins[e]={priority:n,handler:t},a.prototype[e]=function(t){return new a.plugins[e].handler(t,this)}},a.parseDocument=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=Object.keys(a.plugins).sort((function(e,t){return a.plugins[e].priority1&&void 0!==arguments[1]?arguments[1]:document,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"div",r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],o=t.querySelector(e);if(!o){0===r.length&&(r=e.split(".").filter((function(e){return e})));var i=a.createElement(n,r);return t.appendChild(i),i}return o},a.each=function(e,t){var n=void 0;for(n=0;n=200&&r.status<400?t(a._stripScripts(r.responseText)):n()},r.onerror=function(){return n()},r.send()}))},a._stripScripts=function(e){var t=document.createElement("div");t.innerHTML=e;for(var n=t.getElementsByTagName("script"),r=n.length;r--;)n[r].parentNode.removeChild(n[r]);return t.innerHTML.replace(/ +/g," ")},a.getGlobalConfig=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return window.hasOwnProperty("bulmaOptions")&&window.bulmaOptions.hasOwnProperty(e)?window.bulmaOptions[e]:t},a.prototype.data=function(e,t){return t?(a.cache.set(this._elem[a.id],e,t),this):a.cache.get(this._elem[a.id],e)},a.prototype.destroyData=function(){return a.cache.destroy(this._elem[a.id]),this},a.prototype.getElement=function(){return this._elem},document.addEventListener("DOMContentLoaded",(function(){a.getGlobalConfig("autoParseDocument",!0)&&a.parseDocument(),a.getGlobalConfig("onLoaded")&&a.getGlobalConfig("onLoaded")()})),t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},n=arguments[1];if(l(this,e),t.root=n instanceof a.default?n._elem:n,this.config=new i.default(r({},this.constructor.defaultConfig(),t)),!n&&!this.config.has("parent"))throw new Error("A plugin requires a root and/or a parent.");this.parent=this.config.get("parent",t.root?t.root.parentNode:null),this._events={}}return o(e,null,[{key:"defaultConfig",value:function(){return{}}}]),o(e,[{key:"on",value:function(e,t){this._events.hasOwnProperty(e)||(this._events[e]=[]),this._events[e].push(t)}},{key:"trigger",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this._events.hasOwnProperty(e))for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];if(i(this,e),"object"!==(void 0===t?"undefined":r(t)))throw new TypeError("initialConfig must be of type object.");this._items=t}return o(e,[{key:"set",value:function(e,t){if(!e||!t)throw new Error("A key and value must be provided when setting a new option.");this._items[e]=t}},{key:"has",value:function(e){if(!e)throw new Error("A key must be provided.");return this._items.hasOwnProperty(e)&&this._items[e]}},{key:"get",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return t&&!this.has(e)?"function"==typeof t?t():t:this._items[e]}}]),e}();t.default=a},,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.File=void 0;var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=function(){function e(e,t){for(var n=0;n1&&this.setFileName(e.target.files.length+" files"),this.trigger("changed",e)}},{key:"clearFileName",value:function(){this.filename.innerHTML=""}},{key:"getFilename",value:function(){return this.filename.innerHTML}},{key:"setFileName",value:function(e){this.filename.innerHTML=e}},{key:"addHoverClass",value:function(){this.root.classList.add("is-hovered")}},{key:"removeHoverClass",value:function(){this.root.classList.remove("is-hovered")}}]),t}(a.default);i.default.registerPlugin("file",l),t.default=i.default}]).default})); -------------------------------------------------------------------------------- /dist/message.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Bulma",[],t):"object"==typeof exports?exports.Bulma=t():e.Bulma=t()}(window,(function(){return function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=8)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o,r=n(2),i=(o=r)&&o.__esModule?o:{default:o};function s(e){return this instanceof s?e instanceof s?e:(e instanceof HTMLElement?this._elem=e:this._elem=document.querySelector(e),e||(this._elem=document.createElement("div")),this._elem.hasOwnProperty(s.id)||(this._elem[s.id]=i.default.uid++),this):new s(e)}s.VERSION="0.12.1",s.id="bulma-"+(new Date).getTime(),s.cache=new i.default,s.plugins={},s.create=function(e,t){if(!e||!s.plugins.hasOwnProperty(e))throw new Error("[BulmaJS] A plugin with the key '"+e+"' has not been registered.");return new s.plugins[e].handler(t)},s.registerPlugin=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(!e)throw new Error("[BulmaJS] Key attribute is required.");s.plugins[e]={priority:n,handler:t},s.prototype[e]=function(t){return new s.plugins[e].handler(t,this)}},s.parseDocument=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=Object.keys(s.plugins).sort((function(e,t){return s.plugins[e].priority1&&void 0!==arguments[1]?arguments[1]:document,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"div",o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],r=t.querySelector(e);if(!r){0===o.length&&(o=e.split(".").filter((function(e){return e})));var i=s.createElement(n,o);return t.appendChild(i),i}return r},s.each=function(e,t){var n=void 0;for(n=0;n=200&&o.status<400?t(s._stripScripts(o.responseText)):n()},o.onerror=function(){return n()},o.send()}))},s._stripScripts=function(e){var t=document.createElement("div");t.innerHTML=e;for(var n=t.getElementsByTagName("script"),o=n.length;o--;)n[o].parentNode.removeChild(n[o]);return t.innerHTML.replace(/ +/g," ")},s.getGlobalConfig=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return window.hasOwnProperty("bulmaOptions")&&window.bulmaOptions.hasOwnProperty(e)?window.bulmaOptions[e]:t},s.prototype.data=function(e,t){return t?(s.cache.set(this._elem[s.id],e,t),this):s.cache.get(this._elem[s.id],e)},s.prototype.destroyData=function(){return s.cache.destroy(this._elem[s.id]),this},s.prototype.getElement=function(){return this._elem},document.addEventListener("DOMContentLoaded",(function(){s.getGlobalConfig("autoParseDocument",!0)&&s.parseDocument(),s.getGlobalConfig("onLoaded")&&s.getGlobalConfig("onLoaded")()})),t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},n=arguments[1];if(u(this,e),t.root=n instanceof s.default?n._elem:n,this.config=new i.default(o({},this.constructor.defaultConfig(),t)),!n&&!this.config.has("parent"))throw new Error("A plugin requires a root and/or a parent.");this.parent=this.config.get("parent",t.root?t.root.parentNode:null),this._events={}}return r(e,null,[{key:"defaultConfig",value:function(){return{}}}]),r(e,[{key:"on",value:function(e,t){this._events.hasOwnProperty(e)||(this._events[e]=[]),this._events[e].push(t)}},{key:"trigger",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this._events.hasOwnProperty(e))for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];if(i(this,e),"object"!==(void 0===t?"undefined":o(t)))throw new TypeError("initialConfig must be of type object.");this._items=t}return r(e,[{key:"set",value:function(e,t){if(!e||!t)throw new Error("A key and value must be provided when setting a new option.");this._items[e]=t}},{key:"has",value:function(e){if(!e)throw new Error("A key must be provided.");return this._items.hasOwnProperty(e)&&this._items[e]}},{key:"get",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return t&&!this.has(e)?"function"==typeof t?t():t:this._items[e]}}]),e}();t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t){for(var n=0;n"+this.title+"

",this.title=e,this.root.insertBefore(this.title,this.root.firstChild)}},{key:"setSize",value:function(){this.root.classList.add("is-"+this.size)}},{key:"insertBody",value:function(){var e=document.createElement("div");e.classList.add("message-body"),e.innerHTML=this.body,this.root.appendChild(e)}},{key:"prependCloseButton",value:function(){this.title.appendChild(this.closeButton)}}]),t}(s.default);i.default.registerPlugin("message",u),t.default=i.default}]).default})); -------------------------------------------------------------------------------- /dist/modal.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Bulma",[],t):"object"==typeof exports?exports.Bulma=t():e.Bulma=t()}(window,(function(){return function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=5)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o,r=n(2),i=(o=r)&&o.__esModule?o:{default:o};function a(e){return this instanceof a?e instanceof a?e:(e instanceof HTMLElement?this._elem=e:this._elem=document.querySelector(e),e||(this._elem=document.createElement("div")),this._elem.hasOwnProperty(a.id)||(this._elem[a.id]=i.default.uid++),this):new a(e)}a.VERSION="0.12.1",a.id="bulma-"+(new Date).getTime(),a.cache=new i.default,a.plugins={},a.create=function(e,t){if(!e||!a.plugins.hasOwnProperty(e))throw new Error("[BulmaJS] A plugin with the key '"+e+"' has not been registered.");return new a.plugins[e].handler(t)},a.registerPlugin=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(!e)throw new Error("[BulmaJS] Key attribute is required.");a.plugins[e]={priority:n,handler:t},a.prototype[e]=function(t){return new a.plugins[e].handler(t,this)}},a.parseDocument=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=Object.keys(a.plugins).sort((function(e,t){return a.plugins[e].priority1&&void 0!==arguments[1]?arguments[1]:document,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"div",o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],r=t.querySelector(e);if(!r){0===o.length&&(o=e.split(".").filter((function(e){return e})));var i=a.createElement(n,o);return t.appendChild(i),i}return r},a.each=function(e,t){var n=void 0;for(n=0;n=200&&o.status<400?t(a._stripScripts(o.responseText)):n()},o.onerror=function(){return n()},o.send()}))},a._stripScripts=function(e){var t=document.createElement("div");t.innerHTML=e;for(var n=t.getElementsByTagName("script"),o=n.length;o--;)n[o].parentNode.removeChild(n[o]);return t.innerHTML.replace(/ +/g," ")},a.getGlobalConfig=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return window.hasOwnProperty("bulmaOptions")&&window.bulmaOptions.hasOwnProperty(e)?window.bulmaOptions[e]:t},a.prototype.data=function(e,t){return t?(a.cache.set(this._elem[a.id],e,t),this):a.cache.get(this._elem[a.id],e)},a.prototype.destroyData=function(){return a.cache.destroy(this._elem[a.id]),this},a.prototype.getElement=function(){return this._elem},document.addEventListener("DOMContentLoaded",(function(){a.getGlobalConfig("autoParseDocument",!0)&&a.parseDocument(),a.getGlobalConfig("onLoaded")&&a.getGlobalConfig("onLoaded")()})),t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},n=arguments[1];if(l(this,e),t.root=n instanceof a.default?n._elem:n,this.config=new i.default(o({},this.constructor.defaultConfig(),t)),!n&&!this.config.has("parent"))throw new Error("A plugin requires a root and/or a parent.");this.parent=this.config.get("parent",t.root?t.root.parentNode:null),this._events={}}return r(e,null,[{key:"defaultConfig",value:function(){return{}}}]),r(e,[{key:"on",value:function(e,t){this._events.hasOwnProperty(e)||(this._events[e]=[]),this._events[e].push(t)}},{key:"trigger",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this._events.hasOwnProperty(e))for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];if(i(this,e),"object"!==(void 0===t?"undefined":o(t)))throw new TypeError("initialConfig must be of type object.");this._items=t}return r(e,[{key:"set",value:function(e,t){if(!e||!t)throw new Error("A key and value must be provided when setting a new option.");this._items[e]=t}},{key:"has",value:function(e){if(!e)throw new Error("A key must be provided.");return this._items.hasOwnProperty(e)&&this._items[e]}},{key:"get",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return t&&!this.has(e)?"function"==typeof t?t():t:this._items[e]}}]),e}();t.default=a},,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Modal=void 0;var o=function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:0;if(!t)throw new Error("[BulmaJS] Key attribute is required.");a.plugins[t]={priority:n,handler:e},a.prototype[t]=function(e){return new a.plugins[t].handler(e,this)}},a.parseDocument=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,e=Object.keys(a.plugins).sort((function(t,e){return a.plugins[t].priority1&&void 0!==arguments[1]?arguments[1]:document,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"div",r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],o=e.querySelector(t);if(!o){0===r.length&&(r=t.split(".").filter((function(t){return t})));var i=a.createElement(n,r);return e.appendChild(i),i}return o},a.each=function(t,e){var n=void 0;for(n=0;n=200&&r.status<400?e(a._stripScripts(r.responseText)):n()},r.onerror=function(){return n()},r.send()}))},a._stripScripts=function(t){var e=document.createElement("div");e.innerHTML=t;for(var n=e.getElementsByTagName("script"),r=n.length;r--;)n[r].parentNode.removeChild(n[r]);return e.innerHTML.replace(/ +/g," ")},a.getGlobalConfig=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return window.hasOwnProperty("bulmaOptions")&&window.bulmaOptions.hasOwnProperty(t)?window.bulmaOptions[t]:e},a.prototype.data=function(t,e){return e?(a.cache.set(this._elem[a.id],t,e),this):a.cache.get(this._elem[a.id],t)},a.prototype.destroyData=function(){return a.cache.destroy(this._elem[a.id]),this},a.prototype.getElement=function(){return this._elem},document.addEventListener("DOMContentLoaded",(function(){a.getGlobalConfig("autoParseDocument",!0)&&a.parseDocument(),a.getGlobalConfig("onLoaded")&&a.getGlobalConfig("onLoaded")()})),e.default=a},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=Object.assign||function(t){for(var e=1;e0&&void 0!==arguments[0]?arguments[0]:{},n=arguments[1];if(l(this,t),e.root=n instanceof a.default?n._elem:n,this.config=new i.default(r({},this.constructor.defaultConfig(),e)),!n&&!this.config.has("parent"))throw new Error("A plugin requires a root and/or a parent.");this.parent=this.config.get("parent",e.root?e.root.parentNode:null),this._events={}}return o(t,null,[{key:"defaultConfig",value:function(){return{}}}]),o(t,[{key:"on",value:function(t,e){this._events.hasOwnProperty(t)||(this._events[t]=[]),this._events[t].push(e)}},{key:"trigger",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this._events.hasOwnProperty(t))for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];if(i(this,t),"object"!==(void 0===e?"undefined":r(e)))throw new TypeError("initialConfig must be of type object.");this._items=e}return o(t,[{key:"set",value:function(t,e){if(!t||!e)throw new Error("A key and value must be provided when setting a new option.");this._items[t]=e}},{key:"has",value:function(t){if(!t)throw new Error("A key must be provided.");return this._items.hasOwnProperty(t)&&this._items[t]}},{key:"get",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return e&&!this.has(t)?"function"==typeof e?e():e:this._items[t]}}]),t}();e.default=a},,,,function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Navbar=void 0;var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o=function(){function t(t,e){for(var n=0;nthis.stickyOffset?(this.root.classList.add("is-fixed-top"),document.body.classList.add("has-navbar-fixed-top"),this.shadow&&this.root.classList.add("has-shadow")):(this.root.classList.remove("is-fixed-top"),document.body.classList.remove("has-navbar-fixed-top"),this.shadow&&this.root.classList.remove("has-shadow")),this.hideOnScroll){var e=this.calculateScrollDirection(t,this.lastScrollY),n=this.difference(t,this.lastScrollY)>=this.tolerance;if("down"===e){var r=t>this.hideOffset;n&&r&&this.root.classList.add("is-hidden-scroll")}else{var o=te?t-e:e-t}},{key:"calculateScrollDirection",value:function(t,e){return t>=e?"down":"up"}}]),e}(a.default);i.default.registerPlugin("navbar",l),e.default=i.default}]).default})); -------------------------------------------------------------------------------- /dist/notification.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Bulma",[],t):"object"==typeof exports?exports.Bulma=t():e.Bulma=t()}(window,(function(){return function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=6)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o,r=n(2),i=(o=r)&&o.__esModule?o:{default:o};function u(e){return this instanceof u?e instanceof u?e:(e instanceof HTMLElement?this._elem=e:this._elem=document.querySelector(e),e||(this._elem=document.createElement("div")),this._elem.hasOwnProperty(u.id)||(this._elem[u.id]=i.default.uid++),this):new u(e)}u.VERSION="0.12.1",u.id="bulma-"+(new Date).getTime(),u.cache=new i.default,u.plugins={},u.create=function(e,t){if(!e||!u.plugins.hasOwnProperty(e))throw new Error("[BulmaJS] A plugin with the key '"+e+"' has not been registered.");return new u.plugins[e].handler(t)},u.registerPlugin=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(!e)throw new Error("[BulmaJS] Key attribute is required.");u.plugins[e]={priority:n,handler:t},u.prototype[e]=function(t){return new u.plugins[e].handler(t,this)}},u.parseDocument=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=Object.keys(u.plugins).sort((function(e,t){return u.plugins[e].priority1&&void 0!==arguments[1]?arguments[1]:document,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"div",o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],r=t.querySelector(e);if(!r){0===o.length&&(o=e.split(".").filter((function(e){return e})));var i=u.createElement(n,o);return t.appendChild(i),i}return r},u.each=function(e,t){var n=void 0;for(n=0;n=200&&o.status<400?t(u._stripScripts(o.responseText)):n()},o.onerror=function(){return n()},o.send()}))},u._stripScripts=function(e){var t=document.createElement("div");t.innerHTML=e;for(var n=t.getElementsByTagName("script"),o=n.length;o--;)n[o].parentNode.removeChild(n[o]);return t.innerHTML.replace(/ +/g," ")},u.getGlobalConfig=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return window.hasOwnProperty("bulmaOptions")&&window.bulmaOptions.hasOwnProperty(e)?window.bulmaOptions[e]:t},u.prototype.data=function(e,t){return t?(u.cache.set(this._elem[u.id],e,t),this):u.cache.get(this._elem[u.id],e)},u.prototype.destroyData=function(){return u.cache.destroy(this._elem[u.id]),this},u.prototype.getElement=function(){return this._elem},document.addEventListener("DOMContentLoaded",(function(){u.getGlobalConfig("autoParseDocument",!0)&&u.parseDocument(),u.getGlobalConfig("onLoaded")&&u.getGlobalConfig("onLoaded")()})),t.default=u},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},n=arguments[1];if(s(this,e),t.root=n instanceof u.default?n._elem:n,this.config=new i.default(o({},this.constructor.defaultConfig(),t)),!n&&!this.config.has("parent"))throw new Error("A plugin requires a root and/or a parent.");this.parent=this.config.get("parent",t.root?t.root.parentNode:null),this._events={}}return r(e,null,[{key:"defaultConfig",value:function(){return{}}}]),r(e,[{key:"on",value:function(e,t){this._events.hasOwnProperty(e)||(this._events[e]=[]),this._events[e].push(t)}},{key:"trigger",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this._events.hasOwnProperty(e))for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];if(i(this,e),"object"!==(void 0===t?"undefined":o(t)))throw new TypeError("initialConfig must be of type object.");this._items=t}return r(e,[{key:"set",value:function(e,t){if(!e||!t)throw new Error("A key and value must be provided when setting a new option.");this._items[e]=t}},{key:"has",value:function(e){if(!e)throw new Error("A key must be provided.");return this._items.hasOwnProperty(e)&&this._items[e]}},{key:"get",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return t&&!this.has(e)?"function"==typeof t?t():t:this._items[e]}}]),e}();t.default=u},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:0;if(!e)throw new Error("[BulmaJS] Key attribute is required.");a.plugins[e]={priority:n,handler:t},a.prototype[e]=function(t){return new a.plugins[e].handler(t,this)}},a.parseDocument=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=Object.keys(a.plugins).sort((function(e,t){return a.plugins[e].priority1&&void 0!==arguments[1]?arguments[1]:document,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"div",r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],o=t.querySelector(e);if(!o){0===r.length&&(r=e.split(".").filter((function(e){return e})));var i=a.createElement(n,r);return t.appendChild(i),i}return o},a.each=function(e,t){var n=void 0;for(n=0;n=200&&r.status<400?t(a._stripScripts(r.responseText)):n()},r.onerror=function(){return n()},r.send()}))},a._stripScripts=function(e){var t=document.createElement("div");t.innerHTML=e;for(var n=t.getElementsByTagName("script"),r=n.length;r--;)n[r].parentNode.removeChild(n[r]);return t.innerHTML.replace(/ +/g," ")},a.getGlobalConfig=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return window.hasOwnProperty("bulmaOptions")&&window.bulmaOptions.hasOwnProperty(e)?window.bulmaOptions[e]:t},a.prototype.data=function(e,t){return t?(a.cache.set(this._elem[a.id],e,t),this):a.cache.get(this._elem[a.id],e)},a.prototype.destroyData=function(){return a.cache.destroy(this._elem[a.id]),this},a.prototype.getElement=function(){return this._elem},document.addEventListener("DOMContentLoaded",(function(){a.getGlobalConfig("autoParseDocument",!0)&&a.parseDocument(),a.getGlobalConfig("onLoaded")&&a.getGlobalConfig("onLoaded")()})),t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},n=arguments[1];if(s(this,e),t.root=n instanceof a.default?n._elem:n,this.config=new i.default(r({},this.constructor.defaultConfig(),t)),!n&&!this.config.has("parent"))throw new Error("A plugin requires a root and/or a parent.");this.parent=this.config.get("parent",t.root?t.root.parentNode:null),this._events={}}return o(e,null,[{key:"defaultConfig",value:function(){return{}}}]),o(e,[{key:"on",value:function(e,t){this._events.hasOwnProperty(e)||(this._events[e]=[]),this._events[e].push(t)}},{key:"trigger",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this._events.hasOwnProperty(e))for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];if(i(this,e),"object"!==(void 0===t?"undefined":r(t)))throw new TypeError("initialConfig must be of type object.");this._items=t}return o(e,[{key:"set",value:function(e,t){if(!e||!t)throw new Error("A key and value must be provided when setting a new option.");this._items[e]=t}},{key:"has",value:function(e){if(!e)throw new Error("A key must be provided.");return this._items.hasOwnProperty(e)&&this._items[e]}},{key:"get",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return t&&!this.has(e)?"function"==typeof t?t():t:this._items[e]}}]),e}();t.default=a},,,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PanelTabs=void 0;var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:0;if(!e)throw new Error("[BulmaJS] Key attribute is required.");u.plugins[e]={priority:n,handler:t},u.prototype[e]=function(t){return new u.plugins[e].handler(t,this)}},u.parseDocument=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=Object.keys(u.plugins).sort((function(e,t){return u.plugins[e].priority1&&void 0!==arguments[1]?arguments[1]:document,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"div",r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],o=t.querySelector(e);if(!o){0===r.length&&(r=e.split(".").filter((function(e){return e})));var i=u.createElement(n,r);return t.appendChild(i),i}return o},u.each=function(e,t){var n=void 0;for(n=0;n=200&&r.status<400?t(u._stripScripts(r.responseText)):n()},r.onerror=function(){return n()},r.send()}))},u._stripScripts=function(e){var t=document.createElement("div");t.innerHTML=e;for(var n=t.getElementsByTagName("script"),r=n.length;r--;)n[r].parentNode.removeChild(n[r]);return t.innerHTML.replace(/ +/g," ")},u.getGlobalConfig=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return window.hasOwnProperty("bulmaOptions")&&window.bulmaOptions.hasOwnProperty(e)?window.bulmaOptions[e]:t},u.prototype.data=function(e,t){return t?(u.cache.set(this._elem[u.id],e,t),this):u.cache.get(this._elem[u.id],e)},u.prototype.destroyData=function(){return u.cache.destroy(this._elem[u.id]),this},u.prototype.getElement=function(){return this._elem},document.addEventListener("DOMContentLoaded",(function(){u.getGlobalConfig("autoParseDocument",!0)&&u.parseDocument(),u.getGlobalConfig("onLoaded")&&u.getGlobalConfig("onLoaded")()})),t.default=u},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},n=arguments[1];if(s(this,e),t.root=n instanceof u.default?n._elem:n,this.config=new i.default(r({},this.constructor.defaultConfig(),t)),!n&&!this.config.has("parent"))throw new Error("A plugin requires a root and/or a parent.");this.parent=this.config.get("parent",t.root?t.root.parentNode:null),this._events={}}return o(e,null,[{key:"defaultConfig",value:function(){return{}}}]),o(e,[{key:"on",value:function(e,t){this._events.hasOwnProperty(e)||(this._events[e]=[]),this._events[e].push(t)}},{key:"trigger",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this._events.hasOwnProperty(e))for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];if(i(this,e),"object"!==(void 0===t?"undefined":r(t)))throw new TypeError("initialConfig must be of type object.");this._items=t}return o(e,[{key:"set",value:function(e,t){if(!e||!t)throw new Error("A key and value must be provided when setting a new option.");this._items[e]=t}},{key:"has",value:function(e){if(!e)throw new Error("A key must be provided.");return this._items.hasOwnProperty(e)&&this._items[e]}},{key:"get",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return t&&!this.has(e)?"function"==typeof t?t():t:this._items[e]}}]),e}();t.default=u},,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Tabs=void 0;var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=function(){function e(e,t){for(var n=0;n ul > li")}},{key:"setupNavEvents",value:function(){var e=this;i.default.each(this.navItems,(function(t,n){t.addEventListener("click",(function(){e.setActive(n)})),e.hover&&t.addEventListener("mouseover",(function(){e.setActive(n)}))}))}},{key:"setActive",value:function(e){i.default.each(this.navItems,(function(e){e.classList.remove("is-active")})),i.default.each(this.contentItems,(function(e){e.classList.remove("is-active")})),this.navItems[e].classList.add("is-active"),this.contentItems[e].classList.add("is-active")}}]),t}(u.default);i.default.registerPlugin("tabs",s),t.default=i.default}]).default})); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vizuaalog/bulmajs", 3 | "version": "0.12.2", 4 | "description": "Unofficial javascript extension to the awesome Bulma CSS framework.", 5 | "main": "src/bulma.js", 6 | "author": "Thomas Erbe ", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/VizuaaLOG/BulmaJS.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/VizuaaLOG/BulmaJS/issues" 14 | }, 15 | "homepage": "https://github.com/VizuaaLOG/BulmaJS", 16 | "devDependencies": { 17 | "babel-core": "^6.26.0", 18 | "babel-loader": "^7.1.4", 19 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 20 | "babel-preset-env": "^1.6.1", 21 | "bulma": "^0.7.1", 22 | "eslint": "^4.19.0", 23 | "node-qunit-puppeteer": "^1.0.12", 24 | "webpack": "^4.20.0", 25 | "webpack-cli": "^3.1.1", 26 | "webpack-glob-entry": "^2.1.1" 27 | }, 28 | "scripts": { 29 | "lint": "eslint 'src/**'", 30 | "dev": "webpack --config webpack.config.js --mode development", 31 | "production": "webpack --config webpack.config.js --mode production", 32 | "watch": "webpack --config webpack.config.js --mode development --watch", 33 | "test": "node-qunit-puppeteer ./tests/test-runner.html 10000 '--allow-file-access-from-files --no-sandbox'" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/ConfigBag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Object to hold a plugin's configuration 3 | * @class ConfigBag 4 | * @since 0.11.0 5 | * @author Thomas Erbe 6 | */ 7 | export default class ConfigBag { 8 | constructor(initialConfig = []) { 9 | if(typeof initialConfig !== 'object') { 10 | throw new TypeError('initialConfig must be of type object.'); 11 | } 12 | 13 | this._items = initialConfig; 14 | } 15 | 16 | /** 17 | * Set a new config property 18 | * @param {string} key The config property's key 19 | * @param {mixed} value The config property's value 20 | */ 21 | set(key, value) { 22 | if(!key || !value) { 23 | throw new Error('A key and value must be provided when setting a new option.'); 24 | } 25 | 26 | this._items[key] = value; 27 | } 28 | 29 | /** 30 | * Check if a key exists 31 | * @param {string} key 32 | * @returns {boolean} 33 | */ 34 | has(key) { 35 | if(!key) { 36 | throw new Error('A key must be provided.'); 37 | } 38 | 39 | return (this._items.hasOwnProperty(key) && this._items[key]); 40 | } 41 | 42 | /** 43 | * Get a property by it's key. Returns the defaultValue if it doesn't exists 44 | * @param {string} key 45 | * @param {mixed} defaultValue 46 | * @returns {mixed} 47 | */ 48 | get(key, defaultValue = null) { 49 | if(defaultValue && !this.has(key)) { 50 | if(typeof defaultValue === 'function') { 51 | return defaultValue(); 52 | } 53 | 54 | return defaultValue; 55 | } 56 | 57 | return this._items[key]; 58 | } 59 | } -------------------------------------------------------------------------------- /src/Data.js: -------------------------------------------------------------------------------- 1 | class Data { 2 | constructor() { 3 | this._data = {}; 4 | } 5 | 6 | set(uid, key, value) { 7 | if(!this._data.hasOwnProperty(uid)) { 8 | this._data[uid] = {}; 9 | } 10 | 11 | this._data[uid][key] = value; 12 | } 13 | 14 | get(uid, key) { 15 | if(!this._data.hasOwnProperty(uid)) { 16 | return undefined; 17 | } 18 | 19 | return this._data[uid][key]; 20 | } 21 | 22 | destroy(uid) { 23 | if(this._data.hasOwnProperty(uid)) { 24 | delete this._data[uid]; 25 | } 26 | } 27 | } 28 | 29 | Data.uid = 1; 30 | 31 | export default Data; -------------------------------------------------------------------------------- /src/bulma.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | 3 | import Bulma from './core'; 4 | import { Notification } from './plugins/notification'; 5 | import { Navbar } from './plugins/navbar'; 6 | import { Message } from './plugins/message'; 7 | import { Dropdown } from './plugins/dropdown'; 8 | import { Modal } from './plugins/modal'; 9 | import { Alert } from './plugins/alert'; 10 | import { File } from './plugins/file'; 11 | import { Tabs } from './plugins/tabs'; 12 | import { PanelTabs } from './plugins/panelTabs'; 13 | 14 | export default Bulma; 15 | -------------------------------------------------------------------------------- /src/core.js: -------------------------------------------------------------------------------- 1 | import Data from './Data'; 2 | 3 | /** 4 | * Wrap an element around Bulma. 5 | * @param {String|HTMLElement} selector The selector or HTMLElement to wrap. 6 | */ 7 | function Bulma(selector) { 8 | if (!(this instanceof Bulma)) { 9 | return new Bulma(selector); 10 | } 11 | 12 | if (selector instanceof Bulma) { 13 | return selector; 14 | } 15 | 16 | if (selector instanceof HTMLElement) { 17 | this._elem = selector; 18 | } else { 19 | this._elem = document.querySelector(selector); 20 | } 21 | 22 | if (!selector) { 23 | this._elem = document.createElement('div'); 24 | } 25 | 26 | if (!this._elem.hasOwnProperty(Bulma.id)) { 27 | this._elem[Bulma.id] = Data.uid++; 28 | } 29 | 30 | return this; 31 | } 32 | 33 | /** 34 | * Current BulmaJS version. 35 | * @type {String} 36 | */ 37 | Bulma.VERSION = '0.12.1'; 38 | 39 | /** 40 | * Unique ID of Bulma 41 | * @type {String} 42 | */ 43 | Bulma.id = 'bulma-' + new Date().getTime(); 44 | 45 | /** 46 | * Global data cache for HTML elements 47 | * @type {Data} 48 | */ 49 | Bulma.cache = new Data(); 50 | 51 | /** 52 | * An index of the registered plugins 53 | * @type {Object} 54 | */ 55 | Bulma.plugins = {}; 56 | 57 | /** 58 | * Helper method to create a new plugin. 59 | * @param {String} key The plugin's key 60 | * @param {Object} config The config to be passed to the plugin 61 | * @return {Object} The newly created plugin instance 62 | */ 63 | Bulma.create = (key, config) => { 64 | if (!key || !Bulma.plugins.hasOwnProperty(key)) { 65 | throw new Error('[BulmaJS] A plugin with the key \'' + key + '\' has not been registered.'); 66 | } 67 | 68 | return new Bulma.plugins[key].handler(config); 69 | }; 70 | 71 | /** 72 | * Register a new plugin 73 | * @param {String} key The key to register the plugin under 74 | * @param {Object} plugin The plugin's main constructor 75 | * @param {number?} priority The priority this plugin has over other plugins. Higher means the plugin is registered before lower. 76 | * @return {undefined} 77 | */ 78 | Bulma.registerPlugin = (key, plugin, priority = 0) => { 79 | if (!key) { 80 | throw new Error('[BulmaJS] Key attribute is required.'); 81 | } 82 | 83 | Bulma.plugins[key] = { 84 | priority: priority, 85 | handler: plugin 86 | }; 87 | 88 | Bulma.prototype[key] = function (config) { 89 | return new Bulma.plugins[key].handler(config, this); 90 | }; 91 | }; 92 | 93 | /** 94 | * Parse the HTML DOM searching for data-bulma attributes. We will then pass 95 | * each element to the appropriate plugin to handle the required processing. 96 | * @param {HTMLElement} root The root of the document we're going to parse. 97 | * @return {undefined} 98 | */ 99 | Bulma.parseDocument = (root = document) => { 100 | let sortedPlugins = Object.keys(Bulma.plugins) 101 | .sort((a, b) => Bulma.plugins[a].priority < Bulma.plugins[b].priority); 102 | 103 | Bulma.each(sortedPlugins, (key) => { 104 | if (!Bulma.plugins[key].handler.hasOwnProperty('parseDocument')) { 105 | // eslint-disable-next-line no-console 106 | console.error('[BulmaJS] Plugin ' + key + ' does not have a parseDocument method. Automatic document parsing is not possible for this plugin.'); 107 | return; 108 | } 109 | 110 | Bulma.plugins[key].handler.parseDocument(root); 111 | }); 112 | }; 113 | 114 | /** 115 | * Create an element and assign classes 116 | * @param {string} name The name of the element to create 117 | * @param {array} classes An array of classes to add to the element 118 | * @return {HTMLElement} The newly created element 119 | */ 120 | Bulma.createElement = (name, classes) => { 121 | if (!classes) { 122 | classes = []; 123 | } 124 | 125 | if (typeof classes === 'string') { 126 | classes = [classes]; 127 | } 128 | 129 | let elem = document.createElement(name); 130 | 131 | Bulma.each(classes, (className) => { 132 | elem.classList.add(className); 133 | }); 134 | 135 | return elem; 136 | }; 137 | 138 | /** 139 | * Find an element otherwise create a new one. 140 | * @param {string} query The CSS selector query to find 141 | * @param {HTMLElement|null} parent The parent we want to search/create within 142 | * @param {[string]} elemName The name of the element to create 143 | * @param {[array]} classes The classes to apply to the element 144 | * @returns {HTMLElement} The HTML element we found or created 145 | */ 146 | Bulma.findOrCreateElement = (query, parent = document, elemName = 'div', classes = []) => { 147 | var elem = parent.querySelector(query); 148 | 149 | if (!elem) { 150 | if (classes.length === 0) { 151 | classes = query.split('.').filter((item) => { 152 | return item; 153 | }); 154 | } 155 | 156 | var newElem = Bulma.createElement(elemName, classes); 157 | 158 | parent.appendChild(newElem); 159 | 160 | return newElem; 161 | } 162 | 163 | return elem; 164 | }; 165 | 166 | /** 167 | * For loop helper 168 | * @param {*} objects The array/object to loop through 169 | * @param {*} callback The callback used for each item 170 | */ 171 | Bulma.each = (objects, callback) => { 172 | let i; 173 | 174 | for (i = 0; i < objects.length; i++) { 175 | callback(objects[i], i); 176 | } 177 | }; 178 | 179 | /** 180 | * Make an AJAX GET request to the specified URL. Stripping any script tags from the response. 181 | * @param {*} url The url to send the request to 182 | * @returns {Promise} Returns a promise containing the response HTML or error 183 | */ 184 | Bulma.ajax = (url) => { 185 | return new Promise((resolve, reject) => { 186 | var request = new XMLHttpRequest(); 187 | request.open('GET', url, true); 188 | 189 | request.onload = () => { 190 | if (request.status >= 200 && request.status < 400) { 191 | resolve(Bulma._stripScripts(request.responseText)); 192 | } else { 193 | reject(); 194 | } 195 | }; 196 | 197 | request.onerror = () => reject(); 198 | 199 | request.send(); 200 | }); 201 | }; 202 | 203 | /** 204 | * Strip any script tags from a HTML string. 205 | * @param {string} htmlString 206 | * @returns {string} The cleaned HTML string 207 | * 208 | * @private 209 | */ 210 | Bulma._stripScripts = (htmlString) => { 211 | var div = document.createElement('div'); 212 | div.innerHTML = htmlString; 213 | 214 | var scripts = div.getElementsByTagName('script'); 215 | 216 | var i = scripts.length; 217 | 218 | while (i--) { 219 | scripts[i].parentNode.removeChild(scripts[i]); 220 | } 221 | 222 | return div.innerHTML.replace(/ +/g, ' '); 223 | }; 224 | 225 | /** 226 | * Grabs a configuration property from the window.bulmaOptions global, if it's defined, 227 | * returns defaultValue otherwise. 228 | * @param {string} key The name of the option to check for 229 | * @param {*} [defaultValue=null] A default value of the key is not found 230 | */ 231 | Bulma.getGlobalConfig = function (key, defaultValue = null) { 232 | if (!window.hasOwnProperty('bulmaOptions')) { 233 | return defaultValue; 234 | } 235 | 236 | if (!window.bulmaOptions.hasOwnProperty(key)) { 237 | return defaultValue; 238 | } 239 | 240 | return window.bulmaOptions[key]; 241 | }; 242 | 243 | /** 244 | * Get or set custom data on a Bulma element. 245 | * @type {String} key 246 | * @type {any} value 247 | * @returns {Bulma} 248 | */ 249 | Bulma.prototype.data = function (key, value) { 250 | if (!value) { 251 | return Bulma.cache.get(this._elem[Bulma.id], key); 252 | } 253 | 254 | Bulma.cache.set(this._elem[Bulma.id], key, value); 255 | 256 | return this; 257 | }; 258 | 259 | /** 260 | * Destroy the data for an element. 261 | * @returns {Bulma} 262 | */ 263 | Bulma.prototype.destroyData = function () { 264 | Bulma.cache.destroy(this._elem[Bulma.id]); 265 | 266 | return this; 267 | }; 268 | 269 | /** 270 | * Returns the internal HTMLElement we're wrapping. 271 | * 272 | * @returns {HTMLElement} 273 | */ 274 | Bulma.prototype.getElement = function () { 275 | return this._elem; 276 | }; 277 | 278 | document.addEventListener('DOMContentLoaded', () => { 279 | if (Bulma.getGlobalConfig('autoParseDocument', true)) { 280 | Bulma.parseDocument(); 281 | } 282 | 283 | if (Bulma.getGlobalConfig('onLoaded')) { 284 | Bulma.getGlobalConfig('onLoaded')(); 285 | } 286 | }); 287 | 288 | export default Bulma; 289 | -------------------------------------------------------------------------------- /src/dismissableComponent.js: -------------------------------------------------------------------------------- 1 | import Bulma from './core'; 2 | import Plugin from './plugin'; 3 | 4 | /** 5 | * @module DismissableComponent 6 | * @since 0.2.0 7 | * @author Thomas Erbe 8 | */ 9 | export default class DismissableComponent extends Plugin { 10 | /** 11 | * Returns an object containing the default config for this plugin. 12 | * @returns {object} The default config object. 13 | */ 14 | static defaultConfig() { 15 | return { 16 | isDismissable: false, 17 | destroyOnDismiss: true, 18 | element: null 19 | }; 20 | } 21 | 22 | /** 23 | * Plugin constructor 24 | * @param {string} name Plugin's name 25 | * @param {Object} config Plugin's config 26 | * @return {this} The new plugin instance 27 | */ 28 | constructor(name, config, root) { 29 | if(!root._elem.classList.contains(name)) { 30 | config['parent'] = root; 31 | root = null; 32 | } 33 | 34 | super(config, root); 35 | 36 | /** 37 | * The name of this component, this will be used as the root class 38 | * @type {string} 39 | */ 40 | this.name = name; 41 | 42 | /** 43 | * Body text. 44 | * @type {string} 45 | */ 46 | this.body = this.config.get('body'); 47 | 48 | /** 49 | * Color modifier. 50 | * @type {string} Possible values are null, primary, info, success, warning, danger 51 | */ 52 | this.color = this.config.get('color'); 53 | 54 | /** 55 | * How long to wait before auto dismissing the component. 56 | * @type {int|null} If null component must be dismissed manually. 57 | */ 58 | this.dismissInterval = this.config.get('dismissInterval') ? this.createDismissInterval(this.config.get('dismissInterval')) : null; 59 | 60 | /** 61 | * Does this component have a dismiss button? 62 | * @type {Boolean} 63 | */ 64 | this.isDismissable = this.config.get('isDismissable'); 65 | 66 | /** 67 | * Should this component be destroyed when it is dismissed. 68 | * @type {Boolean} 69 | */ 70 | this.destroyOnDismiss = this.config.get('destroyOnDismiss'); 71 | 72 | // TODO: Make internal element references all be a Bulma instance. This will keep consistency. 73 | if(!(this.parent instanceof Bulma)) { 74 | this.parent = Bulma(this.parent); 75 | } 76 | 77 | /** 78 | * The root element. 79 | * @type {HTMLElement|null} If this is not provided a new element will be created. 80 | */ 81 | this.root = this.config.get('root', this.createRootElement.bind(this)); 82 | 83 | /** 84 | * The element used to close the component. 85 | * @type {HTMLElement} 86 | */ 87 | this.closeButton = this.config.get('closeButton', this.createCloseButton()); 88 | 89 | if(this.body) { 90 | this.insertBody(); 91 | } 92 | 93 | if(this.color) { 94 | this.setColor(); 95 | } 96 | } 97 | 98 | /** 99 | * Create the main element. 100 | * @return {HTMLElement} 101 | */ 102 | createRootElement() { 103 | let elem = document.createElement('div'); 104 | elem.classList.add(this.name, 'is-hidden'); 105 | elem.setAttribute('data-bulma-attached', 'attached'); 106 | 107 | this.parent.getElement().appendChild(elem); 108 | 109 | return elem; 110 | } 111 | 112 | /** 113 | * Show the component. 114 | * @return {undefined} 115 | */ 116 | show() { 117 | this.root.classList.remove('is-hidden'); 118 | } 119 | 120 | /** 121 | * Hide the component. 122 | * @return {undefined} 123 | */ 124 | hide() { 125 | this.root.classList.add('is-hidden'); 126 | } 127 | 128 | /** 129 | * Insert the body text into the component. 130 | * @return {undefined} 131 | */ 132 | insertBody() { 133 | this.root.innerHTML = this.body; 134 | } 135 | 136 | /** 137 | * Create the element that will be used to close the component. 138 | * @return {HTMLElement} The newly created close button 139 | */ 140 | createCloseButton() { 141 | var closeButton = document.createElement('button'); 142 | closeButton.setAttribute('type', 'button'); 143 | closeButton.classList.add('delete'); 144 | 145 | return closeButton; 146 | } 147 | 148 | /** 149 | * Create an interval to dismiss the component after the set number of ms. 150 | * @param {int} interval The time to wait before dismissing the component 151 | * @return {undefined} 152 | */ 153 | createDismissInterval(interval) { 154 | return setInterval(() => { 155 | this.handleCloseEvent(); 156 | }, interval); 157 | } 158 | 159 | /** 160 | * Insert the close button before our content. 161 | * @return {undefined} 162 | */ 163 | prependCloseButton() { 164 | this.root.insertBefore(this.closeButton, this.root.firstChild); 165 | } 166 | 167 | /** 168 | * Setup the event listener for the close button. 169 | * @return {undefined} 170 | */ 171 | setupCloseEvent() { 172 | this.closeButton.addEventListener('click', this.handleCloseEvent.bind(this)); 173 | } 174 | 175 | /** 176 | * Handle the event when our close button is clicked. 177 | * @return {undefined} 178 | */ 179 | handleCloseEvent() { 180 | this.trigger('dismissed'); 181 | 182 | if(this.destroyOnDismiss) { 183 | this.destroy(); 184 | } else { 185 | this.hide(); 186 | } 187 | 188 | this.trigger('close'); 189 | } 190 | 191 | /** 192 | * Set the colour of the component. 193 | * @return {undefined} 194 | */ 195 | setColor() { 196 | this.root.classList.add('is-' + this.color); 197 | } 198 | 199 | /** 200 | * Destroy the component, removing the event listener, interval and element. 201 | * @return {undefined} 202 | */ 203 | destroy() { 204 | super.destroy(); 205 | 206 | if(this.closeButton) { 207 | this.closeButton.removeEventListener('click', this.handleCloseEvent.bind(this)); 208 | } 209 | 210 | clearInterval(this.dismissInterval); 211 | 212 | this.parent.getElement().removeChild(this.root); 213 | this.parent = null; 214 | this.root = null; 215 | 216 | this.trigger('destroyed'); 217 | } 218 | } -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | import ConfigBag from './ConfigBag'; 2 | import Bulma from './core'; 3 | 4 | /** 5 | * Base plugin class. Provides basic, common functionality. 6 | * @class Plugin 7 | * @since 0.7.0 8 | * @author Thomas Erbe 9 | */ 10 | export default class Plugin { 11 | /** 12 | * Returns an object containing the default config for this plugin. 13 | * @returns {object} The default config object. 14 | */ 15 | static defaultConfig() { 16 | return {}; 17 | } 18 | 19 | /** 20 | * Create a plugin. 21 | * @param {object} config The config for this plugin 22 | */ 23 | constructor(config = {}, root) { 24 | config.root = (root instanceof Bulma) ? root._elem : root; 25 | 26 | this.config = new ConfigBag({...this.constructor.defaultConfig(), ...config}); 27 | 28 | if(!root && !this.config.has('parent')) { 29 | throw new Error('A plugin requires a root and/or a parent.'); 30 | } 31 | 32 | this.parent = this.config.get('parent', config.root ? config.root.parentNode : null); 33 | 34 | this._events = {}; 35 | } 36 | 37 | on(event, callback) { 38 | if(!this._events.hasOwnProperty(event)) { 39 | this._events[event] = []; 40 | } 41 | 42 | this._events[event].push(callback); 43 | } 44 | 45 | trigger(event, data = {}) { 46 | if(!this._events.hasOwnProperty(event)) { 47 | return; 48 | } 49 | 50 | for(let i = 0; i < this._events[event].length; i++) { 51 | this._events[event][i](data); 52 | } 53 | } 54 | 55 | destroy() { 56 | Bulma(this.root).destroyData(); 57 | } 58 | } -------------------------------------------------------------------------------- /src/plugins/alert.js: -------------------------------------------------------------------------------- 1 | import Bulma from '../core'; 2 | import { Modal } from './modal'; 3 | 4 | /** 5 | * @module Alert 6 | * @since 0.8.0 7 | * @author Thomas Erbe 8 | */ 9 | export class Alert extends Modal { 10 | /** 11 | * Handle parsing the DOM. 12 | * @param {HTMLElement} element The root element for this accordion 13 | * @return {undefined} 14 | */ 15 | static parseDocument() {} 16 | 17 | /** 18 | * Returns an object containing the default config for this plugin. 19 | * @returns {object} The default config object. 20 | */ 21 | static defaultConfig() { 22 | return { 23 | type: 'info', 24 | title: '', 25 | body: '', 26 | confirm: 'Okay', 27 | cancel: null, 28 | style: 'card', 29 | parent: document.body, 30 | showHeader: true 31 | }; 32 | } 33 | 34 | /** 35 | * Plugin constructor 36 | * @param {Object} config The config object for this plugin 37 | * @return {this} The newly created plugin instance 38 | */ 39 | constructor(config, root) { 40 | super(config, root); 41 | 42 | this.root.classList.add('alert'); 43 | 44 | Bulma(this.root).data('alert', this); 45 | 46 | this.trigger('init'); 47 | 48 | this.open(); 49 | } 50 | 51 | /** 52 | * Create the alerts structure 53 | * @returns {void} 54 | */ 55 | createCardStructure() { 56 | if(this.config.get('showHeader')) { 57 | /** @param {HTMLElement} */ 58 | this.header = Bulma.findOrCreateElement('.modal-card-head', this.content, 'header', ['modal-card-head', 'has-background-' + this.config.get('type')]); 59 | 60 | /** @param {HTMLElement} */ 61 | var textColor = this.config.get('type') == 'warning' ? 'black' : 'white'; 62 | this.headerTitle = Bulma.createElement('p', ['modal-card-title', 'has-text-' + textColor]); 63 | this.headerTitle.innerHTML = this.title; 64 | this.header.appendChild(this.headerTitle); 65 | } 66 | 67 | /** @param {HTMLElement} */ 68 | this.cardBody = Bulma.findOrCreateElement('.modal-card-body', this.content, 'section'); 69 | if(!this.cardBody.innerHTML) { 70 | this.cardBody.innerHTML = this.body; 71 | } 72 | 73 | /** @param {HTMLElement} */ 74 | this.footer = Bulma.findOrCreateElement('.modal-card-foot', this.content, 'footer'); 75 | } 76 | 77 | /** 78 | * Go through the provided buttons option and create the buttons. 79 | * @returns {void} 80 | */ 81 | createButtons() { 82 | var defaultButtonOptions = { close: true, destroy: true, onClick: function() {} }; 83 | 84 | var confirmOptions = this.config.get('confirm'); 85 | if(typeof confirmOptions === 'string') { 86 | confirmOptions = { 87 | label: confirmOptions, 88 | classes: [] 89 | }; 90 | } 91 | confirmOptions = { ...defaultButtonOptions, ...confirmOptions}; 92 | 93 | var confirmButton = Bulma.createElement('button', ['button', 'is-' + this.config.get('type')].concat(confirmOptions.classes)); 94 | confirmButton.innerHTML = confirmOptions.label; 95 | confirmButton.addEventListener('click', e => { 96 | confirmOptions.onClick(e); 97 | 98 | if(confirmOptions.close) { 99 | this.close(); 100 | } 101 | 102 | if(confirmOptions.destroy) { 103 | this.destroy(); 104 | } 105 | }); 106 | this.footer.appendChild(confirmButton); 107 | 108 | if(this.config.get('cancel')) { 109 | var cancelOptions = this.config.get('cancel'); 110 | if(typeof cancelOptions === 'string') { 111 | cancelOptions = { 112 | label: cancelOptions, 113 | classes: [] 114 | }; 115 | } 116 | cancelOptions = { ...defaultButtonOptions, ...cancelOptions}; 117 | 118 | var cancelButton = Bulma.createElement('button', ['button'].concat(cancelOptions.classes)); 119 | cancelButton.innerHTML = cancelOptions.label; 120 | cancelButton.addEventListener('click', e => { 121 | cancelOptions.onClick(e); 122 | 123 | if(cancelOptions.close) { 124 | this.close(); 125 | } 126 | 127 | if(cancelOptions.destroy) { 128 | this.destroy(); 129 | } 130 | }); 131 | this.footer.appendChild(cancelButton); 132 | } 133 | } 134 | } 135 | 136 | Bulma.registerPlugin('alert', Alert); 137 | 138 | export default Bulma; 139 | -------------------------------------------------------------------------------- /src/plugins/dropdown.js: -------------------------------------------------------------------------------- 1 | import Bulma from '../core'; 2 | import Plugin from '../plugin'; 3 | 4 | /** 5 | * @module Dropdown 6 | * @since 0.1.0 7 | * @author Thomas Erbe 8 | */ 9 | export class Dropdown extends Plugin { 10 | /** 11 | * Handle parsing the DOMs data attribute API. 12 | * @param {HtmlElement} element The root element for this instance 13 | * @return {undefined} 14 | */ 15 | static parseDocument(context) { 16 | let elements; 17 | 18 | if (typeof context.classList === 'object' && context.classList.contains('dropdown')) { 19 | elements = [context]; 20 | } else { 21 | elements = context.querySelectorAll('.dropdown'); 22 | } 23 | 24 | Bulma.each(elements, (element) => { 25 | Bulma(element).dropdown(); 26 | }); 27 | } 28 | 29 | /** 30 | * Plugin constructor 31 | * @param {Object} config The config object for this plugin 32 | * @return {this} The newly created instance 33 | */ 34 | constructor(config, root) { 35 | super(config, root); 36 | 37 | /** 38 | * The root dropdown element. 39 | * @type {HTMLElement} 40 | */ 41 | this.root = this.config.get('root'); 42 | this.root.setAttribute('data-bulma-attached', 'attached'); 43 | 44 | /** 45 | * The element to trigger when clicked. 46 | * @type {HTMLElement} 47 | */ 48 | this.triggerElement = this.root.querySelector('.dropdown-trigger'); 49 | 50 | this.registerEvents(); 51 | 52 | Bulma(this.root).data('dropdown', this); 53 | 54 | this.trigger('init'); 55 | } 56 | 57 | /** 58 | * Register all the events this module needs. 59 | * @return {undefined} 60 | */ 61 | registerEvents() { 62 | this.triggerElement.addEventListener('click', this.handleTriggerClick.bind(this)); 63 | } 64 | 65 | /** 66 | * Handle the click event on the trigger. 67 | * @return {undefined} 68 | */ 69 | handleTriggerClick() { 70 | if (this.root.classList.contains('is-active')) { 71 | this.root.classList.remove('is-active'); 72 | 73 | this.trigger('close'); 74 | } else { 75 | this.root.classList.add('is-active'); 76 | 77 | this.trigger('open'); 78 | } 79 | } 80 | } 81 | 82 | Bulma.registerPlugin('dropdown', Dropdown); 83 | 84 | export default Bulma; 85 | -------------------------------------------------------------------------------- /src/plugins/file.js: -------------------------------------------------------------------------------- 1 | import Bulma from '../core'; 2 | import Plugin from '../plugin'; 3 | 4 | /** 5 | * @module File 6 | * @since 0.1.0 7 | * @author Thomas Erbe 8 | */ 9 | export class File extends Plugin { 10 | /** 11 | * Handle parsing the DOMs data attribute API. 12 | * @param {HTMLElement} element The root element for this plugin 13 | * @return {undefined} 14 | */ 15 | static parseDocument(context) { 16 | let elements; 17 | 18 | if (typeof context.classList === 'object' && context.classList.contains('file')) { 19 | elements = [context]; 20 | } else { 21 | elements = context.querySelectorAll('.file'); 22 | } 23 | 24 | Bulma.each(elements, (element) => { 25 | Bulma(element).file(); 26 | }); 27 | } 28 | 29 | /** 30 | * Plugin constructor 31 | * @param {Object} config The config object for this plugin 32 | * @return {this} The newly created plugin instance 33 | */ 34 | constructor(config, root) { 35 | super(config, root); 36 | 37 | /** 38 | * The root file element. 39 | * @type {HTMLElement} 40 | */ 41 | this.root = this.config.get('root'); 42 | this.root.setAttribute('data-bulma-attached', 'attached'); 43 | 44 | /** 45 | * The element to use as the trigger. 46 | * @type {HTMLELement} 47 | */ 48 | this.input = this.root.querySelector('input'); 49 | 50 | /** 51 | * The element to show the file name. 52 | * @type {HTMLElement} 53 | */ 54 | this.filename = this.root.querySelector('.file-name'); 55 | 56 | this.registerEvents(); 57 | 58 | Bulma(this.root).data('file', this); 59 | 60 | this.trigger('init'); 61 | } 62 | 63 | /** 64 | * Register all the events this module needs. 65 | * @return {undefined} 66 | */ 67 | registerEvents() { 68 | if (this.filename) { 69 | this.input.addEventListener('change', this.handleTriggerChange.bind(this)); 70 | } 71 | 72 | this.root.addEventListener('dragover', (e) => { 73 | e.preventDefault(); 74 | this.addHoverClass(); 75 | }); 76 | 77 | this.root.addEventListener('dragleave', (e) => { 78 | e.preventDefault(); 79 | this.addHoverClass(); 80 | }); 81 | 82 | this.root.addEventListener('drop', (e) => { 83 | e.preventDefault(); 84 | this.removeHoverClass(); 85 | this.input.files = e.dataTransfer.files; 86 | }); 87 | } 88 | 89 | /** 90 | * Handle the click event on the trigger. 91 | * @param {Object} event The event object 92 | * @return {undefined} 93 | */ 94 | handleTriggerChange(event) { 95 | if (event.target.files.length === 0) { 96 | this.clearFileName(); 97 | } 98 | 99 | if (event.target.files.length === 1) { 100 | this.setFileName(event.target.files[0].name); 101 | } 102 | 103 | if (event.target.files.length > 1) { 104 | this.setFileName(event.target.files.length + ' files'); 105 | } 106 | 107 | this.trigger('changed', event); 108 | } 109 | 110 | /** 111 | * Clear the file name element. 112 | * @return {undefined} 113 | */ 114 | clearFileName() { 115 | this.filename.innerHTML = ''; 116 | } 117 | 118 | /** 119 | * Get the selected file's name 120 | * 121 | * @returns {string} 122 | */ 123 | getFilename() { 124 | return this.filename.innerHTML; 125 | } 126 | 127 | /** 128 | * Set the text for the file name element. 129 | * @param {string} value The name of the file to update the label with 130 | * @return {undefined} 131 | */ 132 | setFileName(value) { 133 | this.filename.innerHTML = value; 134 | } 135 | 136 | /** 137 | * Add hover class to root element. 138 | * @return {undefined} 139 | */ 140 | addHoverClass() { 141 | this.root.classList.add('is-hovered'); 142 | } 143 | 144 | /** 145 | * Remove hover class from root element. 146 | * @return {undefined} 147 | */ 148 | removeHoverClass() { 149 | this.root.classList.remove('is-hovered'); 150 | } 151 | } 152 | 153 | Bulma.registerPlugin('file', File); 154 | 155 | export default Bulma; 156 | -------------------------------------------------------------------------------- /src/plugins/message.js: -------------------------------------------------------------------------------- 1 | import Bulma from '../core'; 2 | import DismissableComponent from '../dismissableComponent'; 3 | 4 | /** 5 | * @module Message 6 | * @since 0.1.0 7 | * @author Thomas Erbe 8 | * @extends DismissableComponent 9 | */ 10 | export class Message extends DismissableComponent { 11 | /** 12 | * Handle parsing the DOMs data attribute API. 13 | * @param {HTMLElement} element The root element for this plugin 14 | * @return {undefined} 15 | */ 16 | static parseDocument(context) { 17 | let elements; 18 | 19 | if (typeof context.classList === 'object' && context.classList.container('.message')) { 20 | elements = [context]; 21 | } else { 22 | elements = context.querySelectorAll('.message'); 23 | } 24 | 25 | Bulma.each(elements, (element) => { 26 | let closeBtn = element.querySelector('.delete'); 27 | 28 | Bulma(element).message({ 29 | body: null, 30 | closeButton: closeBtn, 31 | isDismissable: !!closeBtn, 32 | destroyOnDismiss: true, 33 | dismissInterval: element.hasAttribute('data-dismiss-interval') ? element.getAttribute('data-dismiss-interval') : null 34 | }); 35 | }); 36 | } 37 | 38 | /** 39 | * Plugin constructor 40 | * @param {Object} config The config object for this plugin 41 | * @return {this} The newly created instance 42 | */ 43 | constructor(config, root) { 44 | super('message', config, root); 45 | 46 | /** 47 | * The size of the message 48 | * @type {String} Possible values are small, normal, medium or large 49 | */ 50 | this.size = this.config.get('size'); 51 | 52 | /** 53 | * The title of the message 54 | * @type {String} 55 | */ 56 | this.title = this.config.get('title'); 57 | 58 | if (this.title) { 59 | this.createMessageHeader(); 60 | } 61 | 62 | // TODO: Move this into the DismissableComponent class. Due to the required 63 | // changes between different components, we may need a way to trigger this 64 | // when the component is ready. 65 | if (this.isDismissable) { 66 | if (!this.config.get('closeButton')) { 67 | this.prependCloseButton(); 68 | } 69 | 70 | this.setupCloseEvent(); 71 | } 72 | 73 | if (this.size) { 74 | this.setSize(); 75 | } 76 | 77 | Bulma(this.root).data('message', this); 78 | 79 | this.trigger('init'); 80 | } 81 | 82 | /** 83 | * Create the message header 84 | * @return {undefined} 85 | */ 86 | createMessageHeader() { 87 | let header = document.createElement('div'); 88 | header.classList.add('message-header'); 89 | 90 | header.innerHTML = '

' + this.title + '

'; 91 | 92 | this.title = header; 93 | 94 | this.root.insertBefore(this.title, this.root.firstChild); 95 | } 96 | 97 | /** 98 | * Set the size of the message. 99 | * @return {undefined} 100 | */ 101 | setSize() { 102 | this.root.classList.add('is-' + this.size); 103 | } 104 | 105 | /** 106 | * Insert the body text into the component. 107 | * @return {undefined} 108 | */ 109 | insertBody() { 110 | let body = document.createElement('div'); 111 | body.classList.add('message-body'); 112 | body.innerHTML = this.body; 113 | 114 | this.root.appendChild(body); 115 | } 116 | 117 | /** 118 | * Insert the close button before our content. 119 | * @return {undefined} 120 | */ 121 | prependCloseButton() { 122 | this.title.appendChild(this.closeButton); 123 | } 124 | } 125 | 126 | Bulma.registerPlugin('message', Message); 127 | 128 | export default Bulma; 129 | -------------------------------------------------------------------------------- /src/plugins/modal.js: -------------------------------------------------------------------------------- 1 | import Bulma from '../core'; 2 | import Plugin from '../plugin'; 3 | 4 | /** 5 | * @module Modal 6 | * @since 0.1.0 7 | * @author Thomas Erbe 8 | */ 9 | export class Modal extends Plugin { 10 | /** 11 | * Handle parsing the DOM. 12 | * @param {HTMLElement} element The root element for this accordion 13 | * @return {undefined} 14 | */ 15 | static parseDocument() {} 16 | 17 | /** 18 | * Returns an object containing the default config for this plugin. 19 | * @returns {object} The default config object. 20 | */ 21 | static defaultConfig() { 22 | return { 23 | style: 'card', 24 | closable: true 25 | }; 26 | } 27 | 28 | /** 29 | * Plugin constructor 30 | * @param {Object} config The config object for this plugin 31 | * @return {this} The newly created plugin instance 32 | */ 33 | constructor(config, root) { 34 | super(config, root); 35 | 36 | /** @param {string} */ 37 | this.style = this.config.get('style'); 38 | 39 | /** @param {HTMLElement} */ 40 | this.root = this.config.get('root'); 41 | 42 | if(!this.root.classList.contains('modal')) { 43 | this.root.classList.add('modal'); 44 | } 45 | 46 | if(!this.parent) { 47 | if(!this.root.parentNode) { 48 | this.parent = document.body; 49 | 50 | this.parent.appendChild(this.root); 51 | } else { 52 | this.parent = this.root.parentNode; 53 | } 54 | } else { 55 | this.parent.appendChild(this.root); 56 | } 57 | 58 | /** @param {HTMLElement} */ 59 | this.background = Bulma.findOrCreateElement('.modal-background', this.root); 60 | 61 | /** @param {HTMLElement} */ 62 | this.content = this.style === 'card' ? Bulma.findOrCreateElement('.modal-card', this.root) : Bulma.findOrCreateElement('.modal-content', this.root); 63 | 64 | /** @param {boolean} */ 65 | this.closable = this.config.get('closable'); 66 | 67 | /** @param {string|null} */ 68 | this.body = this.config.get('body'); 69 | 70 | /** @param {string|null} */ 71 | this.title = this.config.get('title'); 72 | 73 | if(this.config.get('bodyUrl')) { 74 | Bulma.ajax(this.config.get('bodyUrl')) 75 | .then((response) => { 76 | this.body = response; 77 | this.buildModal(); 78 | }); 79 | } else { 80 | this.buildModal(); 81 | } 82 | 83 | Bulma(this.root).data('modal', this); 84 | 85 | this.trigger('init'); 86 | } 87 | 88 | // Build the modal's HTML 89 | buildModal() { 90 | if(this.style === 'card') { 91 | this.createCardStructure(); 92 | } else { 93 | if(!this.content.innerHTML) { 94 | this.content.innerHTML = this.body; 95 | } 96 | } 97 | 98 | if(this.closable) { 99 | /** @param {HTMLElement} */ 100 | this.closeButton = this.style === 'card' ? Bulma.findOrCreateElement('.delete', this.header, 'button') : Bulma.findOrCreateElement('.modal-close', this.root, 'button'); 101 | } 102 | 103 | if(this.style === 'card') { 104 | this.createButtons(); 105 | } 106 | 107 | this.setupEvents(); 108 | } 109 | 110 | /** 111 | * Create the card style structure 112 | * @returns {void} 113 | */ 114 | createCardStructure() { 115 | /** @param {HTMLElement} */ 116 | this.header = Bulma.findOrCreateElement('.modal-card-head', this.content, 'header'); 117 | 118 | /** @param {HTMLElement} */ 119 | this.headerTitle = Bulma.findOrCreateElement('.modal-card-title', this.header, 'p'); 120 | if(!this.headerTitle.innerHTML) { 121 | this.headerTitle.innerHTML = this.title; 122 | } 123 | 124 | /** @param {HTMLElement} */ 125 | this.cardBody = Bulma.findOrCreateElement('.modal-card-body', this.content, 'section'); 126 | if(!this.cardBody.innerHTML) { 127 | this.cardBody.innerHTML = this.body; 128 | } 129 | 130 | /** @param {HTMLElement} */ 131 | this.footer = Bulma.findOrCreateElement('.modal-card-foot', this.content, 'footer'); 132 | } 133 | 134 | /** 135 | * Setup the events used by this modal. 136 | * @returns {void} 137 | */ 138 | setupEvents() { 139 | if(this.closable) { 140 | this.closeButton.addEventListener('click', this.close.bind(this)); 141 | 142 | this.keyupListenerBound = (evt) => this.keyupListener(evt); 143 | document.addEventListener('keyup', this.keyupListenerBound); 144 | 145 | this.background.addEventListener('click', this.close.bind(this)); 146 | } 147 | } 148 | 149 | /** 150 | * Go through the provided buttons option and create the buttons. 151 | * @returns {void} 152 | */ 153 | createButtons() { 154 | var buttonsConfig = this.config.get('buttons', []); 155 | var modal = this; 156 | 157 | Bulma.each(buttonsConfig, function(buttonConfig) { 158 | var button = Bulma.createElement('button', buttonConfig.classes); 159 | button.innerHTML = buttonConfig.label; 160 | 161 | button.addEventListener('click', function(event) { 162 | buttonConfig.onClick(event); 163 | }); 164 | 165 | modal.footer.appendChild(button); 166 | }); 167 | } 168 | 169 | /** 170 | * Open the modal 171 | * @returns {void} 172 | */ 173 | open() { 174 | this.root.classList.add('is-active'); 175 | document.documentElement.classList.add('is-clipped'); 176 | 177 | this.trigger('open'); 178 | } 179 | 180 | /** 181 | * Close the modal 182 | * @returns {void} 183 | */ 184 | close() { 185 | this.root.classList.remove('is-active'); 186 | document.documentElement.classList.remove('is-clipped'); 187 | 188 | this.trigger('close'); 189 | } 190 | 191 | keyupListener(event) { 192 | if(!this.root.classList.contains('is-active')) { 193 | return; 194 | } 195 | 196 | let key = event.key || event.keyCode; 197 | 198 | if(key === 'Escape' || key === 'Esc' || key === 27) { 199 | this.close(); 200 | } 201 | } 202 | 203 | /** 204 | * Destroy this modal, unregistering element references and removing the modal. 205 | * @returns {void} 206 | */ 207 | destroy() { 208 | super.destroy(); 209 | 210 | this.root.remove(); 211 | 212 | this.parent = null; 213 | this.root = null; 214 | this.background = null; 215 | this.content = null; 216 | 217 | if(this.style === 'card') { 218 | this.header = null; 219 | this.headerTitle = null; 220 | this.cardBody = null; 221 | this.footer = null; 222 | } 223 | 224 | if(this.closable) { 225 | this.closeButton = null; 226 | 227 | document.removeEventListener('keyup', this.keyupListenerBound); 228 | } 229 | 230 | this.config.gets = []; 231 | 232 | this.trigger('destroyed'); 233 | } 234 | } 235 | 236 | Bulma.registerPlugin('modal', Modal); 237 | 238 | export default Bulma; 239 | -------------------------------------------------------------------------------- /src/plugins/navbar.js: -------------------------------------------------------------------------------- 1 | import Bulma from '../core'; 2 | import Plugin from '../plugin'; 3 | 4 | /** 5 | * @module Navbar 6 | * @since 0.1.0 7 | * @author Thomas Erbe 8 | */ 9 | export class Navbar extends Plugin { 10 | /** 11 | * Handle parsing the DOMs data attribute API. 12 | * @param {HTMLElement} element The root element for this instance 13 | * @return {undefined} 14 | */ 15 | static parseDocument(context) { 16 | let elements; 17 | 18 | if (typeof context.classList === 'object' && context.classList.contains('navbar')) { 19 | elements = [context]; 20 | } else { 21 | elements = context.querySelectorAll('.navbar'); 22 | } 23 | 24 | Bulma.each(elements, (element) => { 25 | Bulma(element).navbar({ 26 | sticky: element.hasAttribute('data-sticky') ? true : false, 27 | stickyOffset: element.hasAttribute('data-sticky-offset') ? element.getAttribute('data-sticky-offset') : 0, 28 | hideOnScroll: element.hasAttribute('data-hide-on-scroll') ? true : false, 29 | tolerance: element.hasAttribute('data-tolerance') ? element.getAttribute('data-tolerance') : 0, 30 | hideOffset: element.hasAttribute('data-hide-offset') ? element.getAttribute('data-hide-offset') : null, 31 | shadow: element.hasAttribute('data-sticky-shadow') ? true : false 32 | }); 33 | }); 34 | } 35 | 36 | /** 37 | * Returns an object containing the default config for this plugin. 38 | * @returns {object} The default config object. 39 | */ 40 | static defaultconfig() { 41 | return { 42 | sticky: false, 43 | stickyOffset: 0, 44 | hideOnScroll: false, 45 | tolerance: 0, 46 | hideOffset: null, 47 | shadow: false 48 | }; 49 | } 50 | 51 | /** 52 | * Plugin constructor 53 | * @param {Object} config The config object for this plugin 54 | * @return {this} The newly created plugin instance 55 | */ 56 | constructor(config, root) { 57 | super(config, root); 58 | 59 | // Work out the parent if it hasn't been supplied as an option. 60 | if (this.parent === null) { 61 | this.parent = this.config.get('root').parentNode; 62 | } 63 | 64 | /** 65 | * The root navbar element. 66 | * @type {HTMLElement} 67 | */ 68 | this.root = this.config.get('root'); 69 | this.root.setAttribute('data-bulma-attached', 'attached'); 70 | 71 | /** 72 | * The element used for the trigger. 73 | * @type {HTMLElement} 74 | */ 75 | this.triggerElement = this.root.querySelector('.navbar-burger'); 76 | 77 | /** 78 | * The target element. 79 | * @type {HTMLELement} 80 | */ 81 | this.target = this.root.querySelector('.navbar-menu'); 82 | 83 | /** 84 | * Should this navbar stick to the top of the page? 85 | * @type {boolean} 86 | */ 87 | this.sticky = typeof window === 'object' && !!this.config.get('sticky'); 88 | 89 | /** 90 | * The offset in pixels before the navbar will stick to the top of the page 91 | * @type {number} 92 | */ 93 | this.stickyOffset = parseInt(this.config.get('stickyOffset')); 94 | 95 | /** 96 | * Should the navbar hide when scrolling? Note: this just applies a 'is-hidden-scroll' class. 97 | * @type {boolean} 98 | */ 99 | this.hideOnScroll = !!this.config.get('hideOnScroll'); 100 | 101 | /** 102 | * The amount of tolerance required before checking the navbar should hide/show 103 | * @type {number} 104 | */ 105 | this.tolerance = parseInt(this.config.get('tolerance')); 106 | 107 | /** 108 | * Add a shadow when the navbar is sticky? 109 | * @type {boolean} 110 | */ 111 | this.shadow = !!this.config.get('shadow'); 112 | 113 | /** 114 | * The offset in pixels before the navbar will be hidden, defaults to the height of the navbar 115 | * @type {number} 116 | */ 117 | this.hideOffset = parseInt(this.config.get('hideOffset', Math.max(this.root.scrollHeight, this.stickyOffset))); 118 | 119 | /** 120 | * The last scroll Y known, this is used to calculate scroll direction 121 | * @type {number} 122 | */ 123 | this.lastScrollY = 0; 124 | 125 | /** 126 | * An array of any navbar dropdowns 127 | * @type {NodeList} 128 | */ 129 | this.dropdowns = this.root.querySelectorAll('.navbar-item.has-dropdown:not(.is-hoverable)'); 130 | 131 | /** 132 | * Bind the relevant event handlers to this instance. So that we can remove them if needed 133 | */ 134 | this.handleScroll = this.handleScroll.bind(this); 135 | 136 | Bulma(this.root).data('navbar', this); 137 | 138 | this.registerEvents(); 139 | } 140 | 141 | /** 142 | * Register all the events this module needs. 143 | * @return {undefined} 144 | */ 145 | registerEvents() { 146 | if(this.triggerElement) { 147 | this.triggerElement.addEventListener('click', this.handleTriggerClick.bind(this)); 148 | } 149 | 150 | if (this.sticky) { 151 | this.enableSticky(); 152 | } 153 | 154 | Bulma.each(this.dropdowns, (dropdown) => { 155 | dropdown.addEventListener('click', this.handleDropdownTrigger); 156 | }); 157 | } 158 | 159 | /** 160 | * Handle the click event on the trigger. 161 | * @return {undefined} 162 | */ 163 | handleTriggerClick() { 164 | if (this.target.classList.contains('is-active')) { 165 | this.target.classList.remove('is-active'); 166 | this.triggerElement.classList.remove('is-active'); 167 | } else { 168 | this.target.classList.add('is-active'); 169 | this.triggerElement.classList.add('is-active'); 170 | } 171 | } 172 | 173 | /** 174 | * Handle the scroll event 175 | * @return {undefined} 176 | */ 177 | handleScroll() { 178 | this.toggleSticky(window.pageYOffset); 179 | } 180 | 181 | /** 182 | * Handle the click handler for any dropdowns found within the navbar 183 | */ 184 | handleDropdownTrigger() { 185 | if (this.classList.contains('is-active')) { 186 | this.classList.remove('is-active'); 187 | } else { 188 | this.classList.add('is-active'); 189 | } 190 | } 191 | 192 | /** 193 | * Enable the sticky feature by attaching the scroll event. 194 | */ 195 | enableSticky() { 196 | window.addEventListener('scroll', this.handleScroll); 197 | this.root.setAttribute('data-sticky', ''); 198 | this.sticky = true; 199 | } 200 | 201 | /** 202 | * Disable the sticky feature by removing the scroll event. 203 | */ 204 | disableSticky() { 205 | window.removeEventListener('scroll', this.handleScroll); 206 | this.root.removeAttribute('data-sticky'); 207 | this.sticky = false; 208 | } 209 | 210 | /** 211 | * Enable hide on scroll. Also enable sticky if it's not already. 212 | */ 213 | enableHideOnScroll() { 214 | if (!this.sticky) { 215 | this.enableSticky(); 216 | } 217 | 218 | this.root.setAttribute('data-hide-on-scroll', ''); 219 | this.hideOnScroll = true; 220 | } 221 | 222 | /** 223 | * Disable hide on scroll, and show the navbar again if it's hidden. 224 | */ 225 | disableHideOnScroll() { 226 | this.root.removeAttribute('data-hide-on-scroll'); 227 | this.hideOnScroll = false; 228 | this.root.classList.remove('is-hidden-scroll'); 229 | } 230 | 231 | /** 232 | * Toggle the navbar's sticky state 233 | * @param {number} scrollY The amount of pixels that has been scrolled 234 | * @return {undefined} 235 | */ 236 | toggleSticky(scrollY) { 237 | if (scrollY > this.stickyOffset) { 238 | this.root.classList.add('is-fixed-top'); 239 | document.body.classList.add('has-navbar-fixed-top'); 240 | 241 | if (this.shadow) { 242 | this.root.classList.add('has-shadow'); 243 | } 244 | } else { 245 | this.root.classList.remove('is-fixed-top'); 246 | document.body.classList.remove('has-navbar-fixed-top'); 247 | 248 | if (this.shadow) { 249 | this.root.classList.remove('has-shadow'); 250 | } 251 | } 252 | 253 | if (this.hideOnScroll) { 254 | let scrollDirection = this.calculateScrollDirection(scrollY, this.lastScrollY); 255 | let triggeredTolerance = this.difference(scrollY, this.lastScrollY) >= this.tolerance; 256 | 257 | if (scrollDirection === 'down') { 258 | // only hide the navbar at the top if we reach a certain offset so the hiding is more smooth 259 | let isBeyondTopOffset = scrollY > this.hideOffset; 260 | if (triggeredTolerance && isBeyondTopOffset) { 261 | this.root.classList.add('is-hidden-scroll'); 262 | } 263 | } else { 264 | // if scrolling up to the very top where the navbar would be by default always show it 265 | let isAtVeryTop = scrollY < this.hideOffset; 266 | if (triggeredTolerance || isAtVeryTop) { 267 | this.root.classList.remove('is-hidden-scroll'); 268 | } 269 | } 270 | 271 | this.lastScrollY = scrollY; 272 | } 273 | } 274 | 275 | difference(a, b) { 276 | if (a > b) { 277 | return a - b; 278 | } else { 279 | return b - a; 280 | } 281 | } 282 | 283 | calculateScrollDirection(currentY, lastY) { 284 | return currentY >= lastY ? 'down' : 'up'; 285 | } 286 | } 287 | 288 | Bulma.registerPlugin('navbar', Navbar); 289 | 290 | export default Bulma; 291 | -------------------------------------------------------------------------------- /src/plugins/notification.js: -------------------------------------------------------------------------------- 1 | import Bulma from '../core'; 2 | import DismissableComponent from '../dismissableComponent'; 3 | 4 | /** 5 | * @module Notification 6 | * @since 0.1.0 7 | * @author Thomas Erbe 8 | * @extends DismissableComponent 9 | */ 10 | export class Notification extends DismissableComponent { 11 | /** 12 | * Handle parsing the DOMs data attribute API. 13 | * @param {HTMLElement} element The root element for this instance 14 | * @return {undefined} 15 | */ 16 | static parseDocument(context) { 17 | let elements; 18 | 19 | if (typeof context.classList === 'object' && context.classList.contains('notification')) { 20 | elements = [context]; 21 | } else { 22 | elements = context.querySelectorAll('.notification'); 23 | } 24 | 25 | Bulma.each(elements, (element) => { 26 | let bulmaElement = Bulma(element); 27 | 28 | if (bulmaElement.data('notification')) { 29 | return; 30 | } 31 | 32 | let closeBtn = element.querySelector('.delete'); 33 | 34 | bulmaElement.notification({ 35 | body: null, 36 | closeButton: closeBtn, 37 | isDismissable: !!closeBtn, 38 | destroyOnDismiss: true, 39 | dismissInterval: element.hasAttribute('data-dismiss-interval') ? element.getAttribute('data-dismiss-interval') : null 40 | }); 41 | }); 42 | } 43 | 44 | /** 45 | * Plugin constructor 46 | * @param {Object} config The config object for this plugin 47 | * @return {this} The newly created instance 48 | */ 49 | constructor(config, root) { 50 | super('notification', config, root); 51 | 52 | // TODO: Move this into the DismissableComponent class. Due to the required 53 | // changes between different components, we may need a way to trigger this 54 | // when the component is ready. 55 | if (this.isDismissable) { 56 | if (!this.config.has('closeButton')) { 57 | this.prependCloseButton(); 58 | } 59 | 60 | this.setupCloseEvent(); 61 | } 62 | 63 | Bulma(this.root).data('notification', this); 64 | 65 | this.trigger('init'); 66 | } 67 | } 68 | 69 | Bulma.registerPlugin('notification', Notification); 70 | 71 | export default Bulma; 72 | -------------------------------------------------------------------------------- /src/plugins/panelTabs.js: -------------------------------------------------------------------------------- 1 | import Bulma from '../core'; 2 | import Plugin from '../plugin'; 3 | 4 | /** 5 | * @module PanelTabs 6 | * @since 0.12.0 7 | * @author Thomas Erbe 8 | */ 9 | export class PanelTabs extends Plugin { 10 | /** 11 | * Handle parsing the DOMs data attribute API. 12 | * @param {HTMLElement} context The root element for this instance 13 | * @returns {undefined} 14 | */ 15 | static parseDocument(context) { 16 | let elements; 17 | 18 | if (typeof context.classList === 'object' && context.classList.contains('panel')) { 19 | elements = [context]; 20 | } else { 21 | elements = context.querySelectorAll('.panel'); 22 | } 23 | 24 | Bulma.each(elements, (element) => { 25 | if(element.querySelector('.panel-tabs') === null) { 26 | return; 27 | } 28 | 29 | Bulma(element).panelTabs(); 30 | }); 31 | } 32 | 33 | /** 34 | * Returns an object containing the default config for this plugin. 35 | * @returns {object} The default config object. 36 | */ 37 | static defaultConfig() { 38 | return {}; 39 | } 40 | 41 | /** 42 | * Plugin constructor 43 | * @param {Object} config The config object for this plugin 44 | * @return {this} The newly created instance 45 | */ 46 | constructor(config, root) { 47 | super(config, root); 48 | 49 | /** 50 | * The root tab element 51 | * @param {HTMLElement} 52 | */ 53 | this.root = this.config.get('root'); 54 | this.root.setAttribute('data-bulma-attached', 'attached'); 55 | 56 | /** 57 | * The tab nav container 58 | * @param {HTMLElement} 59 | */ 60 | this.nav = this.findNav(); 61 | 62 | /** 63 | * The tab's nav items 64 | * @param {HTMLElement[]} 65 | */ 66 | this.navItems = this.findNavItems(); 67 | 68 | /** 69 | * The tab's content items 70 | * @param {HTMLElement[]} 71 | */ 72 | this.contentItems = this.findContentItems(); 73 | 74 | this.setupNavEvents(); 75 | 76 | this.on('init', this.showActiveTab.bind(this)); 77 | 78 | Bulma(this.root).data('panelTabs', this); 79 | 80 | this.trigger('init'); 81 | } 82 | 83 | /** 84 | * Find the tab navigation container. 85 | * @returns {HTMLElement} The navigation container 86 | */ 87 | findNav() { 88 | return this.root.querySelector('.panel-tabs'); 89 | } 90 | 91 | /** 92 | * Find each individual tab item 93 | * @returns {NodeListOf} An array of the found items 94 | */ 95 | findNavItems() { 96 | return this.nav.querySelectorAll('a'); 97 | } 98 | 99 | /** 100 | * Find each individual content item 101 | * @returns {NodeListOf} An array of the found items 102 | */ 103 | findContentItems() { 104 | return this.root.querySelectorAll('.panel-block[data-category]'); 105 | } 106 | 107 | /** 108 | * Setup the events to handle tab changing 109 | * @returns {void} 110 | */ 111 | setupNavEvents() { 112 | Bulma.each(this.navItems, (navItem) => { 113 | navItem.addEventListener('click', () => { 114 | this.setActive(navItem.getAttribute('data-target')); 115 | }); 116 | }); 117 | } 118 | 119 | /** 120 | * Show the correct category and mark the tab as active. 121 | * 122 | * @param {string|null} category The new category to set 123 | */ 124 | setActive(category) { 125 | this.navItems.forEach((item) => { 126 | if(item.getAttribute('data-target') === category) { 127 | item.classList.add('is-active'); 128 | } else { 129 | item.classList.remove('is-active'); 130 | } 131 | }); 132 | 133 | this.contentItems.forEach((item) => { 134 | if(item.getAttribute('data-category') === category || category === null) { 135 | item.classList.remove('is-hidden'); 136 | } else { 137 | item.classList.add('is-hidden'); 138 | } 139 | }); 140 | } 141 | 142 | /** 143 | * This is called on init and will setup the panel tabs for the current active tab, if any 144 | */ 145 | showActiveTab() { 146 | let activeNavFound = false; 147 | 148 | Bulma.each(this.navItems, (navItem) => { 149 | if(navItem.classList.contains('is-active')) { 150 | this.setActive(navItem.getAttribute('data-target')); 151 | activeNavFound = true; 152 | } 153 | }); 154 | 155 | // If no nav item has is-active then use the first one 156 | if(!activeNavFound) { 157 | this.setActive(this.navItems[0].getAttribute('data-target')); 158 | } 159 | } 160 | } 161 | 162 | Bulma.registerPlugin('panelTabs', PanelTabs); 163 | 164 | export default Bulma; 165 | -------------------------------------------------------------------------------- /src/plugins/tabs.js: -------------------------------------------------------------------------------- 1 | import Bulma from '../core'; 2 | import Plugin from '../plugin'; 3 | 4 | /** 5 | * @module Tabs 6 | * @since 0.4.0 7 | * @author Thomas Erbe 8 | */ 9 | export class Tabs extends Plugin { 10 | /** 11 | * Handle parsing the DOMs data attribute API. 12 | * @param {HTMLElement} element The root element for this instance 13 | * @returns {undefined} 14 | */ 15 | static parseDocument(context) { 16 | let elements; 17 | 18 | if (typeof context.classList === 'object' && context.classList.has('tabs-wrapper')) { 19 | elements = [context]; 20 | } else { 21 | elements = context.querySelectorAll('.tabs-wrapper'); 22 | } 23 | 24 | Bulma.each(elements, (element) => { 25 | Bulma(element).tabs({ 26 | hover: element.hasAttribute('data-hover') ? true : false 27 | }); 28 | }); 29 | } 30 | 31 | /** 32 | * Returns an object containing the default config for this plugin. 33 | * @returns {object} The default config object. 34 | */ 35 | static defaultConfig() { 36 | return { 37 | hover: false 38 | }; 39 | } 40 | 41 | /** 42 | * Plugin constructor 43 | * @param {Object} config The config object for this plugin 44 | * @return {this} The newly created instance 45 | */ 46 | constructor(config, root) { 47 | super(config, root); 48 | 49 | /** 50 | * The root tab element 51 | * @param {HTMLElement} 52 | */ 53 | this.root = this.config.get('root'); 54 | this.root.setAttribute('data-bulma-attached', 'attached'); 55 | 56 | /** 57 | * Whether the tabs should be changed when the nav item is hovered over 58 | * @param {boolean} 59 | */ 60 | this.hover = this.config.get('hover'); 61 | 62 | /** 63 | * The tab nav container 64 | * @param {HTMLElement} 65 | */ 66 | this.nav = this.findNav(); 67 | 68 | /** 69 | * The tab's nav items 70 | * @param {HTMLElement[]} 71 | */ 72 | this.navItems = this.findNavItems(); 73 | 74 | /** 75 | * The tab content container 76 | * @param {HTMLElement} 77 | */ 78 | this.content = this.findContent(); 79 | 80 | /** 81 | * The tab's content items 82 | * @param {HTMLElement[]} 83 | */ 84 | this.contentItems = this.findContentItems(); 85 | 86 | this.setupNavEvents(); 87 | 88 | Bulma(this.root).data('tabs', this); 89 | 90 | this.trigger('init'); 91 | } 92 | 93 | /** 94 | * Find the tab navigation container. 95 | * @returns {HTMLElement} The navigation container 96 | */ 97 | findNav() { 98 | return this.root.querySelector('.tabs'); 99 | } 100 | 101 | /** 102 | * Find each individual tab item 103 | * @returns {HTMLElement[]} An array of the found items 104 | */ 105 | findNavItems() { 106 | return this.nav.querySelectorAll('li'); 107 | } 108 | 109 | /** 110 | * Find the tab content container. 111 | * @returns {HTMLElement} The content container 112 | */ 113 | findContent() { 114 | return this.root.querySelector('.tabs-content'); 115 | } 116 | 117 | /** 118 | * Find each individual content item 119 | * @returns {HTMLElement[]} An array of the found items 120 | */ 121 | findContentItems() { 122 | // We have to use the root here as the querySelectorAll API doesn't 123 | // support using '>' as the first character. So we have to have a 124 | // class to start with. 125 | return this.root.querySelectorAll('.tabs-content > ul > li'); 126 | } 127 | 128 | /** 129 | * Setup the events to handle tab changing 130 | * @returns {void} 131 | */ 132 | setupNavEvents() { 133 | Bulma.each(this.navItems, (navItem, index) => { 134 | navItem.addEventListener('click', () => { 135 | this.setActive(index); 136 | }); 137 | 138 | if (this.hover) { 139 | navItem.addEventListener('mouseover', () => { 140 | this.setActive(index); 141 | }); 142 | } 143 | }); 144 | } 145 | 146 | /** 147 | * Set the provided tab's index as the active tab. 148 | * 149 | * @param {integer} index The new index to set 150 | */ 151 | setActive(index) { 152 | Bulma.each(this.navItems, (navItem) => { 153 | navItem.classList.remove('is-active'); 154 | }); 155 | 156 | Bulma.each(this.contentItems, (contentItem) => { 157 | contentItem.classList.remove('is-active'); 158 | }); 159 | 160 | this.navItems[index].classList.add('is-active'); 161 | this.contentItems[index].classList.add('is-active'); 162 | } 163 | } 164 | 165 | Bulma.registerPlugin('tabs', Tabs); 166 | 167 | export default Bulma; 168 | -------------------------------------------------------------------------------- /supporters.md: -------------------------------------------------------------------------------- 1 | # BulmaJS Supporters 2 | **This file serves as a directory for the kind, generous BulmaJS users who have supported the BulmaJS project.** 3 | 4 | # Patron 5 | The supporters below are those who are currently or have been a regular supporter of this project on [Patreon](https://www.patreon.com/vizuaalog). 6 | 7 | ### Legendary supporters 8 | There are currently no supporters of this tier. 9 | 10 | ### Very generous supporters 11 | There are currently no supporters of this tier. 12 | 13 | ### Generous supporters 14 | There are currently no supporters of this tier. 15 | 16 | ### Awesome supporters 17 | There are currently no supporters of this tier. 18 | 19 | # Paypal 20 | **The supports below are those who have generously donated via Paypal.** 21 | 22 | Andreas Pantle -------------------------------------------------------------------------------- /tests/alert.js: -------------------------------------------------------------------------------- 1 | QUnit.module('Alert'); 2 | 3 | let alert; 4 | 5 | QUnit.testDone(function() { 6 | if(!alert) return; 7 | alert.destroy(); 8 | }); 9 | 10 | QUnit.test('Correct root classes are added', function(assert) { 11 | alert = Bulma().alert({ 12 | title: 'Test alert' 13 | }); 14 | 15 | assert.ok(document.body.querySelector('.alert'), 'The alert class has been added.'); 16 | assert.ok(document.body.querySelector('.modal'), 'The modal class has been added.'); 17 | }); 18 | 19 | QUnit.test('The header is created if showHeader is true', function(assert) { 20 | alert = Bulma().alert({ 21 | title: "Hello world" 22 | }); 23 | 24 | assert.ok(alert.root.querySelector('.modal-card-head'), 'The header is created'); 25 | assert.ok(alert.root.querySelector('.modal-card-head').innerHTML.indexOf('Hello world') !== -1, 'The header is created'); 26 | }); 27 | 28 | QUnit.test('The header is not created if showHeader is false', function(assert) { 29 | alert = Bulma().alert({ 30 | title: "Hello world", 31 | showHeader: false 32 | }); 33 | 34 | assert.notOk(alert.root.querySelector('.modal-card-head'), 'The header is created'); 35 | }); 36 | 37 | QUnit.test('The body is created with the supplied text', function(assert) { 38 | alert = Bulma().alert({ 39 | body: 'Hello world' 40 | }); 41 | 42 | assert.ok(alert.root.querySelector('.modal-card-body'), 'The body is created'); 43 | assert.ok(alert.root.querySelector('.modal-card-body').innerHTML.indexOf('Hello world') !== -1, 'The body is created'); 44 | }); 45 | 46 | QUnit.test('The footer is created', function(assert) { 47 | alert = Bulma().alert({}); 48 | 49 | assert.ok(alert.root.querySelector('.modal-card-foot'), 'The footer is created'); 50 | }); 51 | 52 | QUnit.test('Confirm button is created', function(assert) { 53 | alert = Bulma().alert({ 54 | type: 'danger', 55 | confirm: 'Okay' 56 | }); 57 | 58 | assert.ok(alert.root.querySelector('button.is-danger'), 'The confirm button is created'); 59 | assert.ok(alert.root.querySelector('button.is-danger').innerHTML.indexOf('Okay') !== -1); 60 | }); 61 | 62 | QUnit.test('Cancel button is created when the cancel prop is supplied', function(assert) { 63 | alert = Bulma().alert({ 64 | type: 'danger', 65 | cancel: 'Nope' 66 | }); 67 | 68 | assert.ok(alert.root.querySelectorAll('button').length === 2, 'The confirm button is created'); 69 | assert.ok(alert.root.innerHTML.indexOf('Nope') !== -1); 70 | }); -------------------------------------------------------------------------------- /tests/core.js: -------------------------------------------------------------------------------- 1 | QUnit.module('Core'); 2 | 3 | QUnit.test('A plugin can be registered with core', function(assert) { 4 | createTestPlugin(); 5 | 6 | assert.ok(Bulma.plugins.hasOwnProperty('testplugin'), 'Plugin reference is added to the plugins object.'); 7 | 8 | assert.ok(Bulma.plugins.testplugin.hasOwnProperty('handler'), 'Plugin reference has a handler property.'); 9 | 10 | assert.ok(Bulma.plugins.testplugin.hasOwnProperty('priority'), 'Plugin reference has a priority property.'); 11 | assert.ok(Bulma.plugins.testplugin.priority === 1, 'The priority of a plugin is correctly set.'); 12 | 13 | assert.ok(Bulma.prototype.hasOwnProperty('testplugin'), 'Plugin reference added to the Bulma class instance.'); 14 | }); 15 | 16 | QUnit.test('That the Bulma document parser correctly initialises a plugin', function(assert) { 17 | createTestPlugin(); 18 | 19 | let elem = document.createElement('div'); 20 | document.body.appendChild(testCreateElem('div', 'testplugin', 'firsttest')); 21 | 22 | Bulma.parseDocument(document.body); 23 | 24 | assert.ok(Bulma('.firsttest').data('testplugin'), 'A single instance of testplugin has been created.'); 25 | }); 26 | 27 | QUnit.test('That the Bulma document parser correctly intialises multiple instances of a plugin', function(assert) { 28 | createTestPlugin(); 29 | 30 | document.body.appendChild(testCreateElem('div', 'testplugin', 'secondtest')); 31 | 32 | document.body.appendChild(testCreateElem('div', 'testplugin', 'thirdtest')); 33 | 34 | Bulma.parseDocument(document.body); 35 | 36 | assert.ok(Bulma('.secondtest').data('testplugin'), 'The first div has an instance of test plugin created.'); 37 | assert.ok(Bulma('.thirdtest').data('testplugin'), 'The second div has an instance of test plugin created.'); 38 | }); 39 | 40 | QUnit.test('The createElement method will return the correct element with classes', function(assert) { 41 | let myElement = Bulma.createElement('div', ['testclass1', 'testclass2']); 42 | 43 | assert.ok(myElement.tagName === 'DIV', 'The method creates the correct type of element.'); 44 | assert.ok(myElement.classList.contains('testclass1'), 'The first class is added to the element.'); 45 | assert.ok(myElement.classList.contains('testclass2'), 'The second class is added to the element.'); 46 | }); 47 | 48 | QUnit.test('The findOrCreateElement method will return an existing element, or create a new one', function(assert) { 49 | let myElement2 = testCreateElem('div', 'myelement2'); 50 | document.body.appendChild(myElement2); 51 | 52 | let myElement3 = Bulma.findOrCreateElement('.elementmissing', document.body, 'div', ['myelement3']); 53 | 54 | assert.equal(Bulma.findOrCreateElement('.myelement2'), myElement2, 'The existing element is returned without creating a new one'); 55 | assert.ok(myElement3 instanceof HTMLElement, 'The new element is created and returned as it does not exist.'); 56 | }); 57 | 58 | QUnit.test('The stripScripts method will remove any scripts from the string', function(assert) { 59 | let myString = 'This is a test'; 60 | 61 | assert.equal(Bulma._stripScripts(myString), 'This is a test', 'The script is removed from the string.'); 62 | }) 63 | 64 | QUnit.test('Data can be added and retrieved to/from an element', function(assert) { 65 | let myElement4 = Bulma('.myelement2'); 66 | myElement4.data('hello', 'world'); 67 | 68 | assert.equal(myElement4.data('hello'), 'world', 'The data has been set and matches when retrieved.'); 69 | }); -------------------------------------------------------------------------------- /tests/helpers.js: -------------------------------------------------------------------------------- 1 | function testCreateElem(type, ...classes) { 2 | let e = document.createElement(type); 3 | e.classList.add(...classes); 4 | return e; 5 | } 6 | 7 | function createTestPlugin() { 8 | Bulma.registerPlugin('testplugin', class TestPlugin { 9 | static create(config) { return 'testplugin'; } 10 | 11 | static parseDocument(context) { 12 | let elements = context.querySelectorAll('.testplugin'); 13 | 14 | Bulma.each(elements, (element) => { 15 | Bulma(element).data('testplugin', new TestPlugin({ element: element })); 16 | }); 17 | }; 18 | }, 1); 19 | } -------------------------------------------------------------------------------- /tests/test-runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BulmaJS Tests 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | var entry = require('webpack-glob-entry'); 3 | 4 | module.exports = { 5 | entry: entry('./src/bulma.js', './src/plugins/*.js'), 6 | output: { 7 | filename: '[name].js', 8 | path: path.resolve(__dirname, 'dist'), 9 | library: 'Bulma', 10 | libraryTarget: 'umd', 11 | libraryExport: 'default', 12 | umdNamedDefine: true 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | exclude: /node_modules/, 19 | use: { 20 | loader: 'babel-loader' 21 | } 22 | } 23 | ] 24 | } 25 | }; --------------------------------------------------------------------------------