├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .husky ├── .gitignore └── commit-msg ├── .markdownlint.json ├── .npmrc ├── .travis.yml ├── .vcmrc ├── .vscode ├── launch.json └── settings.json ├── .wireit ├── 646576 │ └── lock ├── 6275696c64 │ ├── fingerprint │ ├── lock │ └── manifest └── 6275696c643a64656d6f │ ├── fingerprint │ ├── lock │ └── manifest ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.js ├── demo ├── assets │ ├── card-16-9.jpg │ ├── component.svg │ ├── github-logo.svg │ ├── github-mark-32px.png │ ├── ic_button_24px.svg │ ├── ic_card_24px.svg │ ├── ic_component_24px.svg │ ├── ic_component_24px_white.svg │ ├── ic_dialog_24px.svg │ ├── ic_list_24px.svg │ ├── ic_menu_24px.svg │ ├── ic_progress_activity.svg │ ├── ic_radio_button_24px.svg │ ├── ic_responsive_layout_24px.svg │ ├── ic_ripple_24px.svg │ ├── ic_selection_control_24px.svg │ ├── ic_shadow_24px.svg │ ├── ic_side_navigation_24px.svg │ ├── ic_slider_24px.svg │ ├── ic_switch_24px.svg │ ├── ic_tabs_24px.svg │ ├── ic_text_field_24px.svg │ ├── ic_theme_24px.svg │ ├── ic_toast_24px.svg │ ├── ic_toolbar_24px.svg │ ├── ic_typography_24px.svg │ ├── logo.svg │ ├── mdc-logo.svg │ ├── tile-1-1.jpg │ ├── vue-awesome.png │ ├── vue-banner.png │ ├── vue-logo.-dark.png │ └── vue-logo.png ├── component.vue ├── components │ ├── banner │ │ ├── README.md │ │ ├── banner.js │ │ └── demo.vue │ ├── button │ │ ├── README.md │ │ ├── button.js │ │ ├── demo.vue │ │ └── next.svg │ ├── card │ │ ├── README.md │ │ ├── card.js │ │ └── demo.vue │ ├── checkbox │ │ ├── README.md │ │ ├── checkbox.js │ │ └── demo.vue │ ├── chips │ │ ├── README.md │ │ └── demo.vue │ ├── circular-progress │ │ ├── README.md │ │ ├── circular-progress.js │ │ └── demo.vue │ ├── data-table │ │ ├── README.md │ │ ├── data-table.js │ │ └── demo.vue │ ├── dialog │ │ ├── README.md │ │ ├── demo.vue │ │ └── dialog.js │ ├── drawer │ │ ├── README.md │ │ ├── demo.vue │ │ └── drawer.js │ ├── fab │ │ ├── README.md │ │ ├── demo.vue │ │ └── fab.js │ ├── icon-button │ │ ├── README.md │ │ ├── demo.vue │ │ └── icon-button.js │ ├── layout-grid │ │ ├── README.md │ │ └── demo.vue │ ├── linear-progress │ │ ├── README.md │ │ ├── demo.vue │ │ └── linear-progress.js │ ├── list │ │ ├── README.md │ │ ├── demo.vue │ │ └── list.js │ ├── material-icon │ │ ├── README.md │ │ └── demo.vue │ ├── menu │ │ ├── README.md │ │ ├── demo.vue │ │ └── menu.js │ ├── radio │ │ ├── README.md │ │ ├── demo.vue │ │ └── radio.js │ ├── segmented-button │ │ ├── README.md │ │ ├── demo.vue │ │ └── segmented-button.js │ ├── select │ │ ├── README.md │ │ ├── demo.vue │ │ └── select.js │ ├── slider │ │ ├── README.md │ │ ├── demo.vue │ │ └── slider.js │ ├── snackbar │ │ ├── README.md │ │ ├── demo.vue │ │ └── snackbar.js │ ├── styles.scss │ ├── switch │ │ ├── README.md │ │ ├── demo.vue │ │ └── switch.js │ ├── tabs │ │ ├── README.md │ │ ├── demo.vue │ │ └── tabs.js │ ├── textfield │ │ ├── README.md │ │ ├── demo.vue │ │ └── textfield.js │ ├── tooltip │ │ ├── README.md │ │ ├── demo.vue │ │ └── tooltip.js │ └── top-app-bar │ │ ├── README.md │ │ ├── demo.vue │ │ └── top-app-bar.js ├── doc.vue ├── home.vue ├── index.html ├── index.vue ├── links.js ├── main.js ├── routes.js ├── styles │ ├── _constant.scss │ ├── demo.scss │ ├── index.js │ └── markdown.scss ├── top-app-bar.vue └── utils │ ├── case.js │ └── index.js ├── dist ├── vue-material-adapter.amd.min.js ├── vue-material-adapter.cjs.min.js └── vue-material-adapter.esm.js ├── docs ├── about.md ├── getting-started.md └── theming.md ├── examples ├── README.md ├── basic-vue-ts │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc.json │ ├── README.md │ ├── env.d.ts │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── App.vue │ │ ├── main.ts │ │ ├── shims-vue.d.ts │ │ └── styles │ │ │ ├── _index.scss │ │ │ ├── button.scss │ │ │ ├── shape.scss │ │ │ ├── snackbar.scss │ │ │ └── theme.scss │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── basic-vue │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc.json │ ├── README.md │ ├── index.html │ ├── package.json │ ├── src │ │ ├── app.vue │ │ ├── main.js │ │ └── styles │ │ │ ├── _index.scss │ │ │ ├── button.scss │ │ │ ├── shape.scss │ │ │ ├── snackbar.scss │ │ │ └── theme.scss │ └── vite.config.js └── pwa-express │ └── README.md ├── jest.config.js ├── jsconfig.json ├── mcw-docs └── List.Evolution.Public.Documentation.PUBLIC.DRAFT.pdf ├── package-lock.json ├── package.json ├── release-notes.md ├── scripts ├── build.mjs ├── bundles.mjs └── cp-pkgs.js ├── src ├── banner │ ├── banner-content.vue │ ├── banner.js │ ├── banner.vue │ └── index.js ├── base │ ├── base-plugin.js │ ├── custom-event.js │ ├── custom-link.js │ ├── form-field-wrapper.js │ ├── index.js │ └── touch-wrapper.js ├── button │ ├── button.js │ ├── button.vue │ └── index.js ├── card │ ├── card-action-buttons.js │ ├── card-action-icons.js │ ├── card-actions.js │ ├── card-media.js │ ├── card-primary-action.js │ ├── card.js │ └── index.js ├── checkbox │ ├── checkbox-content.js │ ├── checkbox.js │ ├── checkbox.vue │ └── index.js ├── chips │ ├── chip-action.js │ ├── chip-action.vue │ ├── chip-checkmark.js │ ├── chip-checkmark.vue │ ├── chip-set.js │ ├── chip-set.vue │ ├── chip.js │ ├── chip.vue │ ├── index.js │ ├── trailing-action.jsX │ └── trailing-action.vueX ├── circular-progress │ ├── circular-progress.js │ ├── circular-progress.vue │ └── index.js ├── data-table │ ├── data-table.js │ └── index.js ├── dialog │ ├── dialog-button.js │ ├── dialog-content.js │ ├── dialog-footer.js │ ├── dialog-title.js │ ├── dialog.js │ ├── dialog.vue │ └── index.js ├── drawer │ ├── drawer.js │ ├── drawer.vue │ └── index.js ├── fab │ ├── fab.js │ ├── fab.vue │ └── index.js ├── floating-label │ ├── floating-label.js │ └── index.js ├── icon-button │ ├── icon-button.js │ ├── icon-button.vue │ ├── icon-toggle.js │ └── index.js ├── index.js ├── layout-grid │ ├── index.js │ ├── layout-cell.js │ ├── layout-grid.js │ └── layout-inner-grid.js ├── line-ripple │ ├── index.js │ └── line-ripple.js ├── linear-progress │ ├── index.js │ ├── linear-progress.js │ └── linear-progress.vue ├── list │ ├── index.js │ ├── list-item.js │ ├── list-item.vue │ └── list.js ├── material-icon │ ├── index.js │ └── material-icon.js ├── menu │ ├── index.js │ ├── menu-anchor.js │ ├── menu-item.js │ ├── menu-surface.js │ ├── menu.js │ └── menu.vue ├── notched-outline │ ├── index.js │ └── notched-outline.js ├── radio │ ├── index.js │ ├── radio.js │ └── radio.vue ├── ripple │ ├── index.js │ └── ripple-plugin.js ├── segmented-button │ ├── index.js │ ├── segment.js │ ├── segment.vue │ └── segmented-button.js ├── select │ ├── index.js │ ├── select-helper-text.js │ ├── select-icon.js │ ├── select.js │ └── select.vue ├── slider │ ├── index.js │ ├── slider.js │ └── slider.vue ├── snackbar │ ├── index.js │ ├── snackbar-queue.js │ ├── snackbar.js │ └── snackbar.vue ├── switch │ ├── index.js │ ├── switch.js │ └── switch.vue ├── tabs │ ├── index.js │ ├── tab-bar.js │ ├── tab-indicator.js │ ├── tab-scroller.js │ ├── tab.js │ └── tab.vue ├── textfield │ ├── character-counter.js │ ├── character-counter.vue │ ├── index.js │ ├── textfield-helper-text.js │ ├── textfield-helper-text.vue │ ├── textfield-icon.js │ ├── textfield-icon.vue │ ├── textfield.js │ └── textfield.vue ├── tooltip │ ├── index.js │ ├── tooltip.js │ └── tooltip.vue └── top-app-bar │ ├── index.js │ └── top-app-bar.js ├── static ├── .gitkeep ├── icon-128x128.png ├── icon-144x144.png ├── icon-152x152.png ├── icon-192x192.png ├── icon-384x384.png ├── icon-512x512.png ├── icon-72x72.png ├── icon-96x96.png └── logo.svg ├── test ├── button.spec.js ├── card.spec.js ├── checkbox.spec.js ├── chips.spec.js ├── circular-progress.spec.js ├── dialog.spec.js ├── drawer.spec1.js ├── fab.spec.js ├── floating-label.spec.js ├── icon-button.spec.js ├── layout-grid.spec.js ├── line-ripple.spec.js ├── linear-progress.spec.js ├── list.spec1.js ├── material-icon.spec.js ├── menu.spec1.js ├── notched-outline.spec.js ├── radio.spec.js ├── select.spec1.js ├── slider.spec.js ├── snackbar.spec.js ├── switch.spec.js ├── tabs.spec1.js ├── textfield.spec1.js ├── top-app-bar.spec.js ├── typography.spec1.js └── unit-test │ ├── index.js │ └── polyfills.js ├── types └── index.d.ts └── vite.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.css] 15 | indent_style = space 16 | indent_size = 4 17 | 18 | [*.scss] 19 | indent_style = space 20 | indent_size = 4 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ** PLEASE READ THIS BEFORE FILING AN ISSUE ** 2 | 3 | vue-material-adapter is still under active development. You can see our current progress on [master](https://github.com/pgbross/vue-material-adapter/tree/master). 4 | 5 | ## Bugs 6 | 7 | Follow the template below to ensure the quickest and most accurate response to your issue. 8 | 9 | ### What vue-material-adapter Version are you using? 10 | 11 | > Please be specific, e.g. _major.minor.patch_ 12 | 13 | ### What browser(s) is this bug affecting? 14 | 15 | > Please include the browser version. A user-agent string is also quite helpful. 16 | 17 | ### What OS are you using? 18 | 19 | > Please include the OS version. 20 | 21 | ### What are the steps to reproduce the bug? 22 | 23 | > Please write the steps which need to be taken in order to reproduce the bug. These steps should be 24 | > as detailed as possible, e.g. 25 | > 26 | > 1. Run the demo server 27 | > 2. localhost:8080/#/component/textfield 28 | > 3. Tab-focus on the first text field 29 | > 4. Observe the component's behavior 30 | > 31 | > We encourage you to use [CodePen](http://codepen.io/) to create a reproduction of 32 | > the issue. The less time it takes for us to repro the issue, the less time it takes to verify and 33 | > fix it! 34 | 35 | ### What is the expected behavior? 36 | 37 | > Please describe what the component/code should be doing that it's not. 38 | 39 | ### What is the actual behavior? 40 | 41 | > Please describe what the component/code is actually doing that's different from what it should be 42 | > doing. 43 | 44 | ### Any other information you believe would be useful? 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dev/ 3 | build 4 | node_modules*/ 5 | .wireit/ 6 | public/ 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | test/unit/coverage 11 | packages/*/dist 12 | demo/dist/ 13 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD002": false, 3 | "MD013": false, 4 | "MD041": false 5 | } 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | message="v%s" 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '13' 4 | cache: 5 | directories: 6 | - node_modules 7 | script: npm run ci 8 | branches: 9 | only: 10 | - master 11 | -------------------------------------------------------------------------------- /.vcmrc: -------------------------------------------------------------------------------- 1 | { 2 | "types": ["feat", "fix", "docs", "style", "refactor", "perf", "test", "build", "ci", "chore", "revert"], 3 | "scope": { 4 | "required": false, 5 | "allowed": [ 6 | "button", 7 | "card", 8 | "checkbox", 9 | "dialog", 10 | "drawer", 11 | "fab", 12 | "form-field", 13 | "grid-list", 14 | "icon-toggle", 15 | "layout-grid", 16 | "linear-progress", 17 | "list", 18 | "menu", 19 | "radio", 20 | "select", 21 | "slider", 22 | "snackbar", 23 | "switch", 24 | "tabs", 25 | "textfield", 26 | "theme", 27 | "toolbar", 28 | "typography", 29 | "utils", 30 | "demo", 31 | "infra", 32 | "mcw", 33 | "package" 34 | ], 35 | "validate": false, 36 | "multiple": false 37 | }, 38 | "warnOnFail": true, 39 | "maxSubjectLength": 100, 40 | "subjectPattern": ".+", 41 | "subjectPatternErrorMsg": "subject does not match subject pattern!", 42 | "helpMessage": "%s\nNOTE: Please see angular's commit message guidelines (https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit) for information on how to format commit messages.", 43 | "autoFix": false 44 | } 45 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Jest test", 11 | "runtimeExecutable": "npm", 12 | "runtimeArgs": ["run-script", "test"], 13 | "port": 9229, 14 | "skipFiles": ["/**"] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.organizeImports": false 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.wireit/6275696c64/fingerprint: -------------------------------------------------------------------------------- 1 | {"fullyTracked":false,"platform":"linux","arch":"x64","nodeVersion":"v20.3.1","command":"cross-env BROWSERSLIST_ENV=defaults node --experimental-json-modules scripts/build.mjs","extraArgs":[],"clean":true,"files":{},"output":["dist/**","!.git/","!.hg/","!.svn/","!.wireit/","!.yarn/","!CVS/","!node_modules/"],"dependencies":{},"env":{}} -------------------------------------------------------------------------------- /.wireit/6275696c64/lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/.wireit/6275696c64/lock -------------------------------------------------------------------------------- /.wireit/6275696c64/manifest: -------------------------------------------------------------------------------- 1 | {"/ddata/extra/vue-material-adapter/dist/vue-material-adapter.amd.min.js":{"t":"f","m":1692372632496.3,"s":136198},"/ddata/extra/vue-material-adapter/dist/vue-material-adapter.cjs.min.js":{"t":"f","m":1692372632159.2996,"s":136867},"/ddata/extra/vue-material-adapter/dist/vue-material-adapter.esm.js":{"t":"f","m":1692372632857.3003,"s":261496}} -------------------------------------------------------------------------------- /.wireit/6275696c643a64656d6f/fingerprint: -------------------------------------------------------------------------------- 1 | {"fullyTracked":false,"platform":"linux","arch":"x64","nodeVersion":"v20.3.1","command":"vite build --base=/vue-material-adapter/","extraArgs":[],"clean":true,"files":{},"output":["demo/dist/**","!.git/","!.hg/","!.svn/","!.wireit/","!.yarn/","!CVS/","!node_modules/"],"dependencies":{},"env":{}} -------------------------------------------------------------------------------- /.wireit/6275696c643a64656d6f/lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/.wireit/6275696c643a64656d6f/lock -------------------------------------------------------------------------------- /.wireit/646576/lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/.wireit/646576/lock -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Philip Ross 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Material Components for Vue 2 | 3 | [![Build Status](https://travis-ci.com/pgbross/vue-material-adapter.svg?branch=master)](https://travis-ci.com/pgbross/vue-material-adapter) 4 | 5 | Vue components (Vue 3.0) for [Material Web Components](https://material.io/components/web/) which uses the 6 | [`Using Foundations and Adapters`](https://github.com/material-components/material-components-web/blob/master/docs/integrating-into-frameworks.md#the-advanced-approach-using-foundations-and-adapters) integration technique. 7 | 8 | This project defines Vue components that use the `Material Components Web` library to implement Material Design.mization while sticking to the _Vue Spirit_ (approachable, versatile, and performant) 9 | 10 | > _This is the Vue 3 version, for the Vue 2 version install `vue-material-adapter-legacy` see [Legacy Vue 2](https://pgbross.github.io/vue-material-adapter/tree/legacy) (semantic version references to the previous version eg. `vue-material-adapter@^0.17.3` will continue to work normally with Vue 2)._ 11 | 12 | ## Install 13 | 14 | ``` 15 | $ npm install vue-material-adapter 16 | ``` 17 | 18 | ## Documentation & Demo 19 | 20 | See [Documentation & Demo](https://pgbross.github.io/vue-material-adapter) for working examples of how to structure the markup for Vue. 21 | 22 | ## Examples 23 | 24 | There are examples of using `vue-material-adapter` both as a `basic-vue-cli` and a `basic-webpack` project [examples](https://github.com/pgbross/vue-material-adapter/tree/main/examples). 25 | 26 | ## Release Notes 27 | 28 | See [Release Notes](https://github.com/pgbross/vue-material-adapter/tree/main/release-notes.md) 29 | 30 | ### Building and running demo 31 | 32 | Install dependencies 33 | 34 | ``` 35 | npm install vue-material-adapter 36 | ``` 37 | 38 | to build everything 39 | 40 | ``` 41 | npm run build 42 | npm run build:demo 43 | ``` 44 | 45 | to run demo 46 | 47 | ``` 48 | npm run dev 49 | ``` 50 | 51 | ### Acknowledgements 52 | 53 | This project is based on a fork of the successful [vue-mdc-adapter](https://stasson.github.io/vue-mdc-adapter) project. 54 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: [ 3 | '@babel/plugin-syntax-dynamic-import', 4 | // '@babel/plugin-proposal-partial-application', 5 | '@babel/plugin-proposal-optional-chaining', 6 | '@babel/plugin-proposal-nullish-coalescing-operator', 7 | '@babel/plugin-proposal-class-properties', 8 | // ['@babel/plugin-proposal-pipeline-operator', { proposal: 'smart' }], 9 | // [ 10 | // '@babel/plugin-transform-runtime', 11 | // { 12 | // regenerator: true, 13 | // }, 14 | // ], 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /demo/assets/card-16-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/demo/assets/card-16-9.jpg -------------------------------------------------------------------------------- /demo/assets/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/github-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/assets/github-mark-32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/demo/assets/github-mark-32px.png -------------------------------------------------------------------------------- /demo/assets/ic_button_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_card_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_component_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/assets/ic_component_24px_white.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/assets/ic_dialog_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_list_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_menu_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_progress_activity.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_radio_button_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_responsive_layout_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_ripple_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_selection_control_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_shadow_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /demo/assets/ic_side_navigation_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_slider_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_switch_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_tabs_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_text_field_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_theme_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_toast_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_toolbar_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/ic_typography_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /demo/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/mdc-logo.svg: -------------------------------------------------------------------------------- 1 | Slice 1 -------------------------------------------------------------------------------- /demo/assets/tile-1-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/demo/assets/tile-1-1.jpg -------------------------------------------------------------------------------- /demo/assets/vue-awesome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/demo/assets/vue-awesome.png -------------------------------------------------------------------------------- /demo/assets/vue-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/demo/assets/vue-banner.png -------------------------------------------------------------------------------- /demo/assets/vue-logo.-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/demo/assets/vue-logo.-dark.png -------------------------------------------------------------------------------- /demo/assets/vue-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/demo/assets/vue-logo.png -------------------------------------------------------------------------------- /demo/components/banner/banner.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ 6 | showBanner: false, 7 | reason: '', 8 | action: '', 9 | }); 10 | 11 | const onClosing = ({ reason }) => (uiState.reason = reason); 12 | const onAction = ({ action }) => (uiState.action = action); 13 | return { ...toRefs(uiState), onClosing, onAction }; 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /demo/components/banner/demo.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | 32 | -------------------------------------------------------------------------------- /demo/components/button/button.js: -------------------------------------------------------------------------------- 1 | import { computed, onMounted, reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ 6 | type: '', 7 | disabled: false, 8 | icon: 'favorite', 9 | svg: false, 10 | }); 11 | 12 | const buttonProps = computed(() => ({ 13 | disabled: uiState.disabled, 14 | raised: uiState.type === 'raised', 15 | outlined: uiState.type === 'outlined', 16 | unelevated: uiState.type === 'unelevated', 17 | })); 18 | 19 | onMounted(() => { 20 | setTimeout(() => { 21 | uiState.icon = 'flight_takeoff'; 22 | }, 2000); 23 | 24 | setTimeout(() => { 25 | uiState.svg = true; 26 | }, 1000); 27 | }); 28 | 29 | return { ...toRefs(uiState), buttonProps, onClick }; 30 | }, 31 | }; 32 | 33 | const onClick = () => console.log('on-click'); 34 | -------------------------------------------------------------------------------- /demo/components/button/next.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/components/card/card.js: -------------------------------------------------------------------------------- 1 | import card from '/assets/card-16-9.jpg'; 2 | 3 | const onPrimaryAction = () => { 4 | console.log('on primary action'); 5 | }; 6 | 7 | export default { 8 | setup() { 9 | return { card, onPrimaryAction }; 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /demo/components/card/demo.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 | 37 | -------------------------------------------------------------------------------- /demo/components/checkbox/checkbox.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ 6 | checked: false, 7 | indeterminate: false, 8 | disabled: false, 9 | checkedSolo: false, 10 | ids: [], 11 | }); 12 | 13 | const onIndeterminate = () => { 14 | uiState.indeterminate = true; 15 | }; 16 | 17 | const onIds = nv => { 18 | console.log(nv); 19 | uiState.ids = nv; 20 | }; 21 | 22 | return { ...toRefs(uiState), onIds, onIndeterminate }; 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /demo/components/checkbox/demo.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 52 | 53 | 77 | -------------------------------------------------------------------------------- /demo/components/circular-progress/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | The [MDC Circular Progress](https://github.com/material-components/material-components-web/tree/master/packages/mcw-circular-progress) component. 4 | 5 | ```html 6 | 7 | 8 | 9 | ``` 10 | 11 | ```javascript 12 | var vm = new Vue({ 13 | data(){ 14 | return {progressValue: 0.8} 15 | } 16 | } 17 | ``` 18 | 19 | ### props 20 | 21 | | prop | Type | Default | Description | 22 | | --------------- | ------- | --------- | ---------------------------------------------------------- | 23 | | `open` | Boolean | true | When changed to `false`, closes the component | 24 | | `indeterminate` | Boolean | false | When set to `true`, renders the indeterminate variant | 25 | | `medium` | Boolean | false | When set to `true`, renders the medium variant | 26 | | `progress` | Number | undefined | Decimal value between 0 and 1, sets the progress bar width | 27 | | `tag` | String | div | The html root element type | 28 | | `label` | String | | Optional aria-label | 29 | | `fourColor` | Boolean | false | Use four color rendering | 30 | 31 | - Determinate 32 | 33 | ```html 34 | 35 | ``` 36 | 37 | - Indeterminate 38 | 39 | ```html 40 | 41 | ``` 42 | 43 | - Medium 44 | 45 | ```html 46 | 47 | ``` 48 | 49 | ### Reference 50 | 51 | - 52 | -------------------------------------------------------------------------------- /demo/components/circular-progress/circular-progress.js: -------------------------------------------------------------------------------- 1 | export default { 2 | setup() { 3 | return { progressValue: 0.75 }; 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /demo/components/circular-progress/demo.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 25 | -------------------------------------------------------------------------------- /demo/components/dialog/dialog.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | const clean = value => { 4 | return value.replace(/\s/g, '-'); 5 | }; 6 | 7 | export default { 8 | setup() { 9 | const uiState = reactive({ 10 | openBasic: false, 11 | openSimple: false, 12 | openAlert: false, 13 | openConfirmation: false, 14 | openScrolling: false, 15 | openValidate: false, 16 | valid: false, 17 | action: '', 18 | selectedIndex: -1, 19 | choices: ['Never gonna give you up', 'Host cross buns', 'None'], 20 | picked: '', 21 | hasBeenOpened: false, 22 | checkboxSelected: [false, false, true], 23 | }); 24 | 25 | const onOpen = action => { 26 | uiState[`open${action}`] = !uiState[`open${action}`]; 27 | }; 28 | const onClosed = ({ action }) => { 29 | uiState.hasBeenOpened = true; 30 | uiState.action = 31 | action === 'dismiss' 32 | ? 'Declined... Maybe next time?' 33 | : ('Accepted, thanks!', console.log(action)); 34 | 35 | console.log(uiState.checkboxSelected); 36 | }; 37 | const checkValidationAndClose = () => { 38 | if (uiState.valid) { 39 | uiState.valid = false; 40 | uiState.openValidate = false; 41 | uiState.action = 'Task complete, submitting...'; 42 | } else { 43 | uiState.action = 'Please complete the task before submitting!'; 44 | } 45 | }; 46 | 47 | return { 48 | ...toRefs(uiState), 49 | onClosed, 50 | onOpen, 51 | checkValidationAndClose, 52 | clean, 53 | }; 54 | }, 55 | }; 56 | -------------------------------------------------------------------------------- /demo/components/drawer/demo.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/components/drawer/drawer.js: -------------------------------------------------------------------------------- 1 | export default { 2 | setup() { 3 | return {}; 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /demo/components/fab/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```html 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ``` 12 | 13 | > refer to [Material Design Icon Font](https://material.io/icons/) for the list of icons 14 | 15 | > add an `href` attribute for link 16 | 17 | ### events 18 | 19 | | event | args | Description | 20 | | -------- | ---- | ---------------- | 21 | | `@click` | | emitted on click | 22 | 23 | ### props 24 | 25 | | prop | Type | Default | Description | 26 | | -------- | ------- | ------- | ------------------------------------- | 27 | | `icon` | String | | material-icon content (\*) | 28 | | `mini` | Boolean | | mini style (40x40 pixels) (\*) | 29 | | `label` | String | | If label, is extended FAB | 30 | | `exited` | Boolean | | If true animates the FAB out of view. | 31 | 32 | > Supports Vue-Router props 33 | 34 | ### Custom Icons 35 | 36 | **Font Awsome** 37 | 38 | ```html 39 | 40 | 41 | 42 | ``` 43 | 44 | **SVG Icons** 45 | 46 | ```html 47 | 48 | 49 | 50 | ``` 51 | 52 | ### Link FAB 53 | 54 | #### Simple Link 55 | 56 | Using the href attribute will render `` 57 | 58 | ```html 59 | Home 60 | ``` 61 | 62 | #### Router-link 63 | 64 | If the `to` property is defined, the item behaves as a 65 | [router-link](https://router.vuejs.org/en/api/router-link.html) 66 | 67 | ```html 68 | Home 69 | ``` 70 | 71 | ### Reference 72 | 73 | - 74 | -------------------------------------------------------------------------------- /demo/components/fab/demo.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 25 | -------------------------------------------------------------------------------- /demo/components/fab/fab.js: -------------------------------------------------------------------------------- 1 | const onClick = () => { 2 | console.log('fab onClick'); 3 | }; 4 | 5 | export default { 6 | setup() { 7 | return { onClick }; 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /demo/components/icon-button/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```html 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ``` 17 | 18 | ### Props 19 | 20 | | prop | Type | Default | Description | 21 | | ---------- | ------- | ------- | ----------------------------------- | 22 | | `isOn` | Boolean | false | Model value for icon-toggle variant | 23 | | `disabled` | Boolean | false | Disables button if true | 24 | 25 | > If there is an `href` attribute, element is rendered as anchor, else button. 26 | 27 | ### Events 28 | 29 | | event | args | Description | 30 | | ----------------------------- | --------------- | ----------- | 31 | | `@change` | | On click | 32 | | `@MDCIconButtonToggle:change` | {isOn: Boolean} | On change | 33 | 34 | ### Reference 35 | 36 | - 37 | -------------------------------------------------------------------------------- /demo/components/icon-button/demo.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /demo/components/icon-button/icon-button.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ isOn: false, disabled: false }); 6 | 7 | return { ...toRefs(uiState) }; 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /demo/components/layout-grid/demo.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 34 | -------------------------------------------------------------------------------- /demo/components/linear-progress/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | The [MDC Linear Progress](https://github.com/material-components/material-components-web/tree/master/packages/mcw-linear-progress) component. 4 | 5 | ```html 6 | 7 | 8 | 9 | ``` 10 | 11 | ```javascript 12 | var vm = new Vue({ 13 | data: { 14 | progressValue: 0.5, 15 | }, 16 | }); 17 | ``` 18 | 19 | ### props 20 | 21 | | prop | Type | Default | Description | 22 | | --------------- | ------- | --------- | ------------------------------------------------------------ | 23 | | `open` | Boolean | true | When changed to `false`, closes the component | 24 | | `indeterminate` | Boolean | false | If `true`, renders the indeterminate variant | 25 | | `progress` | Number | undefined | Decimal value between 0 and 1, sets the progress bar width | 26 | | `buffer` | Number | undefined | Decimal value between 0 and 1, sets the buffer bar width | 27 | | `bufferingDots` | Boolean | true | Whether to show the buffer dots in the un-progressed section | 28 | | `tag` | String | 'div' | The root element | 29 | 30 | - Determinate 31 | 32 | ```html 33 | 34 | ``` 35 | 36 | - Buffer 37 | 38 | ```html 39 | 40 | ``` 41 | 42 | - Indeterminate 43 | 44 | ```html 45 | 46 | ``` 47 | 48 | - Reversed 49 | 50 | ```html 51 | 52 | ``` 53 | 54 | ### Reference 55 | 56 | - 57 | -------------------------------------------------------------------------------- /demo/components/linear-progress/demo.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | -------------------------------------------------------------------------------- /demo/components/linear-progress/linear-progress.js: -------------------------------------------------------------------------------- 1 | import { onMounted, reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ isOpen: true }); 6 | 7 | onMounted(() => { 8 | setInterval(() => { 9 | uiState.isOpen = !uiState.isOpen; 10 | }, 5000); 11 | }); 12 | return { ...toRefs(uiState) }; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /demo/components/list/list.js: -------------------------------------------------------------------------------- 1 | import { onMounted, reactive, toRefs } from 'vue'; 2 | 3 | const onAction = ({ detail }) => { 4 | console.log(detail.index); 5 | }; 6 | 7 | export default { 8 | setup() { 9 | const uiState = reactive({ 10 | selected: 1, 11 | listSelected: [], 12 | radioSelected: 'cat', 13 | checkboxSelected: [false, false], 14 | picked: '1', 15 | test: true, 16 | show2a: false, 17 | }); 18 | 19 | onMounted(() => { 20 | setTimeout(() => { 21 | uiState.show2a = true; 22 | }, 5000); 23 | }); 24 | 25 | return { ...toRefs(uiState), onAction }; 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /demo/components/material-icon/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```html 4 | 5 |
6 | 7 | ``` 8 | 9 | ### props 10 | 11 | | props | Type | Default | Description | 12 | | ------ | ------ | ------- | ---------------------------- | 13 | | `icon` | String | | Type of icon to be displayed | 14 | | `tag` | String | i | Custom element type | 15 | 16 | ### Reference - 17 | 18 | [Material Icons Documentation](https://google.github.io/material-design-icons/#icon-font-for-the-web) 19 | -------------------------------------------------------------------------------- /demo/components/material-icon/demo.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /demo/components/menu/demo.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 31 | 32 | 40 | -------------------------------------------------------------------------------- /demo/components/menu/menu.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | const onSelect = data => { 4 | console.log(data); 5 | }; 6 | export default { 7 | setup() { 8 | const uiState = reactive({ 9 | open: false, 10 | openGroup: false, 11 | }); 12 | 13 | return { ...toRefs(uiState), onSelect }; 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /demo/components/radio/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```html 4 | 12 | 19 | 20 |

Answer: {{ picked }}

21 | ``` 22 | 23 | ```javascript 24 | var vm = new Vue({ 25 | data: { 26 | picked: dog, 27 | }, 28 | }); 29 | ``` 30 | 31 | > NOTE: If a label property is specified, the `.mcw-radio` element and the associated label are wrapped around an `.mcw-form-field` element for styling. 32 | 33 | ### mcw-radio props 34 | 35 | | Prop Name | Type | Description | 36 | | -------------- | ------- | ------------------------------------------------------------------ | 37 | | `id` | String | (required) The html id of the input element | 38 | | `name` | String | (required) The name of the html input element | 39 | | `checked` | Boolean | Default `false`. When true will switch radio to the checked state. | 40 | | `value` | String | The associated value with the radio element. | 41 | | `picked` | String | Tracks the selected value (v-model) | 42 | | `disabled` | Boolean | Default `false`. When true will disable the radio element. | 43 | | `radioClasses` | String | Classes to be applied to the `.mcw-radio` element. | 44 | | `label` | String | Optional, Label associated with radio input control. | 45 | 46 | ### Reference 47 | 48 | - 49 | -------------------------------------------------------------------------------- /demo/components/radio/demo.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /demo/components/radio/radio.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ 6 | picked: 'dog', 7 | }); 8 | 9 | return { ...toRefs(uiState) }; 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /demo/components/segmented-button/demo.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 | 35 | -------------------------------------------------------------------------------- /demo/components/segmented-button/segmented-button.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | const onChange = (/* detail */) => { 4 | // console.dir(detail); 5 | }; 6 | const onFavourite = () => { 7 | console.log('onFavourite'); 8 | }; 9 | 10 | export default { 11 | setup() { 12 | const uiState = reactive({ singleIndex: undefined, multiIndex: [] }); 13 | 14 | return { ...toRefs(uiState), onChange, onFavourite }; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /demo/components/select/demo.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 51 | 52 | 57 | -------------------------------------------------------------------------------- /demo/components/select/select.js: -------------------------------------------------------------------------------- 1 | import { computed, reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ 6 | selectedType: 'Meat', 7 | selectedValue: undefined, 8 | food: { 9 | Vegetables: ['Spinach', 'Carrots', 'Onions', 'Broccoli'], 10 | Meat: ['Eggs', 'Chicken', 'Fish', 'Turkey', 'Pork', 'Beef'], 11 | Fruits: ['Apples', 'Oranges', 'Bananas', 'Berries', 'Lemons'], 12 | }, 13 | }); 14 | 15 | const types = computed(() => { 16 | return Object.keys(uiState.food); 17 | }); 18 | 19 | const options = computed(() => { 20 | return uiState.selectedType ? uiState.food[uiState.selectedType] : []; 21 | }); 22 | 23 | const onTypeChanged = nv => { 24 | console.log(nv); 25 | uiState.selectedType = nv; 26 | uiState.selectedValue = undefined; 27 | }; 28 | 29 | return { ...toRefs(uiState), types, options, onTypeChanged }; 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /demo/components/slider/demo.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 46 | 47 | 52 | -------------------------------------------------------------------------------- /demo/components/slider/slider.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ 6 | sliderValueStart: 2, 7 | sliderValueEnd: 7, 8 | sliderValue1: 5, 9 | sliderValue2: 4, 10 | }); 11 | 12 | return { ...toRefs(uiState) }; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /demo/components/snackbar/demo.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /demo/components/snackbar/snackbar.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs, watch } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ 6 | open: false, 7 | n: 0, 8 | action: '', 9 | reason: '', 10 | snack: {}, 11 | bar: undefined, 12 | }); 13 | 14 | const onReason = reason => { 15 | uiState.reason = reason == 'action' ? 'Took action' : 'Declined action'; 16 | }; 17 | 18 | const showSimple = () => { 19 | uiState.open = !uiState.open; 20 | uiState.open && uiState.n++; 21 | }; 22 | const showBaseline = () => { 23 | const nextSnack = { 24 | message: `Can't send photo. Retry in 5 seconds.`, 25 | timeoutMs: 5000, 26 | actionText: 'Retry', 27 | actionHandler: ({ reason }) => { 28 | alert(`Action, reason: ${reason}`); 29 | }, 30 | closeOnEscape: false, 31 | }; 32 | 33 | uiState.snack = nextSnack; 34 | // this.$refs.bar.handleSnack(nextSnack); 35 | }; 36 | const showStacked = () => { 37 | uiState.bar.handleSnack({ 38 | message: `This item already has the label "travel". You can add a new label.`, 39 | timeoutMs: 5000, 40 | actionText: 'Add a new label', 41 | stacked: true, 42 | }); 43 | }; 44 | 45 | const showLeading = () => { 46 | const nextSnack = { 47 | message: `Your photo has been archived.`, 48 | timeoutMs: 5000, 49 | actionText: 'Undo', 50 | leading: true, 51 | }; 52 | 53 | uiState.snack = nextSnack; 54 | }; 55 | 56 | watch( 57 | () => uiState.snack, 58 | nv => { 59 | console.log(nv); 60 | }, 61 | ); 62 | 63 | return { 64 | ...toRefs(uiState), 65 | showBaseline, 66 | showLeading, 67 | showSimple, 68 | showStacked, 69 | onReason, 70 | }; 71 | }, 72 | watchaa: { 73 | snack(nv) { 74 | console.dir(nv); 75 | }, 76 | }, 77 | methods: {}, 78 | }; 79 | -------------------------------------------------------------------------------- /demo/components/styles.scss: -------------------------------------------------------------------------------- 1 | @use "@material/theme" with ( 2 | $primary: rgb(190, 108, 53) 3 | 4 | ); 5 | 6 | @use "@material/banner/styles" as banner; 7 | @use "@material/button/mdc-button"; 8 | @use "@material/card/mdc-card"; 9 | @use "@material/circular-progress/mdc-circular-progress"; 10 | @use "@material/checkbox/mdc-checkbox"; 11 | @use "@material/chips/styles" as chips; 12 | @use "@material/data-table/mdc-data-table"; 13 | @use "@material/dialog/mdc-dialog"; 14 | @use "@material/drawer/mdc-drawer"; 15 | @use "@material/elevation/mdc-elevation"; 16 | @use "@material/fab/mdc-fab"; 17 | @use "@material/floating-label/mdc-floating-label"; 18 | @use "@material/form-field/mdc-form-field"; 19 | @use "@material/icon-button/mdc-icon-button"; 20 | @use "@material/image-list/mdc-image-list"; 21 | @use "@material/layout-grid/mdc-layout-grid"; 22 | @use "@material/line-ripple/mdc-line-ripple"; 23 | @use "@material/linear-progress/mdc-linear-progress"; 24 | @use "@material/menu/mdc-menu"; 25 | @use "@material/menu-surface/mdc-menu-surface"; 26 | @use "@material/notched-outline/mdc-notched-outline"; 27 | @use "@material/radio/mdc-radio"; 28 | @use "@material/ripple/mdc-ripple"; 29 | @use "@material/select/mdc-select"; 30 | @use "@material/segmented-button/styles" as segmentedButton; 31 | @use "@material/slider/styles" as slider; 32 | @use "@material/snackbar/mdc-snackbar"; 33 | @use "@material/switch/styles" as switch; 34 | @use "@material/tab/mdc-tab"; 35 | @use "@material/tab-bar/mdc-tab-bar"; 36 | @use "@material/tab-indicator/mdc-tab-indicator"; 37 | @use "@material/tab-scroller/mdc-tab-scroller"; 38 | @use "@material/textfield/mdc-text-field"; 39 | @use "@material/tooltip/styles" as tooltip; 40 | @use "@material/top-app-bar/mdc-top-app-bar"; 41 | @use "@material/typography/mdc-typography"; 42 | 43 | @use "@material/list/evolution-mixins"as list-evolution-mixins; 44 | @include list-evolution-mixins.core-styles(); 45 | -------------------------------------------------------------------------------- /demo/components/switch/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | The switch component is rendered as an input with checkbox type 4 | 5 | ```html 6 | 7 | {{label}} 8 | 9 | ``` 10 | 11 | ```javascript 12 | var vm = new Vue({ 13 | data: { 14 | label: 'This is a switch', 15 | checked: true, 16 | }, 17 | }); 18 | ``` 19 | 20 | ### Props 21 | 22 | | props | Type | Default | Description | 23 | | ----------- | ------- | ------- | ---------------------------------------------------- | 24 | | v-model | Boolean | | whether the checkbox is checked, bind with `v-model` | 25 | | `disabled` | Boolean | false | whether the checkbox is disabled | 26 | | `label` | String | | checkbox label | 27 | | `align-end` | Boolean | false | align the checkbox after the label | 28 | | `value` | String | | input control value | 29 | | `name` | String | | input name | 30 | 31 | ### Events 32 | 33 | | event | args | Description | 34 | | -------------------- | ---- | ------------------------ | 35 | | `@update:modelValue` | - | On checked status change | 36 | 37 | ### Reference 38 | 39 | - 40 | -------------------------------------------------------------------------------- /demo/components/switch/demo.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /demo/components/switch/switch.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ checked: false }); 6 | 7 | return { ...toRefs(uiState) }; 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /demo/components/tabs/tabs.js: -------------------------------------------------------------------------------- 1 | import { computed, onMounted, reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const items = [ 6 | 'item one', 7 | 'item two', 8 | 'item three', 9 | 'item four', 10 | 'item five', 11 | 'item six', 12 | 'item seven', 13 | ]; 14 | const uiState = reactive({ 15 | selectedItem: items[0], 16 | items, 17 | activeTabIndex: 1, 18 | firstExample: undefined, 19 | }); 20 | 21 | const filteredItems = computed(() => { 22 | return uiState.items.slice(0, 3); 23 | }); 24 | 25 | const onSelected = index => { 26 | uiState.selectedItem = uiState.items[index]; 27 | }; 28 | 29 | onMounted(() => { 30 | uiState.firstExample.activateTab(1); 31 | }); 32 | return { ...toRefs(uiState), filteredItems, onSelected }; 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /demo/components/textfield/textfield.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | 3 | const ondelete = () => { 4 | console.log('ondelete'); 5 | }; 6 | 7 | export default { 8 | setup() { 9 | const uiState = reactive({ 10 | textField: '', 11 | enabled: true, 12 | password: '', 13 | }); 14 | return { ...toRefs(uiState), ondelete }; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /demo/components/tooltip/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```html 4 |
5 | My tooltip 1 6 | Another tooltip 2 9 | This is Link . 10 |

11 | Here is 12 | My Button 13 |

14 |
15 | ``` 16 | 17 | ```javascript 18 | var vm = new Vue({ 19 | data: {}, 20 | methods: { 21 | onHidden() { 22 | console.dir('hidden'); 23 | }, 24 | }, 25 | }); 26 | ``` 27 | 28 | ### events 29 | 30 | | event | args | Description | 31 | | ----------------- | ---- | ------------------------------------------ | 32 | | mdctooltip:hidden | | Emitted when the tooltip stops being shown | 33 | 34 | ### props 35 | 36 | | prop | Type | Default | Description | 37 | | ------------ | ------------- | ------- | --------------------------- | 38 | | position | String/Object | | The position of the tooltop | 39 | | boundaryType | String/Number | | The anchor boundary type | 40 | 41 | `position`: This is either a comma separated string or an object {xPos: XPosition, yPos: YPosition} 42 | eg. "end,above" or {xPos:"center", yPos:"below"} or {xPos: 0, yPos: 1} 43 | 44 | XPosition { 45 | 'detected' = 0, 46 | 'start' = 1, 47 | 'center' = 2, 48 | 'end' = 3, 49 | } 50 | 51 | YPosition { 52 | 'detected' = 0, 53 | 'above' = 1, 54 | 'below' = 2, 55 | } 56 | 57 | `boundaryType`: This is either a string or a number. 58 | eg. "bounded" or 0 59 | 60 | AnchorBoundaryType { 61 | 'bounded' = 0, 62 | 'unbounded' = 1, 63 | } 64 | 65 | ### Theming and Reference 66 | 67 | - 68 | -------------------------------------------------------------------------------- /demo/components/tooltip/tooltip.js: -------------------------------------------------------------------------------- 1 | const addEventListenerHandlerFunction = (event, handler) => { 2 | const section = document.querySelector('.mdc-drawer-app-content'); 3 | 4 | section?.addEventListener(event, handler); 5 | }; 6 | 7 | const removeEventListenerHandlerFunction = (event, handler) => { 8 | const section = document.querySelector('.mdc-drawer-app-content'); 9 | 10 | section?.removeEventListener(event, handler); 11 | }; 12 | 13 | export default { 14 | setup() { 15 | return { 16 | addEventListenerHandlerFn: addEventListenerHandlerFunction, 17 | removeEventListenerHandlerFn: removeEventListenerHandlerFunction, 18 | }; 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /demo/components/top-app-bar/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```html 4 | 5 |
6 |
9 | 15 | vue material adapter 16 |
17 | 18 |
21 | 30 | 31 | 40 |
41 |
42 |
43 | ``` 44 | 45 | ```javascript 46 | var vm = new Vue({ 47 | methods: { 48 | showHelp() { 49 | console.log('show help'); 50 | }, 51 | 52 | goto(href) { 53 | window.open(href, '_blank'); 54 | }, 55 | }, 56 | }); 57 | ``` 58 | 59 | ### Reference 60 | 61 | - 62 | -------------------------------------------------------------------------------- /demo/components/top-app-bar/demo.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 58 | 59 | 73 | -------------------------------------------------------------------------------- /demo/components/top-app-bar/top-app-bar.js: -------------------------------------------------------------------------------- 1 | import { onMounted, reactive, toRefs } from 'vue'; 2 | 3 | export default { 4 | setup() { 5 | const uiState = reactive({ 6 | buttonText: '', 7 | navigation: false, 8 | navigationCount: 0, 9 | scrollTarget: undefined, 10 | root: undefined, 11 | }); 12 | 13 | const onNav = () => { 14 | uiState.navigation = true; 15 | uiState.navigationCount += 1; 16 | }; 17 | 18 | onMounted(() => { 19 | const demoElement = uiState.root.querySelector('.mcw-demo'); 20 | uiState.scrollTarget = demoElement; 21 | }); 22 | 23 | return { ...toRefs(uiState), onNav }; 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /demo/doc.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 34 | -------------------------------------------------------------------------------- /demo/home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 21 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-material-adapter 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /demo/index.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 68 | 86 | -------------------------------------------------------------------------------- /demo/main.js: -------------------------------------------------------------------------------- 1 | import { createApp, h } from 'vue'; 2 | import VueMaterialAdapter from 'vue-material-adapter'; 3 | import { createRouter, createWebHashHistory } from 'vue-router'; 4 | import index from './index.vue'; 5 | import routes from './routes.js'; 6 | import './styles/index.js'; 7 | 8 | // Vue.config.productionTip = true; 9 | const router = createRouter({ 10 | history: createWebHashHistory(), 11 | routes, 12 | }); 13 | 14 | // mount app 15 | const app = createApp({ 16 | render: () => h(index), 17 | }); 18 | 19 | app.use(router); 20 | app.use(VueMaterialAdapter); 21 | 22 | app.mount('#app'); 23 | -------------------------------------------------------------------------------- /demo/routes.js: -------------------------------------------------------------------------------- 1 | import Component from './component.vue'; 2 | import Documentation from './doc.vue'; 3 | import Home from './home.vue'; 4 | 5 | export default [ 6 | { path: '/component/:id', component: Component, name: 'component' }, 7 | { path: '/docs/:id', component: Documentation, name: 'docs' }, 8 | { path: '/', component: Home, name: 'home' }, 9 | { path: '/:pathMatch(.*)*', redirect: '/' }, 10 | ]; 11 | -------------------------------------------------------------------------------- /demo/styles/_constant.scss: -------------------------------------------------------------------------------- 1 | $black-02: rgba(0, 0, 0, 0.02); 2 | $black-05: rgba(0, 0, 0, 0.05); 3 | $black-10: rgba(0, 0, 0, 0.1); 4 | $black-20: rgba(0, 0, 0, 0.2); 5 | $black-54: rgba(0, 0, 0, 0.54); 6 | $black-87: rgba(0, 0, 0, 0.87); 7 | $black: #000; 8 | $brown-200: #bcaaa4; 9 | $brown-400: #8d6e63; 10 | $green-a700: #00c853; 11 | $grey-50: #fafafa; 12 | $grey-100: #f5f5f5; 13 | $grey-200: #eee; 14 | $grey-300: #e0e0e0; 15 | $grey-400: #bdbdbd; 16 | $grey-600: #757575; 17 | $grey-700: #616161; 18 | $grey-800: #424242; 19 | $grey-900: #212121; 20 | $indigo-500: #3f51b5; 21 | $light-blue-500: #03a9f4; 22 | $pink-900: #880e4f; 23 | $teal-400: #26a69a; 24 | $white-50: rgba(255, 255, 255, 0.5); 25 | $white: #fff; 26 | -------------------------------------------------------------------------------- /demo/styles/index.js: -------------------------------------------------------------------------------- 1 | import 'highlight.js/styles/googlecode.css'; 2 | import './demo.scss'; 3 | -------------------------------------------------------------------------------- /demo/top-app-bar.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 63 | 64 | 74 | -------------------------------------------------------------------------------- /demo/utils/case.js: -------------------------------------------------------------------------------- 1 | export const capitalize = string => { 2 | return string.replace(/[._-]/g, ' ').replace(/\b(\w)/g, function (_, x) { 3 | return x.toUpperCase(); 4 | }); 5 | }; 6 | 7 | export const camelize = string => { 8 | return string.replace(/[._-](\w|$)/g, function (_, x) { 9 | return x.toUpperCase(); 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /demo/utils/index.js: -------------------------------------------------------------------------------- 1 | export * from './case.js'; 2 | -------------------------------------------------------------------------------- /docs/about.md: -------------------------------------------------------------------------------- 1 | [vue-material-adapter](https://github.com/pgbross/vue-material-adapter) is an integration of 2 | [Material Web Components](https://material.io/components/web/) 3 | and [Vue.js](https://vuejs.org) which uses the 4 | [`Using Foundations and Adapters`](https://github.com/material-components/material-components-web/blob/master/docs/integrating-into-frameworks.md#the-advanced-approach-using-foundations-and-adapters) integration technique. 5 | 6 | This project defines Vue components that use the `Material Components Web` library to implement Material Design. 7 | 8 | ## List is being updated 9 | 10 | `vue material adapter` versions >= 4.0.0 use the the new list version from `material components web` in Menu, Select and Navigation Drawer. If you want to follow what is still recommended in `material components web` and use the deprecated list version for those components, then you will need to use `vue-material-adapter` version <=3.2.3. 11 | 12 | > Please raise issues or pull-request suggestions on [GitHub](https://github.com/pgbross/vue-material-adapter/issues) 13 | 14 | > NOTE: Material Components Web tends to release breaking changes on a monthly basis, but follows 15 | > [semver](https://semver.org/) so you can control when you incorporate them. 16 | > Similarly, `vue-material-adapter` will therefore often also have some breaking changes, but many changes to HTML layout 17 | > or other API changes only affect the internal implementation so there will be less work in applications to adopt a new version. 18 | -------------------------------------------------------------------------------- /docs/theming.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/docs/theming.md -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ### Vue-material-adapter Examples 2 | 3 | See individual folders for examples: 4 | 5 | - `basic-vue-cli` A basic example using vue-cli to generate a project and get started with vue-material-adapter 6 | - `basic-webpack` A basic example using webpack and vue-material-adapter to illustrate development and production environments. 7 | - `pwa-express` A fully featured progressive web application with express server. 8 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | root: true, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | '@vue/eslint-config-prettier/skip-formatting' 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 'latest' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "trailingComma": "none" 8 | } -------------------------------------------------------------------------------- /examples/basic-vue-ts/README.md: -------------------------------------------------------------------------------- 1 | # basic-vue-ts 2 | 3 | This is an example of using `vue-material-adapter` with a typescript project generated with `create-vue`. 4 | 5 | ## Quick start 6 | 7 | ```bash 8 | npm install 9 | npm run dev 10 | ``` 11 | 12 | ## Project setup 13 | 14 | ``` 15 | npm install 16 | ``` 17 | 18 | ### Compiles and hot-reloads for development 19 | 20 | ``` 21 | npm run dev 22 | ``` 23 | 24 | ### Compiles and minifies for production 25 | 26 | ``` 27 | npm run build 28 | ``` 29 | 30 | ### Customize configuration 31 | 32 | See [Configuration Reference](https://github.com/vuejs/create-vue). 33 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic-vue-ts", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "run-p type-check build-only", 8 | "preview": "vite preview", 9 | "build-only": "vite build", 10 | "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false", 11 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 12 | "format": "prettier --write src/" 13 | }, 14 | "dependencies": { 15 | "vue": "^3.3.4" 16 | }, 17 | "devDependencies": { 18 | "@rushstack/eslint-patch": "^1.3.2", 19 | "@tsconfig/node18": "^18.2.0", 20 | "@types/node": "^18.17.0", 21 | "@vitejs/plugin-vue": "^4.2.3", 22 | "@vue/eslint-config-prettier": "^8.0.0", 23 | "@vue/eslint-config-typescript": "^11.0.3", 24 | "@vue/tsconfig": "^0.4.0", 25 | "eslint": "^8.45.0", 26 | "eslint-plugin-vue": "^9.15.1", 27 | "npm-run-all": "^4.1.5", 28 | "prettier": "^3.0.0", 29 | "sass": "^1.66.0", 30 | "typescript": "~5.1.6", 31 | "vite": "^4.4.6", 32 | "vue-material-adapter": "^6.2.0", 33 | "vue-tsc": "^1.8.6" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/src/App.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 51 | 52 | 55 | 56 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp, h } from 'vue'; 2 | import { button, dialog, snackbar } from 'vue-material-adapter'; 3 | import App from './App.vue'; 4 | 5 | 6 | const app = createApp({ 7 | render: () => h(App), 8 | }); 9 | 10 | app.use(button); 11 | app.use(snackbar); 12 | app.use(dialog); 13 | 14 | app.mount('#app'); -------------------------------------------------------------------------------- /examples/basic-vue-ts/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/src/styles/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'theme'; 2 | @forward 'shape'; 3 | @forward 'button'; 4 | @forward 'snackbar'; 5 | @forward '@material/dialog/mdc-dialog'; 6 | @import url("https://fonts.googleapis.com/icon?family=Material+Icons"); 7 | @import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500"); 8 | @import url("https://fonts.googleapis.com/css?family=Roboto+Mono:300,400,500"); 9 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/src/styles/button.scss: -------------------------------------------------------------------------------- 1 | @use '@material/button/styles'; 2 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/src/styles/shape.scss: -------------------------------------------------------------------------------- 1 | @use '@material/shape' with ( 2 | $small-component-radius: 8px, 3 | $medium-component-radius: 16px, 4 | ); 5 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/src/styles/snackbar.scss: -------------------------------------------------------------------------------- 1 | @use '@material/snackbar'; 2 | @use '@material/icon-button/styles'; 3 | 4 | @include snackbar.core-styles; 5 | @include snackbar.fill-color(#541154); 6 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/src/styles/theme.scss: -------------------------------------------------------------------------------- 1 | $dark-mode-primary: #67360b; 2 | $dark-mode-on-primary: white; 3 | 4 | @use '@material/theme' with ( 5 | $primary: #be6c35, 6 | $on-primary: white, 7 | ); 8 | 9 | :root { 10 | @media screen and (prefers-color-scheme: dark) { 11 | --mdc-theme-primary: #{$dark-mode-primary}; 12 | --mdc-theme-on-primary: #{$dark-mode-on-primary}; 13 | 14 | background: #291202; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "baseUrl": ".", 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Bundler", 14 | "types": ["node"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/basic-vue-ts/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [ 9 | vue(), 10 | ], 11 | resolve: { 12 | alias: { 13 | '@': fileURLToPath(new URL('./src', import.meta.url)) 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /examples/basic-vue/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | root: true, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-prettier/skip-formatting' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 'latest' 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/basic-vue/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /examples/basic-vue/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "trailingComma": "none" 8 | } -------------------------------------------------------------------------------- /examples/basic-vue/README.md: -------------------------------------------------------------------------------- 1 | # basic-vue 2 | 3 | This is an example of using `vue-material-adapter` with a project generated with `create-vue`. 4 | 5 | ## Quick start 6 | 7 | ```bash 8 | npm install 9 | npm run dev 10 | ``` 11 | 12 | ## Steps to reproduce this example 13 | 14 | To install `vue-material-adapter` to a fresh project, first add the library: 15 | 16 | ``` 17 | npm install --save vue-material-adapter 18 | ``` 19 | 20 | Next, import and use the library in `main.js` 21 | 22 | ```javascript 23 | import { button, dialog, snackbar } from 'vue-material-adapter'; 24 | ... 25 | 26 | app.use(button); 27 | app.use(snackbar); 28 | app.use(dialog); 29 | ``` 30 | 31 | Finally, import styles for the components you choose to use. This can be either global in an scss file, 32 | as this repo uses, or in the `style` section of a SFC. 33 | 34 | ```scss 35 | @use '@material/button/mdc-button'; 36 | ``` 37 | 38 | ## Project setup 39 | 40 | ``` 41 | npm install 42 | ``` 43 | 44 | ### Compiles and hot-reloads for development 45 | 46 | ``` 47 | npm run dev 48 | ``` 49 | 50 | ### Compiles and minifies for production 51 | 52 | ``` 53 | npm run build 54 | ``` 55 | 56 | ### Customize configuration 57 | 58 | See [Configuration Reference](https://github.com/vuejs/create-vue). 59 | -------------------------------------------------------------------------------- /examples/basic-vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Basic Vue 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/basic-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-project", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "format": "^0.2.2", 12 | "run": "^1.4.0", 13 | "vue": "^3.3.4", 14 | "vue-material-adapter": "^6.2.0" 15 | }, 16 | "devDependencies": { 17 | "@rushstack/eslint-patch": "^1.3.3", 18 | "@vitejs/plugin-vue": "^4.3.1", 19 | "@vue/eslint-config-prettier": "^8.0.0", 20 | "eslint": "^8.47.0", 21 | "eslint-plugin-vue": "^9.17.0", 22 | "prettier": "^3.0.2", 23 | "sass": "^1.66.0", 24 | "vite": "^4.4.9" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/basic-vue/src/app.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 45 | 46 | 49 | -------------------------------------------------------------------------------- /examples/basic-vue/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp, h } from 'vue'; 2 | import { button, dialog, snackbar } from 'vue-material-adapter'; 3 | import App from './app.vue'; 4 | 5 | const app = createApp({ 6 | render: () => h(App), 7 | }); 8 | 9 | app.use(button); 10 | app.use(snackbar); 11 | app.use(dialog); 12 | 13 | app.mount('#app'); 14 | -------------------------------------------------------------------------------- /examples/basic-vue/src/styles/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'theme'; 2 | @forward 'shape'; 3 | @forward 'button'; 4 | @forward 'snackbar'; 5 | @forward '@material/dialog/mdc-dialog'; 6 | @import url("https://fonts.googleapis.com/icon?family=Material+Icons"); 7 | @import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500"); 8 | @import url("https://fonts.googleapis.com/css?family=Roboto+Mono:300,400,500"); 9 | -------------------------------------------------------------------------------- /examples/basic-vue/src/styles/button.scss: -------------------------------------------------------------------------------- 1 | @use '@material/button/styles'; 2 | -------------------------------------------------------------------------------- /examples/basic-vue/src/styles/shape.scss: -------------------------------------------------------------------------------- 1 | @use '@material/shape' with ( 2 | $small-component-radius: 8px, 3 | $medium-component-radius: 16px, 4 | ); 5 | -------------------------------------------------------------------------------- /examples/basic-vue/src/styles/snackbar.scss: -------------------------------------------------------------------------------- 1 | @use '@material/snackbar'; 2 | @use '@material/icon-button/styles'; 3 | 4 | @include snackbar.core-styles; 5 | @include snackbar.fill-color(#542d11); 6 | -------------------------------------------------------------------------------- /examples/basic-vue/src/styles/theme.scss: -------------------------------------------------------------------------------- 1 | $dark-mode-primary: #67360b; 2 | $dark-mode-on-primary: white; 3 | 4 | @use '@material/theme' with ( 5 | $primary: #be6c35, 6 | $on-primary: white, 7 | ); 8 | 9 | :root { 10 | @media screen and (prefers-color-scheme: dark) { 11 | --mdc-theme-primary: #{$dark-mode-primary}; 12 | --mdc-theme-on-primary: #{$dark-mode-on-primary}; 13 | 14 | background: #291202; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/basic-vue/vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [ 9 | vue(), 10 | ], 11 | resolve: { 12 | alias: { 13 | '@': fileURLToPath(new URL('./src', import.meta.url)) 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /examples/pwa-express/README.md: -------------------------------------------------------------------------------- 1 | # pwa-express 2 | 3 | > Full example of a progressive web application with express server and full api. 4 | 5 | ### Comming Soon... 6 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable quote-props */ 2 | module.exports = { 3 | testPathIgnorePatterns: ['/.c9/', '/node_modules/', '/demo/'], 4 | moduleDirectories: ['node_modules'], 5 | moduleFileExtensions: ['js', 'json', 'vue'], 6 | modulePathIgnorePatterns: ['/packages'], 7 | transform: { 8 | '.*\\.(vue)$': '/node_modules/vue-jest', 9 | '^.+\\.js$': '/node_modules/babel-jest', 10 | }, 11 | transformIgnorePatterns: ['/node_modules/(?!@material/)'], 12 | moduleNameMapper: { 13 | '^\\/([^\\/]+)': '/packages/$1/index', 14 | '^~/(.*)': '/packages/$1', 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["./src/**/*"] 3 | } 4 | -------------------------------------------------------------------------------- /mcw-docs/List.Evolution.Public.Documentation.PUBLIC.DRAFT.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/mcw-docs/List.Evolution.Public.Documentation.PUBLIC.DRAFT.pdf -------------------------------------------------------------------------------- /scripts/bundles.mjs: -------------------------------------------------------------------------------- 1 | export const bundleTypes = { 2 | ESM: 'ESM', 3 | }; 4 | 5 | export const bundles = {}; 6 | -------------------------------------------------------------------------------- /src/banner/banner-content.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /src/banner/banner.vue: -------------------------------------------------------------------------------- 1 | 2 | 48 | -------------------------------------------------------------------------------- /src/banner/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwBanner from './banner.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwBanner, 6 | }); 7 | export { default as mcwBanner } from './banner.vue'; 8 | -------------------------------------------------------------------------------- /src/base/base-plugin.js: -------------------------------------------------------------------------------- 1 | export function BasePlugin(components) { 2 | return { 3 | version: '__VERSION__', 4 | install: vm => { 5 | for (const [key, component] of Object.entries(components)) { 6 | const name = key.replace(/([\da-z])([A-Z])/g, '$1-$2').toLowerCase(); 7 | 8 | // eslint-disable-next-line no-unused-vars 9 | const [pfx, ...rest] = name.split('-'); 10 | 11 | const mdcName = ['mdc', ...rest].join('-'); 12 | const mcwName = ['mcw', ...rest].join('-'); 13 | 14 | const haveComponent = vm._context.components[mcwName]; 15 | if (!haveComponent) { 16 | vm.component(mcwName, component); 17 | vm.component(mdcName, component); 18 | } 19 | } 20 | }, 21 | components, 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/base/custom-event.js: -------------------------------------------------------------------------------- 1 | export function emitCustomEvent( 2 | element, 3 | eventType, 4 | eventData, 5 | shouldBubble = false, 6 | ) { 7 | if (element) { 8 | eventType = eventType.toLowerCase(); 9 | 10 | const event_ = 11 | typeof CustomEvent === 'function' 12 | ? new CustomEvent(eventType, { 13 | detail: eventData, 14 | bubbles: shouldBubble, 15 | }) 16 | : createCustomEvent(eventType, shouldBubble, eventData); 17 | 18 | element.dispatchEvent(event_); 19 | } 20 | } 21 | 22 | // === 23 | // Private functions 24 | // === 25 | 26 | const createCustomEvent = (eventType, shouldBubble, eventData) => { 27 | const event_ = document.createEvent('CustomEvent'); 28 | return event_.initCustomEvent(eventType, shouldBubble, false, eventData); 29 | }; 30 | -------------------------------------------------------------------------------- /src/base/custom-link.js: -------------------------------------------------------------------------------- 1 | import { h, resolveDynamicComponent } from 'vue'; 2 | 3 | export const CustomLink = { 4 | name: 'custom-link', 5 | props: { 6 | tag: String, 7 | to: [String, Object], 8 | }, 9 | setup(props, { slots, attrs }) { 10 | return () => { 11 | // destructure the props in the render function so we use the current value 12 | // if their value has changed since we were created 13 | const { to, href, tag } = props; 14 | 15 | const routerLink = resolveDynamicComponent('router-link'); 16 | if (to && routerLink) { 17 | const rtag = tag ?? 'a'; 18 | 19 | return h( 20 | routerLink, 21 | { 22 | custom: true, 23 | ...attrs, 24 | to, 25 | }, 26 | { 27 | default: props => 28 | h( 29 | rtag, 30 | { 31 | ...attrs, 32 | onClick: event_ => { 33 | event_.__itemId = attrs.itemId; 34 | props.navigate(event_); 35 | }, 36 | }, 37 | slots.default?.(), 38 | ), 39 | }, 40 | ); 41 | } 42 | 43 | const element = href ? 'a' : tag ?? 'a'; 44 | const role = href 45 | ? 'button' 46 | : element === 'button' 47 | ? undefined 48 | : 'button'; 49 | 50 | const children = slots.default?.(); 51 | return h(element, { ...attrs, role }, { default: () => children }); 52 | }; 53 | }, 54 | }; 55 | -------------------------------------------------------------------------------- /src/base/index.js: -------------------------------------------------------------------------------- 1 | export { BasePlugin } from './base-plugin.js'; 2 | export { emitCustomEvent } from './custom-event.js'; 3 | export { CustomLink } from './custom-link.js'; 4 | export { default as formFieldWrapper } from './form-field-wrapper.js'; 5 | export { default as touchWrapper } from './touch-wrapper.js'; 6 | -------------------------------------------------------------------------------- /src/base/touch-wrapper.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | inheritAttrs: false, 5 | props: { isTouch: Boolean }, 6 | setup(props, { slots }) { 7 | return () => { 8 | if (props.isTouch) { 9 | return h('div', { class: 'mdc-touch-target-wrapper' }, [ 10 | slots.default(), 11 | ]); 12 | } 13 | 14 | return slots.default(); 15 | }; 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /src/button/button.js: -------------------------------------------------------------------------------- 1 | import { computed, defineComponent, ref } from 'vue'; 2 | import { CustomLink } from '../base/index.js'; 3 | import { useRipplePlugin } from '../ripple/index.js'; 4 | 5 | export default defineComponent({ 6 | name: 'mcw-button', 7 | props: { 8 | raised: Boolean, 9 | unelevated: Boolean, 10 | outlined: Boolean, 11 | icon: String, 12 | trailingIcon: String, 13 | isTouch: Boolean, 14 | }, 15 | components: { CustomLink }, 16 | setup(props, { slots }) { 17 | const root = ref(); 18 | 19 | const { classes: rippleClasses, styles } = useRipplePlugin(root); 20 | 21 | const haveIcon = computed(() => { 22 | return slots.icon ?? props.icon; 23 | }); 24 | 25 | const haveTrailingIcon = computed(() => { 26 | return slots.trailingIcon ?? props.trailingIcon; 27 | }); 28 | 29 | const classes = computed(() => { 30 | return { 31 | ...rippleClasses.value, 32 | 'mdc-button': true, 33 | 'mdc-button--raised': props.raised, 34 | 'mdc-button--unelevated': props.unelevated && !props.raised, 35 | 'mdc-button--outlined': props.outlined, 36 | 'mdc-button--icon-leading': haveIcon.value, 37 | 'mdc-button--icon-trailing': haveTrailingIcon.value, 38 | }; 39 | }); 40 | 41 | return { 42 | styles, 43 | classes, 44 | root, 45 | haveIcon, 46 | haveTrailingIcon, 47 | }; 48 | }, 49 | }); 50 | -------------------------------------------------------------------------------- /src/button/button.vue: -------------------------------------------------------------------------------- 1 | 2 | 23 | -------------------------------------------------------------------------------- /src/button/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwButton from './button.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwButton, 6 | }); 7 | export { default as mcwButton } from './button.vue'; 8 | -------------------------------------------------------------------------------- /src/card/card-action-buttons.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-card-action-buttons', 5 | 6 | setup(props, { slots }) { 7 | return () => { 8 | return h( 9 | 'div', 10 | { 11 | class: ['mdc-card__action-buttons'], 12 | }, 13 | [slots.default?.()], 14 | ); 15 | }; 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /src/card/card-action-icons.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-card-action-icons', 5 | 6 | setup(props, { slots }) { 7 | return () => { 8 | return h( 9 | 'div', 10 | { 11 | class: ['mdc-card__action-icons'], 12 | }, 13 | [slots.default?.()], 14 | ); 15 | }; 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /src/card/card-actions.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-card-actions', 5 | props: { 6 | fullBleed: Boolean, 7 | }, 8 | setup(props, { slots }) { 9 | return () => { 10 | return h( 11 | 'section', 12 | { 13 | class: [ 14 | { 15 | 'mdc-card__actions': 1, 16 | 'mdc-card__actions--full-bleed': props.fullBleed, 17 | }, 18 | ], 19 | }, 20 | slots.default?.(), 21 | ); 22 | }; 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /src/card/card-media.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-card-media', 5 | props: { 6 | src: String, 7 | square: { 8 | type: Boolean, 9 | default() { 10 | return false; 11 | }, 12 | }, 13 | wide: { 14 | type: Boolean, 15 | default() { 16 | return false; 17 | }, 18 | }, 19 | contentClass: String, 20 | }, 21 | 22 | setup(props, { slots }) { 23 | return () => { 24 | const nodes = []; 25 | 26 | const content = slots.default?.(); 27 | if (content) { 28 | nodes.push( 29 | h( 30 | 'div', 31 | { class: ['mdc-card__media-content', props.contentClass] }, 32 | content, 33 | ), 34 | ); 35 | } 36 | 37 | return h( 38 | 'section', 39 | { 40 | class: { 41 | 'mdc-card__media': 1, 42 | 'mdc-card__media--square': props.square, 43 | 'mdc-card__media--16-9': props.wide && !props.square, 44 | }, 45 | style: { 46 | backgroundImage: `url(${props.src})`, 47 | }, 48 | }, 49 | nodes, 50 | ); 51 | }; 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /src/card/card-primary-action.js: -------------------------------------------------------------------------------- 1 | import { computed, h, ref } from 'vue'; 2 | import { CustomLink } from '../base/index.js'; 3 | import { useRipplePlugin } from '../ripple/index.js'; 4 | 5 | export default { 6 | name: 'mcw-card-primary-action', 7 | setup(props, { slots }) { 8 | const root = ref(); 9 | 10 | const { classes: rippleClasses, styles } = useRipplePlugin(root); 11 | const classes = computed(() => { 12 | return { ...rippleClasses.value, 'mdc-card__primary-action': 1 }; 13 | }); 14 | 15 | return () => { 16 | return h( 17 | CustomLink, 18 | { ref: root, class: classes.value, style: styles, tabindex: '0' }, 19 | () => [slots.default(), h('div', { class: 'mdc-card__ripple' })], 20 | ); 21 | }; 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /src/card/card.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-card', 5 | props: { 6 | outlined: Boolean, 7 | }, 8 | setup(props, { attrs, slots }) { 9 | return () => { 10 | const { outlined } = props; 11 | 12 | return h( 13 | 'div', 14 | { 15 | class: [ 16 | { 17 | 'mdc-card': 1, 18 | 'mdc-card--outlined': outlined, 19 | }, 20 | ], 21 | ...attrs, 22 | }, 23 | slots.default?.(), 24 | ); 25 | }; 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /src/card/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwCardActionButtons from './card-action-buttons.js'; 3 | import mcwCardActionIcons from './card-action-icons.js'; 4 | import mcwCardActions from './card-actions.js'; 5 | import mcwCardMedia from './card-media.js'; 6 | import mcwCardPrimaryAction from './card-primary-action.js'; 7 | import mcwCard from './card.js'; 8 | 9 | export { default as mcwCardActionButtons } from './card-action-buttons.js'; 10 | export { default as mcwCardActionIcons } from './card-action-icons.js'; 11 | export { default as mcwCardActions } from './card-actions.js'; 12 | export { default as mcwCardMedia } from './card-media.js'; 13 | export { default as mcwCardPrimaryAction } from './card-primary-action.js'; 14 | export { default as mcwCard } from './card.js'; 15 | 16 | export default BasePlugin({ 17 | mcwCard, 18 | mcwCardPrimaryAction, 19 | mcwCardMedia, 20 | mcwCardActions, 21 | mcwCardActionButtons, 22 | mcwCardActionIcons, 23 | }); 24 | -------------------------------------------------------------------------------- /src/checkbox/checkbox-content.js: -------------------------------------------------------------------------------- 1 | import { MDCFormFieldFoundation } from '@material/form-field/foundation.js'; 2 | import { computed, h, onBeforeUnmount, onMounted, ref } from 'vue'; 3 | 4 | export default { 5 | props: { 6 | activate: Function, 7 | deactivate: Function, 8 | alignEnd: Boolean, 9 | checkboxId: String, 10 | }, 11 | inheritAttrs: false, 12 | setup(props, { slots }) { 13 | const labelElement = ref(); 14 | 15 | let formField; 16 | 17 | const hasLabel = !!slots.label; 18 | 19 | const formFieldClasses = computed(() => { 20 | return { 21 | 'mdc-form-field': hasLabel, 22 | 'mdc-form-field--align-end': hasLabel && props.alignEnd, 23 | }; 24 | }); 25 | 26 | onMounted(() => { 27 | if (props.hasLabel) { 28 | formField = new MDCFormFieldFoundation({ 29 | registerInteractionHandler: (type, handler) => { 30 | labelElement.value.addEventListener(type, handler); 31 | }, 32 | deregisterInteractionHandler: (type, handler) => { 33 | labelElement.value.removeEventListener(type, handler); 34 | }, 35 | activateInputRipple: () => { 36 | props?.activate(); 37 | }, 38 | deactivateInputRipple: () => { 39 | props?.deactivate(); 40 | }, 41 | }); 42 | formField.init(); 43 | } 44 | }); 45 | 46 | onBeforeUnmount(() => { 47 | formField?.destroy(); 48 | }); 49 | 50 | return () => { 51 | if (hasLabel) { 52 | return h( 53 | 'div', 54 | { class: { 'mdc-checkbox-wrapper': 1, ...formFieldClasses.value } }, 55 | [ 56 | slots.default?.(), 57 | h( 58 | 'label', 59 | { 60 | for: props.checkboxId, 61 | ref: labelElement, 62 | }, 63 | [slots.label?.()], 64 | ), 65 | ], 66 | ); 67 | } 68 | 69 | return slots.default(); 70 | }; 71 | }, 72 | }; 73 | -------------------------------------------------------------------------------- /src/checkbox/checkbox.vue: -------------------------------------------------------------------------------- 1 | 2 | 37 | -------------------------------------------------------------------------------- /src/checkbox/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwCheckbox from './checkbox.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwCheckbox, 6 | }); 7 | export { default as mcwCheckbox } from './checkbox.vue'; 8 | -------------------------------------------------------------------------------- /src/chips/chip-checkmark.js: -------------------------------------------------------------------------------- 1 | import { onMounted, ref } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-chip-checkmark', 5 | 6 | setup() { 7 | const width = ref(0); 8 | const root = ref(); 9 | 10 | onMounted(() => (width.value = root.value.getBoundingClientRect().height)); 11 | return { width, root }; 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/chips/chip-checkmark.vue: -------------------------------------------------------------------------------- 1 | 2 | 14 | -------------------------------------------------------------------------------- /src/chips/chip-set.vue: -------------------------------------------------------------------------------- 1 | 2 | 18 | -------------------------------------------------------------------------------- /src/chips/chip.vue: -------------------------------------------------------------------------------- 1 | 2 | 34 | -------------------------------------------------------------------------------- /src/chips/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwChipAction from './chip-action.vue'; 3 | import mcwChipCheckmark from './chip-checkmark.vue'; 4 | import mcwChipSet from './chip-set.vue'; 5 | import mcwChip from './chip.vue'; 6 | 7 | export { default as mcwChipCheckmark } from './chip-checkmark.vue'; 8 | export { default as mcwChipSet } from './chip-set.vue'; 9 | export { default as mcwChip } from './chip.vue'; 10 | 11 | export default BasePlugin({ 12 | mcwChip, 13 | mcwChipSet, 14 | mcwChipCheckmark, 15 | mcwChipAction, 16 | }); 17 | -------------------------------------------------------------------------------- /src/chips/trailing-action.jsX: -------------------------------------------------------------------------------- 1 | import { MDCChipTrailingActionFoundation } from '@material/chips/trailingaction/foundation.js'; 2 | import { onBeforeUnmount, onMounted, ref } from 'vue'; 3 | import { emitCustomEvent } from '../base/index.js'; 4 | import { useRipplePlugin } from '../ripple/ripple-plugin.js'; 5 | 6 | const { strings } = MDCChipTrailingActionFoundation; 7 | 8 | export default { 9 | name: 'mcw-chip-trailing-action', 10 | setup() { 11 | const root = ref(); 12 | let foundation; 13 | 14 | const { classes, styles } = useRipplePlugin(root); 15 | 16 | const adapter = { 17 | focus: () => { 18 | root.value.focus(); 19 | }, 20 | getAttribute: attribute => root.value.getAttribute(attribute), 21 | notifyInteraction: trigger => 22 | emitCustomEvent( 23 | root.value, 24 | strings.INTERACTION_EVENT, 25 | { 26 | trigger, 27 | }, 28 | true, 29 | ), 30 | notifyNavigation: key => 31 | emitCustomEvent( 32 | root.value, 33 | strings.NAVIGATION_EVENT, 34 | { 35 | key, 36 | }, 37 | true, 38 | ), 39 | setAttribute: (attribute, value) => { 40 | root.value.setAttribute(attribute, value); 41 | }, 42 | }; 43 | 44 | const onClick = event_ => foundation.handleClick(event_); 45 | const onKeydown = event_ => foundation.handleKeydown(event_); 46 | const isNavigable = () => foundation.isNavigable(); 47 | const focus = () => foundation.focus(); 48 | const removeFocus = () => foundation.removeFocus(); 49 | 50 | onMounted(() => { 51 | foundation = new MDCChipTrailingActionFoundation(adapter); 52 | foundation.init(); 53 | }); 54 | 55 | onBeforeUnmount(() => { 56 | foundation.destroy(); 57 | }); 58 | return { 59 | root, 60 | styles, 61 | classes, 62 | onClick, 63 | onKeydown, 64 | isNavigable, 65 | focus, 66 | removeFocus, 67 | }; 68 | }, 69 | }; 70 | -------------------------------------------------------------------------------- /src/chips/trailing-action.vueX: -------------------------------------------------------------------------------- 1 | 2 | 19 | -------------------------------------------------------------------------------- /src/circular-progress/circular-progress.vue: -------------------------------------------------------------------------------- 1 | 2 | 50 | -------------------------------------------------------------------------------- /src/circular-progress/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwCircularProgress from './circular-progress.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwCircularProgress, 6 | }); 7 | export { default as mcwCircularProgress } from './circular-progress.vue'; 8 | -------------------------------------------------------------------------------- /src/data-table/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwDataTable from './data-table.js'; 3 | 4 | export default BasePlugin({ 5 | mcwDataTable, 6 | }); 7 | export { default as mcwDataTable } from './data-table.js'; 8 | -------------------------------------------------------------------------------- /src/dialog/dialog-button.js: -------------------------------------------------------------------------------- 1 | import { h, resolveComponent } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-dialog-button', 5 | props: { 6 | action: String, 7 | isDefault: Boolean, 8 | isInitialFocus: Boolean, 9 | }, 10 | 11 | setup(props, { attrs, slots }) { 12 | return () => { 13 | return h( 14 | resolveComponent('mcw-button'), 15 | { 16 | ...attrs, 17 | class: ['mdc-button', 'mdc-dialog__button'], 18 | 'data-mdc-dialog-action': props.action, 19 | 'data-mdc-dialog-button-default': props.isDefault, 20 | 'data-mdc-dialog-initial-focus': props.isInitialFocus, 21 | }, 22 | { default: () => slots.default?.() }, 23 | ); 24 | }; 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/dialog/dialog-content.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-dialog-content', 5 | props: { 6 | tag: { 7 | type: String, 8 | default() { 9 | return 'div'; 10 | }, 11 | }, 12 | }, 13 | 14 | setup(props, { slots }) { 15 | return () => { 16 | return h( 17 | props.tag, 18 | { 19 | class: ['mdc-dialog__content'], 20 | }, 21 | slots.default?.(), 22 | ); 23 | }; 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /src/dialog/dialog-footer.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-dialog-footer', 5 | props: { 6 | tag: { 7 | type: String, 8 | default() { 9 | return 'div'; 10 | }, 11 | }, 12 | }, 13 | 14 | setup(props, { slots }) { 15 | return () => { 16 | return h( 17 | props.tag, 18 | { 19 | class: ['mdc-dialog__actions'], 20 | }, 21 | slots.default?.(), 22 | ); 23 | }; 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /src/dialog/dialog-title.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-dialog-title', 5 | props: { 6 | tag: { 7 | type: String, 8 | default() { 9 | return 'h2'; 10 | }, 11 | }, 12 | }, 13 | 14 | setup(props, { slots }) { 15 | return () => { 16 | return h( 17 | props.tag, 18 | { 19 | class: ['mdc-dialog__title'], 20 | }, 21 | slots.default?.(), 22 | ); 23 | }; 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /src/dialog/dialog.vue: -------------------------------------------------------------------------------- 1 | 2 | 25 | -------------------------------------------------------------------------------- /src/dialog/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwDialogButton from './dialog-button.js'; 3 | import mcwDialogContent from './dialog-content.js'; 4 | import mcwDialogFooter from './dialog-footer.js'; 5 | import mcwDialogTitle from './dialog-title.js'; 6 | import mcwDialog from './dialog.vue'; 7 | 8 | export { default as mcwDialogButton } from './dialog-button.js'; 9 | export { default as mcwDialogContent } from './dialog-content.js'; 10 | export { default as mcwDialogFooter } from './dialog-footer.js'; 11 | export { default as mcwDialogTitle } from './dialog-title.js'; 12 | export { default as mcwDialog } from './dialog.vue'; 13 | 14 | export default BasePlugin({ 15 | mcwDialog, 16 | mcwDialogTitle, 17 | mcwDialogFooter, 18 | mcwDialogButton, 19 | mcwDialogContent, 20 | }); 21 | -------------------------------------------------------------------------------- /src/drawer/drawer.vue: -------------------------------------------------------------------------------- 1 | 2 | 48 | -------------------------------------------------------------------------------- /src/drawer/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwDrawer from './drawer.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwDrawer, 6 | }); 7 | export { default as mcwDrawer } from './drawer.vue'; 8 | -------------------------------------------------------------------------------- /src/fab/fab.js: -------------------------------------------------------------------------------- 1 | import { computed, reactive, ref, toRefs, watch } from 'vue'; 2 | import { CustomLink, touchWrapper } from '../base/index.js'; 3 | import { useRipplePlugin } from '../ripple/ripple-plugin.js'; 4 | 5 | export default { 6 | name: 'mcw-fab', 7 | props: { 8 | icon: String, 9 | mini: Boolean, 10 | exited: Boolean, 11 | label: String, 12 | isTouch: Boolean, 13 | }, 14 | inheritAttrs: false, 15 | components: { CustomLink, touchWrapper }, 16 | setup(props, { slots }) { 17 | const root = ref(); 18 | const uiState = reactive({ 19 | classes: { 20 | 'mdc-fab': 1, 21 | 'mdc-fab--mini': props.mini, 22 | 'mdc-fab--extended': props.label || slots.default, 23 | 'mdc-fab--exited': props.exited, 24 | }, 25 | }); 26 | 27 | const { classes: rippleClasses, styles } = useRipplePlugin(root); 28 | const classes = computed(() => { 29 | return { ...rippleClasses.value, ...uiState.classes }; 30 | }); 31 | 32 | watch( 33 | () => props.icon, 34 | nv => { 35 | uiState.classes = { ...uiState.classes, 'material-icons': nv }; 36 | }, 37 | ); 38 | 39 | watch( 40 | () => props.mini, 41 | nv => { 42 | uiState.classes = { ...uiState.classes, 'mdc-fab--mini': nv }; 43 | }, 44 | ); 45 | 46 | watch( 47 | () => props.exited, 48 | nv => { 49 | uiState.classes = { ...uiState.classes, 'mdc-fab--exited': nv }; 50 | }, 51 | ); 52 | 53 | return { ...toRefs(uiState), classes, root, styles }; 54 | }, 55 | }; 56 | -------------------------------------------------------------------------------- /src/fab/fab.vue: -------------------------------------------------------------------------------- 1 | 2 | 22 | -------------------------------------------------------------------------------- /src/fab/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwFAB from './fab.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwFAB, 6 | }); 7 | export { default as mcwFAB } from './fab.vue'; 8 | -------------------------------------------------------------------------------- /src/floating-label/floating-label.js: -------------------------------------------------------------------------------- 1 | import { MDCFloatingLabelFoundation } from '@material/floating-label/foundation.js'; 2 | import { h, onBeforeUnmount, onMounted, reactive, ref } from 'vue'; 3 | 4 | export default { 5 | name: 'mcw-floating-label', 6 | 7 | props: { required: { type: Boolean } }, 8 | setup(props, { slots, expose }) { 9 | const uiState = reactive({ 10 | labelClasses: { 11 | 'mdc-floating-label': true, 12 | 'mdc-floating-label--required': props.required, 13 | }, 14 | }); 15 | 16 | let foundation; 17 | const root = ref(); 18 | 19 | const adapter = { 20 | addClass: className => 21 | (uiState.labelClasses = { 22 | ...uiState.labelClasses, 23 | [className]: true, 24 | }), 25 | 26 | removeClass: className => { 27 | const { [className]: removed, ...rest } = uiState.labelClasses; 28 | uiState.labelClasses = rest; 29 | }, 30 | 31 | getWidth: () => root.value.scrollWidth, 32 | 33 | registerInteractionHandler: (eventType, handler) => { 34 | root.value.addEventListener(eventType, handler); 35 | }, 36 | 37 | deregisterInteractionHandler: (eventType, handler) => { 38 | root.value.removeEventListener(eventType, handler); 39 | }, 40 | }; 41 | 42 | const getWidth = () => { 43 | return foundation.getWidth(); 44 | }; 45 | 46 | const setRequired = isRequired => { 47 | return foundation.setRequired(isRequired); 48 | }; 49 | 50 | const float = shouldFloat => { 51 | foundation.float(shouldFloat); 52 | }; 53 | 54 | const shake = shouldShake => { 55 | foundation.shake(shouldShake); 56 | }; 57 | 58 | onMounted(() => { 59 | foundation = new MDCFloatingLabelFoundation(adapter); 60 | foundation.init(); 61 | }); 62 | 63 | onBeforeUnmount(() => { 64 | foundation.destroy(); 65 | }); 66 | 67 | expose({ getWidth, float, shake, setRequired }); 68 | return () => { 69 | return h('span', { ref: root, class: uiState.labelClasses }, [ 70 | slots.default(), 71 | ]); 72 | }; 73 | }, 74 | }; 75 | -------------------------------------------------------------------------------- /src/floating-label/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwFloatingLabel from './floating-label.js'; 3 | 4 | export default BasePlugin({ 5 | mcwFloatingLabel, 6 | }); 7 | export { default as mcwFloatingLabel } from './floating-label.js'; 8 | -------------------------------------------------------------------------------- /src/icon-button/icon-button.vue: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /src/icon-button/icon-toggle.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-icon-toggle', 5 | props: { 6 | isOn: Boolean, 7 | }, 8 | 9 | setup(props, { slots }) { 10 | return () => { 11 | return h( 12 | 'i', 13 | { 14 | class: { 15 | 'material-icons': 1, 16 | 'mdc-icon-button__icon': true, 17 | 'mdc-icon-button__icon--on': props.isOn, 18 | }, 19 | }, 20 | slots.default?.(), 21 | ); 22 | }; 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /src/icon-button/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwIconButton from './icon-button.vue'; 3 | import mcwIconToggle from './icon-toggle.js'; 4 | 5 | export { default as mcwIconButton } from './icon-button.vue'; 6 | export { default as mcwIconToggle } from './icon-toggle.js'; 7 | 8 | export default BasePlugin({ 9 | mcwIconButton, 10 | mcwIconToggle, 11 | }); 12 | -------------------------------------------------------------------------------- /src/layout-grid/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwLayoutCell from './layout-cell.js'; 3 | import mcwLayoutGrid from './layout-grid.js'; 4 | import mcwLayoutInnerGrid from './layout-inner-grid.js'; 5 | 6 | export { default as mcwLayoutCell } from './layout-cell.js'; 7 | export { default as mcwLayoutGrid } from './layout-grid.js'; 8 | export { default as mcwLayoutInnerGrid } from './layout-inner-grid.js'; 9 | 10 | export default BasePlugin({ 11 | mcwLayoutGrid, 12 | mcwLayoutCell, 13 | mcwLayoutInnerGrid, 14 | }); 15 | -------------------------------------------------------------------------------- /src/layout-grid/layout-cell.js: -------------------------------------------------------------------------------- 1 | import { computed, h } from 'vue'; 2 | 3 | const spanOptions_ = { 4 | type: [String, Number], 5 | default: undefined, 6 | validator: value => { 7 | const number = Number(value); 8 | return Number.isFinite(number) && number <= 12 && number > 0; 9 | }, 10 | }; 11 | 12 | export default { 13 | name: 'mcw-layout-cell', 14 | props: { 15 | span: spanOptions_, 16 | order: spanOptions_, 17 | phone: spanOptions_, 18 | tablet: spanOptions_, 19 | desktop: spanOptions_, 20 | align: { 21 | type: String, 22 | validator: value => ['top', 'bottom', 'middle'].includes(value), 23 | }, 24 | }, 25 | setup(props, { slots }) { 26 | const classes = computed(() => { 27 | const cssClasses = {}; 28 | 29 | if (props.span) { 30 | cssClasses[`mdc-layout-grid__cell--span-${props.span}`] = true; 31 | } 32 | 33 | if (props.order) { 34 | cssClasses[`mdc-layout-grid__cell--order-${props.order}`] = true; 35 | } 36 | 37 | if (props.phone) { 38 | cssClasses[`mdc-layout-grid__cell--span-${props.phone}-phone`] = true; 39 | } 40 | 41 | if (props.tablet) { 42 | cssClasses[`mdc-layout-grid__cell--span-${props.tablet}-tablet`] = true; 43 | } 44 | 45 | if (props.desktop) { 46 | cssClasses[ 47 | `mdc-layout-grid__cell--span-${props.desktop}-desktop` 48 | ] = true; 49 | } 50 | 51 | if (props.align) { 52 | cssClasses[`mdc-layout-grid__cell--align-${props.align}`] = true; 53 | } 54 | 55 | return cssClasses; 56 | }); 57 | 58 | return () => { 59 | return h( 60 | 'div', 61 | { 62 | class: { 63 | 'mdc-layout-cell': true, 64 | 'mdc-layout-grid__cell': true, 65 | ...classes.value, 66 | }, 67 | }, 68 | [slots.default?.()], 69 | ); 70 | }; 71 | }, 72 | }; 73 | -------------------------------------------------------------------------------- /src/layout-grid/layout-grid.js: -------------------------------------------------------------------------------- 1 | import { computed, h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-layout-grid', 5 | props: { 6 | fixedColumWidth: Boolean, 7 | alignLeft: Boolean, 8 | alignRight: Boolean, 9 | }, 10 | setup(props, { slots }) { 11 | const classes = computed(() => { 12 | return { 13 | 'mdc-layout-grid': true, 14 | 'mdc-layout-grid--fixed-column-width': props.fixedColumnWidth, 15 | 'mdc-layout-grid--align-left': props.alignLeft, 16 | 'mdc-layout-grid--align-right': props.alignRight, 17 | }; 18 | }); 19 | 20 | return () => { 21 | return h( 22 | 'div', 23 | { 24 | class: classes.value, 25 | }, 26 | [h('div', { class: 'mdc-layout-grid__inner' }, [slots.default?.()])], 27 | ); 28 | }; 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /src/layout-grid/layout-inner-grid.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-layout-inner-grid', 5 | setup(props, { slots }) { 6 | return () => { 7 | return h( 8 | 'div', 9 | { class: 'mdc-layout-inner-grid mdc-layout-grid__inner' }, 10 | [slots.default()], 11 | ); 12 | }; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/line-ripple/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwLineRipple from './line-ripple.js'; 3 | 4 | export default BasePlugin({ 5 | mcwLineRipple, 6 | }); 7 | export { default as mcwLineRipple } from './line-ripple.js'; 8 | -------------------------------------------------------------------------------- /src/line-ripple/line-ripple.js: -------------------------------------------------------------------------------- 1 | import { MDCLineRippleFoundation } from '@material/line-ripple/foundation.js'; 2 | import { h, onBeforeUnmount, onMounted, reactive } from 'vue'; 3 | 4 | export default { 5 | name: 'mcw-line-ripple', 6 | setup(props, { expose }) { 7 | const uiState = reactive({ 8 | lineClasses: { 'mdc-line-ripple': 1 }, 9 | lineStyles: {}, 10 | }); 11 | 12 | // note: do not call the property 'foundation' as the tests will then 13 | // expect all methods to be implemented, and we handle transitionend locally. 14 | let foundation_; 15 | 16 | const adapter = { 17 | addClass: className => 18 | (uiState.lineClasses = { 19 | ...uiState.lineClasses, 20 | [className]: true, 21 | }), 22 | 23 | removeClass: className => { 24 | const { [className]: removed, ...rest } = uiState.lineClasses; 25 | uiState.lineClasses = rest; 26 | }, 27 | 28 | hasClass: className => { 29 | return Boolean(uiState.lineClasses[className]); 30 | }, 31 | setStyle: (name, value) => 32 | (uiState.lineStyles = { 33 | ...uiState.lineStyles, 34 | [name]: value, 35 | }), 36 | }; 37 | 38 | expose({ 39 | activate: () => foundation_.activate(), 40 | deactivate: () => foundation_.deactivate(), 41 | setRippleCenter: xCoordinate => foundation_.setRippleCenter(xCoordinate), 42 | }); 43 | 44 | onMounted(() => { 45 | foundation_ = new MDCLineRippleFoundation(adapter); 46 | foundation_.init(); 47 | }); 48 | 49 | onBeforeUnmount(() => { 50 | foundation_.destroy(); 51 | }); 52 | 53 | return () => { 54 | return h('span', { 55 | class: uiState.lineClasses, 56 | style: uiState.lineStyles, 57 | ontransitionend: event_ => foundation_.handleTransitionEnd(event_), 58 | }); 59 | }; 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /src/linear-progress/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwLinearProgress from './linear-progress.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwLinearProgress, 6 | }); 7 | export { default as mcwLinearProgress } from './linear-progress.vue'; 8 | -------------------------------------------------------------------------------- /src/linear-progress/linear-progress.vue: -------------------------------------------------------------------------------- 1 | 2 | 29 | -------------------------------------------------------------------------------- /src/list/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwListItem from './list-item.vue'; 3 | import mcwList from './list.js'; 4 | 5 | export { default as mcwListItem } from './list-item.vue'; 6 | export { default as mcwList } from './list.js'; 7 | 8 | export default BasePlugin({ 9 | mcwList, 10 | mcwListItem, 11 | }); 12 | -------------------------------------------------------------------------------- /src/list/list-item.vue: -------------------------------------------------------------------------------- 1 | 2 | 22 | -------------------------------------------------------------------------------- /src/material-icon/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwMaterialIcon from './material-icon.js'; 3 | 4 | export default BasePlugin({ 5 | mcwMaterialIcon, 6 | }); 7 | export { default as mcwMaterialIcon } from './material-icon.js'; 8 | -------------------------------------------------------------------------------- /src/material-icon/material-icon.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-material-icon', 5 | props: { 6 | icon: String, 7 | tag: { type: String, default: 'i' }, 8 | }, 9 | setup(props, { attrs }) { 10 | return () => { 11 | return h(props.tag, { ...attrs, class: 'material-icons' }, props.icon); 12 | }; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/menu/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import { mcwList } from '../list/index.js'; 3 | import mcwMenuAnchor from './menu-anchor.js'; 4 | import mcwMenuItem from './menu-item.js'; 5 | import mcwMenuSurface from './menu-surface.js'; 6 | import mcwMenu from './menu.vue'; 7 | 8 | export { default as mcwMenuAnchor } from './menu-anchor.js'; 9 | export { default as mcwMenuItem } from './menu-item.js'; 10 | export { default as mcwMenuSurface } from './menu-surface.js'; 11 | export { default as mcwMenu } from './menu.vue'; 12 | 13 | export default BasePlugin({ 14 | mcwMenu, 15 | mcwMenuSurface, 16 | mcwMenuItem, 17 | mcwMenuAnchor, 18 | mcwList, 19 | }); 20 | -------------------------------------------------------------------------------- /src/menu/menu-anchor.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-menu-anchor', 5 | setup(props, { slots }) { 6 | return () => { 7 | return h( 8 | 'div', 9 | { 10 | class: { 11 | 'mdc-menu-surface--anchor': 1, 12 | }, 13 | }, 14 | slots.default?.(), 15 | ); 16 | }; 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/menu/menu-item.js: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | 3 | export default { 4 | name: 'mcw-menu-item', 5 | props: { 6 | disabled: Boolean, 7 | }, 8 | setup(props, { slots }) { 9 | return () => { 10 | return h( 11 | 'li', 12 | { 13 | class: { 14 | 'mdc-menu-divider': 1, 15 | 'mdc-list-divider': 1, 16 | }, 17 | tabindex: props.disabled ? '-1' : '0', 18 | 'aria-disabled': props.disabled, 19 | role: 'menuitem', 20 | }, 21 | slots.default?.(), 22 | ); 23 | }; 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /src/menu/menu.vue: -------------------------------------------------------------------------------- 1 | 2 | 26 | -------------------------------------------------------------------------------- /src/notched-outline/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwNotchedOutline from './notched-outline.js'; 3 | 4 | export default BasePlugin({ 5 | mcwNotchedOutline, 6 | }); 7 | export { default as mcwNotchedOutline } from './notched-outline.js'; 8 | -------------------------------------------------------------------------------- /src/radio/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwRadio from './radio.vue'; 3 | 4 | export default BasePlugin({ mcwRadio }); 5 | export { default as mcwRadio } from './radio.vue'; 6 | -------------------------------------------------------------------------------- /src/radio/radio.vue: -------------------------------------------------------------------------------- 1 | 2 | 35 | -------------------------------------------------------------------------------- /src/ripple/index.js: -------------------------------------------------------------------------------- 1 | export { RippleElement, useRipplePlugin } from './ripple-plugin.js'; 2 | -------------------------------------------------------------------------------- /src/segmented-button/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwSegment from './segment.vue'; 3 | import mcwSegmentedButton from './segmented-button.js'; 4 | 5 | export { default as mcwSegment } from './segment.vue'; 6 | export { default as mcwSegmentedButton } from './segmented-button.js'; 7 | 8 | export default BasePlugin({ 9 | mcwSegmentedButton, 10 | mcwSegment, 11 | }); 12 | -------------------------------------------------------------------------------- /src/segmented-button/segment.vue: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /src/select/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import { mcwFloatingLabel } from '../floating-label/index.js'; 3 | import { mcwLineRipple } from '../line-ripple/index.js'; 4 | import { mcwNotchedOutline } from '../notched-outline/index.js'; 5 | import mcwSelect from './select.vue'; 6 | 7 | export default BasePlugin({ 8 | mcwSelect, 9 | mcwNotchedOutline, 10 | mcwLineRipple, 11 | mcwFloatingLabel, 12 | }); 13 | export { default as mcwSelect } from './select.vue'; 14 | -------------------------------------------------------------------------------- /src/slider/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwSlider from './slider.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwSlider, 6 | }); 7 | export { default as mcwSlider } from './slider.vue'; 8 | -------------------------------------------------------------------------------- /src/snackbar/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwSnackbarQueue from './snackbar-queue.js'; 3 | import mcwSnackbar from './snackbar.vue'; 4 | 5 | export { default as mcwSnackbarQueue } from './snackbar-queue.js'; 6 | export { default as mcwSnackbar } from './snackbar.vue'; 7 | 8 | export default BasePlugin({ 9 | mcwSnackbar, 10 | mcwSnackbarQueue, 11 | }); 12 | -------------------------------------------------------------------------------- /src/snackbar/snackbar-queue.js: -------------------------------------------------------------------------------- 1 | import { h, nextTick, reactive, watch } from 'vue'; 2 | import { mcwSnackbar } from './index.js'; 3 | 4 | const noop = () => {}; 5 | 6 | export default { 7 | name: 'mcw-snackbar-queue', 8 | props: { snack: Object }, 9 | setup(props, { emit, attrs, expose }) { 10 | const uiState = reactive({ 11 | open: false, 12 | }); 13 | 14 | let actionHandler_; 15 | const queue = []; 16 | 17 | let snack; 18 | 19 | const handleSnack = ({ 20 | timeoutMs = 5000, 21 | closeOnEscape, 22 | message = '', 23 | actionText = '', 24 | dismissAction = true, 25 | stacked, 26 | leading, 27 | actionHandler = noop, 28 | }) => { 29 | queue.push(() => { 30 | snack = { 31 | timeoutMs, 32 | closeOnEscape, 33 | message, 34 | actionText, 35 | actionHandler, 36 | dismissAction, 37 | stacked, 38 | leading, 39 | }; 40 | actionHandler_ = actionHandler; 41 | uiState.open = true; 42 | }); 43 | if (queue.length === 1) { 44 | queue[0](); 45 | } 46 | }; 47 | 48 | const handleClosed = () => { 49 | uiState.open = false; 50 | queue.shift(); 51 | 52 | if (queue.length > 0) { 53 | nextTick(() => queue[0]()); 54 | } 55 | }; 56 | 57 | watch( 58 | () => props.snack, 59 | nv => { 60 | if (nv) { 61 | handleSnack(nv); 62 | emit('update:snack'); 63 | } 64 | }, 65 | ); 66 | 67 | expose({ handleSnack }); 68 | 69 | return () => { 70 | return h(mcwSnackbar, { 71 | modelValue: uiState.open, 72 | ...snack, 73 | 'onUpdate:reason': event => { 74 | attrs['update:reason']?.(event); 75 | }, 76 | 'onMdcsnackbar:closed': ({ reason }) => { 77 | if (actionHandler_ && reason === 'action') { 78 | actionHandler_({ reason }); 79 | } 80 | handleClosed(); 81 | emit('closed', { reason }); 82 | }, 83 | }); 84 | }; 85 | }, 86 | }; 87 | -------------------------------------------------------------------------------- /src/snackbar/snackbar.vue: -------------------------------------------------------------------------------- 1 | 2 | 38 | -------------------------------------------------------------------------------- /src/switch/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwSwitch from './switch.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwSwitch, 6 | }); 7 | export { default as mcwSwitch } from './switch.vue'; 8 | -------------------------------------------------------------------------------- /src/switch/switch.vue: -------------------------------------------------------------------------------- 1 | 2 | 51 | -------------------------------------------------------------------------------- /src/tabs/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwTabBar from './tab-bar.js'; 3 | import mcwTabIndicator from './tab-indicator.js'; 4 | import mcwTabScroller from './tab-scroller.js'; 5 | import mcwTab from './tab.vue'; 6 | export { default as mcwTabBar } from './tab-bar.js'; 7 | export { default as mcwTabIndicator } from './tab-indicator.js'; 8 | export { default as mcwTabScroller } from './tab-scroller.js'; 9 | export { default as mcwTab } from './tab.vue'; 10 | 11 | export default BasePlugin({ 12 | mcwTab, 13 | mcwTabBar, 14 | mcwTabScroller, 15 | mcwTabIndicator, 16 | }); 17 | -------------------------------------------------------------------------------- /src/tabs/tab.vue: -------------------------------------------------------------------------------- 1 | 2 | 45 | -------------------------------------------------------------------------------- /src/textfield/character-counter.js: -------------------------------------------------------------------------------- 1 | import { MDCTextFieldCharacterCounterFoundation } from '@material/textfield/character-counter/foundation.js'; 2 | import { onBeforeUnmount, onMounted, reactive, toRefs } from 'vue'; 3 | 4 | export default { 5 | name: 'mcw-character-counter', 6 | setup() { 7 | const uiState = reactive({ textContent: '', foundation: {} }); 8 | 9 | const adapter = { 10 | setContent: content => { 11 | uiState.textContent = content; 12 | }, 13 | }; 14 | 15 | onMounted(() => { 16 | uiState.foundation = new MDCTextFieldCharacterCounterFoundation(adapter); 17 | uiState.foundation.init(); 18 | }); 19 | 20 | onBeforeUnmount(() => { 21 | uiState.foundation.destroy(); 22 | }); 23 | 24 | return { ...toRefs(uiState) }; 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/textfield/character-counter.vue: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/textfield/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import { mcwFloatingLabel } from '../floating-label/index.js'; 3 | import { mcwLineRipple } from '../line-ripple/index.js'; 4 | import { mcwNotchedOutline } from '../notched-outline/index.js'; 5 | import mcwCharacterCounter from './character-counter.vue'; 6 | import mcwTextfieldHelperText from './textfield-helper-text.vue'; 7 | import mcwTextfieldIcon from './textfield-icon.vue'; 8 | import mcwTextfield from './textfield.vue'; 9 | 10 | export { default as mcwCharacterCounter } from './character-counter.vue'; 11 | export { default as mcwTextfieldHelperText } from './textfield-helper-text.vue'; 12 | export { default as mcwTextfieldIcon } from './textfield-icon.vue'; 13 | export { default as mcwTextfield } from './textfield.vue'; 14 | 15 | export default BasePlugin({ 16 | mcwTextfield, 17 | mcwTextfieldIcon, 18 | mcwCharacterCounter, 19 | mcwTextfieldHelperText, 20 | mcwLineRipple, 21 | mcwNotchedOutline, 22 | mcwFloatingLabel, 23 | }); 24 | -------------------------------------------------------------------------------- /src/textfield/textfield-helper-text.js: -------------------------------------------------------------------------------- 1 | import { MDCTextFieldHelperTextFoundation } from '@material/textfield/helper-text/foundation.js'; 2 | import { onBeforeUnmount, onMounted, reactive, toRefs, watch } from 'vue'; 3 | 4 | export default { 5 | name: 'mcw-textfield-helper-text', 6 | props: { 7 | persistent: Boolean, 8 | validation: Boolean, 9 | helptext: String, 10 | }, 11 | setup(props) { 12 | const uiState = reactive({ 13 | classes: { 14 | 'mdc-text-field-helper-text': true, 15 | 'mdc-text-field-helper-text--persistent': props.persistent, 16 | 'mdc-text-field-helper-text--validation-msg': props.validation, 17 | }, 18 | rootAttrs: { 'aria-hidden': true }, 19 | foundation: {}, 20 | }); 21 | 22 | const adapter = { 23 | addClass: className => 24 | (uiState.classes = { ...uiState.classes, [className]: true }), 25 | removeClass: className => { 26 | // eslint-disable-next-line no-unused-vars 27 | const { [className]: removed, ...rest } = uiState.classes; 28 | uiState.classes = rest; 29 | }, 30 | hasClass: className => Boolean(uiState.classes[className]), 31 | 32 | getAttr: attribute => uiState.rootAttrs[attribute], 33 | 34 | setAttr: (attribute, value) => 35 | (uiState.rootAttrs = { ...uiState.rootAttrs, [attribute]: value }), 36 | 37 | removeAttr: attribute => { 38 | // eslint-disable-next-line no-unused-vars 39 | const { [attribute]: removed, ...rest } = uiState.rootAttrs; 40 | uiState.rootAttrs = rest; 41 | }, 42 | 43 | setContent: content => (uiState.helpertext = content), 44 | }; 45 | 46 | watch( 47 | () => props.persistent, 48 | nv => uiState.foundation.setPersistent(nv), 49 | ); 50 | 51 | watch( 52 | () => props.validation, 53 | nv => uiState.foundation.setValidation(nv), 54 | ); 55 | 56 | onMounted(() => { 57 | uiState.foundation = new MDCTextFieldHelperTextFoundation(adapter); 58 | uiState.foundation.init(); 59 | }); 60 | 61 | onBeforeUnmount(() => { 62 | uiState.foundation.destroy(); 63 | }); 64 | 65 | return { ...toRefs(uiState) }; 66 | }, 67 | }; 68 | -------------------------------------------------------------------------------- /src/textfield/textfield-helper-text.vue: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/textfield/textfield-icon.vue: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/tooltip/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwTooltip from './tooltip.vue'; 3 | 4 | export default BasePlugin({ 5 | mcwTooltip, 6 | }); 7 | export { default as mcwTooltip } from './tooltip.vue'; 8 | -------------------------------------------------------------------------------- /src/tooltip/tooltip.vue: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /src/top-app-bar/index.js: -------------------------------------------------------------------------------- 1 | import { BasePlugin } from '../base/index.js'; 2 | import mcwTopAppBar from './top-app-bar.js'; 3 | 4 | export default BasePlugin({ 5 | mcwTopAppBar, 6 | }); 7 | export { default as mcwTopAppBar } from './top-app-bar.js'; 8 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/static/.gitkeep -------------------------------------------------------------------------------- /static/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/static/icon-128x128.png -------------------------------------------------------------------------------- /static/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/static/icon-144x144.png -------------------------------------------------------------------------------- /static/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/static/icon-152x152.png -------------------------------------------------------------------------------- /static/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/static/icon-192x192.png -------------------------------------------------------------------------------- /static/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/static/icon-384x384.png -------------------------------------------------------------------------------- /static/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/static/icon-512x512.png -------------------------------------------------------------------------------- /static/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/static/icon-72x72.png -------------------------------------------------------------------------------- /static/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgbross/vue-material-adapter/79deed9b2ce3242abb184fdaaf6dcd8bb8e747f9/static/icon-96x96.png -------------------------------------------------------------------------------- /static/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/button.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/button/index.js'; 3 | // 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/card.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/card/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/checkbox.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/checkbox/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/chips.spec.js: -------------------------------------------------------------------------------- 1 | import { mount, createLocalVue, checkValidMcwAdapter } from './unit-test'; 2 | import plugin from '../packages/chips/index.js'; 3 | // eslint-disable-next-line no-unused-vars 4 | import { mcwChip, mcwChipSet } from '../packages/chips/index.js'; 5 | 6 | const Spec = { 7 | template: ` 8 | Item One 9 | Item Two 10 | Item Three 11 | `, 12 | }; 13 | 14 | describe(__dirname, () => { 15 | const localVue = createLocalVue(); 16 | localVue.use(plugin); 17 | const spec = mount(Spec, { localVue }); 18 | 19 | describe('mcwChipSet', () => { 20 | const wrapper = spec.findComponent(mcwChipSet); 21 | checkValidMcwAdapter(wrapper.vm); 22 | }); 23 | 24 | // describe('mcwChip', () => { 25 | // const wrapper = spec.find(mcwChip); 26 | // checkValidMcwAdapter(wrapper.vm); 27 | // }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/circular-progress.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/circular-progress/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/dialog.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/dialog/index.js'; 3 | import { mcwButton } from '../packages/button/index.js'; 4 | 5 | pluginSanityCheck(__dirname, plugin, { 6 | mcwDialogContent: { 7 | stubs: { mcwButton: mcwButton }, 8 | }, 9 | mcwDialogButton: { 10 | stubs: { mcwButton: mcwButton }, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /test/drawer.spec1.js: -------------------------------------------------------------------------------- 1 | import { 2 | mount, 3 | createLocalVue, 4 | checkValidMcwAdapter, 5 | checkValidFunctionalComponent, 6 | } from './unit-test'; 7 | import plugin, { 8 | mcwDrawer, 9 | mcwDrawerHeader, 10 | mcwDrawerList, 11 | mcwDrawerItem, 12 | mcwDrawerDivider, 13 | } from '../packages/drawer/index.js'; 14 | const Spec = { 15 | template: ` 16 | 17 | 18 | 19 | Home 20 | 21 | 22 | `, 23 | name: 'spec', 24 | }; 25 | 26 | describe(__dirname, () => { 27 | const localVue = createLocalVue(); 28 | localVue.use(plugin); 29 | const spec = mount(Spec, { localVue, stubs: { 'mcw-drawer': mcwDrawer } }); 30 | 31 | describe('mcwDrawer', () => { 32 | const drawer = spec.findComponent(mcwDrawer); 33 | 34 | checkValidMcwAdapter(drawer.vm); 35 | 36 | describe('mcwDrawerHeader', () => { 37 | const { isFunctionalComponent } = drawer.findComponent(mcwDrawerHeader); 38 | isFunctionalComponent && checkValidFunctionalComponent(mcwDrawerHeader); 39 | }); 40 | 41 | describe('mcwDrawerList', () => { 42 | const { isFunctionalComponent } = drawer.findComponent(mcwDrawerList); 43 | isFunctionalComponent && checkValidFunctionalComponent(mcwDrawerList); 44 | }); 45 | 46 | describe('mcwDrawerItem', () => { 47 | const { isFunctionalComponent } = drawer.findComponent(mcwDrawerItem); 48 | isFunctionalComponent && checkValidFunctionalComponent(mcwDrawerItem); 49 | }); 50 | 51 | describe('mcwDrawerDivider', () => { 52 | const { isFunctionalComponent } = drawer.findComponent(mcwDrawerDivider); 53 | isFunctionalComponent && checkValidFunctionalComponent(mcwDrawerDivider); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/fab.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/fab/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/floating-label.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/floating-label/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/icon-button.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/icon-button/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/layout-grid.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/layout-grid/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/line-ripple.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/line-ripple/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/linear-progress.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/linear-progress/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/list.spec1.js: -------------------------------------------------------------------------------- 1 | import { mount, createLocalVue, checkValidMcwAdapter } from './unit-test'; 2 | import plugin from '../packages/list/index.js'; 3 | import { mcwList } from '../packages/list/index.js'; 4 | 5 | const Spec = { 6 | template: `
7 | 8 | Group 1 9 | 10 |
  • 11 | Single-line item 12 |
  • 13 |
  • 14 | Single-line item 15 |
  • 16 |
  • 17 | Single-line item 18 |
  • 19 |
    20 | 21 | Group 2 22 | 23 |
  • 24 | line item 25 |
  • 26 |
  • 27 | line item 28 |
  • 29 |
    30 |
    31 |
    `, 32 | }; 33 | describe(__dirname, () => { 34 | const localVue = createLocalVue(); 35 | localVue.use(plugin); 36 | const spec = mount(Spec, { localVue }); 37 | 38 | describe('mcwList', () => { 39 | const wrapper = spec.findComponent(mcwList); 40 | checkValidMcwAdapter(wrapper.vm); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/material-icon.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/material-icon/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/menu.spec1.js: -------------------------------------------------------------------------------- 1 | import { createLocalVue, mount, checkValidMcwAdapter } from './unit-test'; 2 | import plugin, { mcwMenu, mcwMenuSurface } from '../packages/menu/index.js'; 3 | import pluginList from '../packages/list/index.js'; 4 | 5 | const Spec = { 6 | template: ``, 7 | }; 8 | 9 | describe(__dirname, () => { 10 | const localVue = createLocalVue(); 11 | localVue.use(plugin); 12 | localVue.use(pluginList); 13 | const spec = mount(Spec, { localVue }); 14 | 15 | describe('mcwMenu', () => { 16 | const wrapper = spec.findComponent(mcwMenu); 17 | checkValidMcwAdapter(wrapper.vm); 18 | }); 19 | 20 | describe('mcwMenuSurface', () => { 21 | const wrapper = spec.findComponent(mcwMenuSurface); 22 | checkValidMcwAdapter(wrapper.vm); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/notched-outline.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/notched-outline/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/radio.spec.js: -------------------------------------------------------------------------------- 1 | import plugin from '../packages/radio/index.js'; 2 | import { pluginSanityCheck } from './unit-test'; 3 | 4 | pluginSanityCheck(__dirname, plugin, { 5 | mcwRadio: { 6 | propsData: { name: 'test', id: 'test-1' }, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /test/select.spec1.js: -------------------------------------------------------------------------------- 1 | import { mount, createLocalVue, checkValidMcwAdapter } from './unit-test'; 2 | import { mcwSelect } from '../packages/select/index.js'; 3 | import { mcwMenu, mcwMenuSurface } from '../packages/menu/index.js'; 4 | import { mcwList } from '../packages/list/index.js'; 5 | import { mcwFloatingLabel } from '../packages/floating-label/index.js'; 6 | import { mcwLineRipple } from '../packages/line-ripple/index.js'; 7 | 8 | const Spec = { 9 | name: 'spec', 10 | template: ` 11 |
    12 | 13 | 14 |
  • type
  • 15 |
    16 | 17 |
    18 |
    `, 19 | data() { 20 | return { 21 | selectValue: undefined, 22 | }; 23 | }, 24 | components: { mcwSelect, mcwMenu }, 25 | }; 26 | 27 | describe(__dirname, () => { 28 | const localVue = createLocalVue(); 29 | describe('mcwSelect', () => { 30 | const spec = mount(Spec, { 31 | localVue, 32 | stubs: { 33 | 'mcw-floating-label': mcwFloatingLabel, 34 | 'mcw-line-ripple': mcwLineRipple, 35 | 'mcw-list': mcwList, 36 | 'mcw-menu-surface': mcwMenuSurface, 37 | }, 38 | }); 39 | test('is a Vue instance', () => { 40 | expect(spec).toBeTruthy(); 41 | }); 42 | describe('mcwSelect', () => { 43 | checkValidMcwAdapter(spec.findComponent(mcwSelect).vm); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/slider.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/slider/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/snackbar.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin, { mcwSnackbar } from '../packages/snackbar/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin, { 5 | mcwSnackbarQueue: { 6 | stubs: { mcwSnackbar: mcwSnackbar }, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /test/switch.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/switch/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/tabs.spec1.js: -------------------------------------------------------------------------------- 1 | import { 2 | pluginSanityCheck, 3 | createLocalVue, 4 | mount, 5 | checkValidMcwAdapter, 6 | } from './unit-test'; 7 | import plugin, { mcwTabBar, mcwTab } from '../packages/tabs/index.js'; 8 | 9 | // pluginSanityCheck(__dirname, plugin); 10 | 11 | const Spec = { 12 | template: ` 13 | One 14 | `, 15 | }; 16 | 17 | describe(__dirname, () => { 18 | const localVue = createLocalVue(); 19 | localVue.use(plugin); 20 | const spec = mount(Spec, { localVue }); 21 | 22 | describe('mcwTabBar', () => { 23 | const wrapper = spec.findComponent(mcwTabBar); 24 | checkValidMcwAdapter(wrapper.vm); 25 | }); 26 | 27 | describe('mcwTab', () => { 28 | const wrapper = spec.findComponent(mcwTab); 29 | checkValidMcwAdapter(wrapper.vm); 30 | }); 31 | 32 | // eslint-disable-next-line no-unused-vars 33 | const { mcwTab: mt, mcwTabBar: mtb, ...rest } = plugin.components; 34 | plugin.components = rest; 35 | 36 | pluginSanityCheck(__dirname, plugin); 37 | }); 38 | -------------------------------------------------------------------------------- /test/textfield.spec1.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck, mount, checkValidFoundation } from './unit-test'; 2 | import plugin, { mcwTextfield } from '../packages/textfield/index.js'; 3 | import { mcwLineRipple } from '../packages/line-ripple/index.js'; 4 | import { mcwFloatingLabel } from '../packages/floating-label/index.js'; 5 | 6 | pluginSanityCheck(__dirname, plugin, { 7 | mcwTextfield: { 8 | propsData: { value: 'test' }, 9 | }, 10 | }); 11 | 12 | describe('mcwTextfield', () => { 13 | const wrapper = mount(mcwTextfield, { 14 | propsData: { value: 'test', label: 'label', helptext: 'helptext' }, 15 | }); 16 | 17 | describe('labelFoundation', () => { 18 | const component = wrapper.findComponent(mcwFloatingLabel); 19 | checkValidFoundation(component.vm.foundation); 20 | }); 21 | 22 | describe('lineRippleFoundation', () => { 23 | const component = wrapper.findComponent(mcwLineRipple); 24 | expect(component).toBeInstanceOf(Object); 25 | }); 26 | 27 | describe('helperTextFoundation', () => { 28 | const component = wrapper.find('.mdc-text-field-helper-line'); 29 | checkValidFoundation(component.vm.foundation); 30 | }); 31 | }); 32 | 33 | describe('mcwTextfield', () => { 34 | const wrapper = mount(mcwTextfield, { 35 | propsData: { value: 'test', label: 'label', outline: true }, 36 | }); 37 | 38 | describe('labelFoundation', () => { 39 | const component = wrapper.findComponent(mcwFloatingLabel); 40 | checkValidFoundation(component.vm.foundation); 41 | }); 42 | 43 | describe('lineRippleFoundation', () => { 44 | const component = wrapper.findComponent(mcwLineRipple); 45 | expect(component).toBeInstanceOf(Object); 46 | }); 47 | 48 | describe('mcwNotchedOutline', () => { 49 | const component = wrapper.find('.mdc-notched-outline'); 50 | checkValidFoundation(component.vm.foundation); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/top-app-bar.spec.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/top-app-bar/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin, { 5 | mcwTopAppBarIcon: { 6 | slots: { default: [''] }, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /test/typography.spec1.js: -------------------------------------------------------------------------------- 1 | import { pluginSanityCheck } from './unit-test'; 2 | import plugin from '../packages/typography/index.js'; 3 | 4 | pluginSanityCheck(__dirname, plugin); 5 | -------------------------------------------------------------------------------- /test/unit-test/polyfills.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | (function() { 4 | var MutationObserver; 5 | 6 | if (window.MutationObserver != null) { 7 | return; 8 | } 9 | 10 | MutationObserver = (function() { 11 | function MutationObserver(callBack) { 12 | this.callBack = callBack; 13 | } 14 | 15 | MutationObserver.prototype.observe = function(element, options) { 16 | this.element = element; 17 | return (this.interval = setInterval( 18 | (function(_this) { 19 | return function() { 20 | var html; 21 | html = _this.element.innerHTML; 22 | if (html !== _this.oldHtml) { 23 | _this.oldHtml = html; 24 | return _this.callBack.apply(null); 25 | } 26 | }; 27 | })(this), 28 | 200, 29 | )); 30 | }; 31 | 32 | MutationObserver.prototype.disconnect = function() { 33 | return window.clearInterval(this.interval); 34 | }; 35 | 36 | return MutationObserver; 37 | })(); 38 | 39 | window.MutationObserver = MutationObserver; 40 | }.call(this)); 41 | (function() { 42 | if (window.matchMedia) { 43 | return; 44 | } 45 | 46 | window.matchMedia = mediaQueryString => { 47 | const mql = { 48 | matches: false, 49 | media: mediaQueryString, 50 | addListener: f => {}, 51 | removeListener: f => {}, 52 | }; 53 | return mql; 54 | }; 55 | }.call(this)); 56 | (function() { 57 | if (window.requestAnimationFrame) { 58 | return; 59 | } 60 | 61 | window.requestAnimationFrame = () => {}; 62 | }.call(this)); 63 | (function() { 64 | if (window.cancelAnimationFrame) { 65 | return; 66 | } 67 | 68 | window.cancelAnimationFrame = () => {}; 69 | }.call(this)); 70 | (function() { 71 | // global HTMLInputElement 72 | Object.defineProperty(window.HTMLInputElement.prototype, 'validity', { 73 | writable: true, 74 | value: { 75 | badInput: false, 76 | valid: true, 77 | }, 78 | }); 79 | 80 | // HTMLInputElement.prototype.validity = { 81 | // badInput: false, 82 | // valid: true 83 | // } 84 | }.call(this)); 85 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | import Markdown from 'vite-plugin-md'; 4 | 5 | import path from 'node:path'; 6 | import url from 'node:url'; 7 | 8 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); 9 | 10 | const resolve = relativePath => path.resolve(__dirname, relativePath); 11 | 12 | // https://vitejs.dev/config/ 13 | export default defineConfig({ 14 | root: './demo/', 15 | plugins: [ 16 | vue({ 17 | include: [/\.vue$/, /\.md$/], 18 | }), 19 | Markdown(), 20 | ], 21 | resolve: { 22 | alias: { 23 | 'vue-material-adapter': resolve('./src/index.js'), 24 | }, 25 | }, 26 | }); 27 | --------------------------------------------------------------------------------