Repository: developit/preact-mdl Branch: master Commit: cc566d5ae37f Files: 16 Total size: 44.7 KB Directory structure: gitextract_gbel3kdn/ ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── docs.md ├── karma.conf.js ├── package.json ├── rollup.config.js ├── src/ │ ├── index.d.ts │ └── index.js └── test/ ├── _setup.js └── index.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [{package.json,.*rc,*.yml}] indent_style = space indent_size = 2 [*.md] trim_trailing_whitespace = false ================================================ FILE: .eslintignore ================================================ *.md *.conf.js ================================================ FILE: .eslintrc ================================================ { "parser": "babel-eslint", "extends": "eslint:recommended", "plugins": [ "react" ], "env": { "browser": true, "mocha": true, "es6": true }, "parserOptions": { "ecmaFeatures": { "modules": true, "jsx": true } }, "globals": { "sinon": true, "expect": true }, "rules": { "react/jsx-uses-react": 2, "react/jsx-uses-vars": 2, "no-empty": 0, "no-console": 0, "no-unused-vars": [0, { "varsIgnorePattern": "^h$" }], "no-cond-assign": 1, "semi": 2, "camelcase": 0, "comma-style": 2, "comma-dangle": [2, "never"], "indent": [2, "tab", {"SwitchCase": 1}], "no-mixed-spaces-and-tabs": [2, "smart-tabs"], "no-trailing-spaces": [2, { "skipBlankLines": true }], "max-nested-callbacks": [2, 3], "no-eval": 2, "no-implied-eval": 2, "no-new-func": 2, "guard-for-in": 2, "eqeqeq": 2, "no-else-return": 2, "no-redeclare": 2, "no-dupe-keys": 2, "radix": 2, "strict": [2, "never"], "no-shadow": 0, "callback-return": [1, ["callback", "cb", "next", "done"]], "no-delete-var": 2, "no-undef-init": 2, "no-shadow-restricted-names": 2, "handle-callback-err": 0, "no-lonely-if": 2, "keyword-spacing": 2, "constructor-super": 2, "no-this-before-super": 2, "no-dupe-class-members": 2, "no-const-assign": 2, "prefer-spread": 2, "no-useless-concat": 2, "no-var": 2, "object-shorthand": 2, "prefer-arrow-callback": 2 } } ================================================ FILE: .gitignore ================================================ /dist /node_modules /npm-debug.log .DS_Store ================================================ FILE: .npmignore ================================================ .eslintrc ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - 4 ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Jason Miller Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # preact-mdl [![NPM](http://img.shields.io/npm/v/preact-mdl.svg)](https://www.npmjs.com/package/preact-mdl) [![travis-ci](https://travis-ci.org/developit/preact-mdl.svg?branch=master)](https://travis-ci.org/developit/preact-mdl) A collection of [Preact] Components that encapsulate Google's [Material Design Lite]. ## Quick Start [![Edit Preact Material Design](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/6w66jzw3mn) Grab the App Skeleton from [JSFiddle](https://jsfiddle.net/developit/weq28uq3/), [Codepen](https://codepen.io/alexkrolick/project/editor/ANGjjr), or [Code Sandbox](https://codesandbox.io/s/6w66jzw3mn) **Using TypeScript?** [preact-mdl-example](https://github.com/tbekolay/preact-mdl-example) is an instant full project setup. ## [Documentation](docs.md) ## Installation ```sh npm install --save material-design-lite preact-mdl ``` ## What does it look like? See for yourself - `preact-mdl` powers [ESBench](http://esbench.com) and [Nectarine](http://nectarine.rocks). _Here's some live-action demos:_ --- ## Usage * Add MDL stylesheets to your html ```html Preact-mdl Example ``` * Import mdl module and components from preact-mdl ```JavaScript import { h, Component } from 'preact'; import mdl from 'material-design-lite/material'; import { Button } from 'preact-mdl'; export default class MyButton extends Component { render() { return(
) } } ``` ## Demos For now, browse these Open Source projects using `preact-mdl`: - **[Documentation Viewer](https://github.com/developit/documentation-viewer/)** - **[Nectarine.rocks](https://github.com/developit/nectarine.rocks/)** - **[Preact Simple Starter](https://github.com/ooade/PreactSimpleStarter)** - _[add yours!](https://github.com/developit/preact-mdl/issues)_ --- ### License [MIT] [Material Design Lite]: http://www.getmdl.io [Preact]: https://github.com/developit/preact [MIT]: http://choosealicense.com/licenses/mit/ ================================================ FILE: docs.md ================================================ ### Table of Contents - [Icon](#icon) - [Button](#button) - [Card](#card) - [Dialog](#dialog) - [Layout](#layout) - [Layout](#layout-1) - [LayoutHeader](#layoutheader) - [LayoutTab](#layouttab) - [LayoutTabPanel](#layouttabpanel) - [Navigation](#navigation) - [Grid](#grid) - [Progress](#progress) - [Spinner](#spinner) - [Menu](#menu) - [MenuItem](#menuitem) - [Slider](#slider) - [Snackbar](#snackbar) - [CheckBox](#checkbox) - [Radio](#radio) - [IconToggle](#icontoggle) - [Switch](#switch) - [Table](#table) - [TableCell](#tablecell) - [ListItem](#listitem) - [TextField](#textfield) - [Tooltip](#tooltip) ## Icon **Extends MaterialComponent** An Icon in the Material Icons font. Note that you must include the font, usually by Google Fonts **Parameters** - `icon` The icon to render. Can also be specified in the Icon text **Examples** ```javascript ``` ```javascript menu ``` ## Button **Extends MaterialComponent** A material button **Parameters** - `primary` = false - `accent` = false - `colored` = false - `raised` = false - `icon` = false - `fab` = false - `mini-fab` = false - `disabled` = false **Examples** ```javascript ``` ## Card **Extends MaterialComponent** Cards are how you represent blocks of infomation. From the Material Design Specifications: A card is a sheet of material that serves as an entry point to more detailed information. TODO: example ## Dialog **Extends MaterialComponent** Dialogs ## Layout **Extends MaterialComponent** Use a layout to specify how your app will use some of material's aspects. Your app should reside in this component. If you want a fixed header, drawer, or tabs, specify them here. **Parameters** - `fixed-header` = false - `fixed-drawer` = false - `overlay-drawer-button` = false - `fixed-tabs` = false **Examples** ```javascript ... ... ``` ## Layout **Extends MaterialComponent** Layouts ## LayoutHeader **Extends MaterialComponent** **Parameters** - `waterfall` = false - `scroll` = false ## LayoutTab **Extends MaterialComponent** **Parameters** - `active` ## LayoutTabPanel **Extends MaterialComponent** **Parameters** - `active` ## Navigation **Extends MaterialComponent** **Parameters** - `large-screen-only` = false ## Grid **Extends MaterialComponent** Responsive Grid **Parameters** - `no-spacing` = false ## Progress **Extends MaterialComponent** **Parameters** - `indeterminate` = false ## Spinner **Extends MaterialComponent** **Parameters** - `active` = false - `single-color` = false ## Menu **Extends MaterialComponent** **Parameters** - `bottom-left` = true - `bottom-right` = false - `top-left` = false - `top-right` = false ## MenuItem **Extends MaterialComponent** **Parameters** - `disabled` = false ## Slider **Extends MaterialComponent** **Parameters** - `min` = 0 - `max` = 100 - `value` = 0 - `tabindex` = 0 - `disabled` = false ## Snackbar **Extends MaterialComponent** Snackbar ## CheckBox **Extends MaterialComponent** **Parameters** - `checked` = false - `disabled` = false ## Radio **Extends MaterialComponent** **Parameters** - `name` (required) - `value` (required) - `checked` = false - `disabled` = false ## IconToggle **Extends MaterialComponent** **Parameters** - `checked` = false - `disabled` = false ## Switch **Extends MaterialComponent** **Parameters** - `checked` = false - `disabled` = false ## Table **Extends MaterialComponent** **Parameters** - `selectable` = false ## TableCell **Extends MaterialComponent** **Parameters** - `non-numeric` = false ## ListItem **Extends MaterialComponent** **Parameters** - `two-line` = false - `three-line` = false ## TextField **Extends MaterialComponent** **Parameters** - `args` **...any** - `floating-label` = false - `multiline` = false - `expandable` = false - `errorMessage` = null - `icon` (used with expandable) ## Tooltip **Extends MaterialComponent** **Parameters** - `for` [id] - `large` = false ================================================ FILE: karma.conf.js ================================================ var path = require('path'); module.exports = function(config) { config.set({ frameworks: ['mocha', 'chai-sinon'], reporters: ['mocha'], browsers: ['PhantomJS'], files: [ 'test/_setup.js', 'test/**/*.js' ], preprocessors: { 'test/**/*.js': ['webpack'], 'src/**/*.js': ['webpack'], '**/*.js': ['sourcemap'] }, webpack: { module: { loaders: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel', query: { sourceMap: 'inline', presets: ['es2015-loose', 'stage-0'], plugins: [ 'transform-object-rest-spread', ['transform-react-jsx', { pragma:'h' }] ] } } ] }, resolve: { modulesDirectories: [__dirname, 'node_modules'], alias: { src: __dirname+'/src' } } }, webpackMiddleware: { noInfo: true } }); }; ================================================ FILE: package.json ================================================ { "name": "preact-mdl", "amdName": "preactMdl", "version": "2.2.2", "description": "A collection of Preact Components that encapsulate Google's Material Design Lite.", "main": "dist/preact-mdl.js", "minified:main": "dist/preact-mdl.min.js", "scripts": { "copy-typescript-definition": "copyfiles -f src/index.d.ts dist", "build": "npm-run-all transpile copy-typescript-definition minify size docs", "transpile": "rollup -c rollup.config.js", "minify": "cross-var cross-env uglifyjs $npm_package_main -cm -o $npm_package_minified_main -p relative --in-source-map ${npm_package_main}.map --source-map ${npm_package_minified_main}.map", "size": "printf \"gzip size: \" && cross-var cross-env gzip-size $npm_package_minified_main | pretty-bytes", "test": "npm-run-all lint build test:karma", "lint": "eslint src test", "test:karma": "karma start --single-run", "docs": "documentation build -f md -o docs.md --infer-private=\"^(component|js|nodeName|render|mdlRender|propClassMapping|ripple|constructor|handle.*|options)$\" --markdown-toc src/index.js", "prepublish": "npm-run-all build test", "release": "cross-var cross-env npm run -s build && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish" }, "typings": "./dist/index.d.ts", "keywords": [ "preact", "mdl", "material design" ], "author": "Jason Miller ", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/developit/preact-mdl.git" }, "bugs": { "url": "https://github.com/developit/preact-mdl/issues" }, "homepage": "https://github.com/developit/preact-mdl", "peerDependencies": { "preact": ">=7" }, "devDependencies": { "babel-core": "^6.5.2", "babel-eslint": "^6.0.2", "babel-loader": "^6.2.3", "babel-plugin-transform-class-properties": "^6.5.2", "babel-plugin-transform-es2015-classes": "^6.5.2", "babel-plugin-transform-object-rest-spread": "^6.6.5", "babel-plugin-transform-react-jsx": "^6.5.2", "babel-preset-es2015": "^6.6.0", "babel-preset-es2015-loose": "^7.0.0", "babel-preset-es2015-loose-rollup": "^7.0.0", "babel-preset-stage-0": "^6.5.0", "chai": "^3.5.0", "copyfiles": "^1.0.0", "cross-env": "^4.0.0", "cross-var": "^1.0.3", "documentation": "^4.0.0-beta.18", "eslint": "^2.2.0", "eslint-plugin-react": "^4.3.0", "gzip-size-cli": "^1.0.0", "karma": "^0.13.21", "karma-chai": "^0.1.0", "karma-chai-sinon": "^0.1.5", "karma-mocha": "^0.2.2", "karma-mocha-reporter": "^2.0.0", "karma-phantomjs-launcher": "^1.0.0", "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^1.7.0", "mkdirp": "^0.5.1", "mocha": "^2.4.5", "npm-run-all": "^1.7.0", "phantomjs": "^2.1.3", "phantomjs-prebuilt": "^2.1.4", "preact": "^7.1.0", "preact-jsx-chai": "^1.4.1", "pretty-bytes-cli": "^1.0.0", "rollup": "^0.25.4", "rollup-plugin-babel": "^2.4.0", "rollup-plugin-commonjs": "^2.2.1", "rollup-plugin-node-resolve": "^1.5.0", "sinon": "^1.17.2", "sinon-chai": "^2.8.0", "uglify-js": "^2.6.2", "webpack": "^1.12.14" }, "dependencies": {} } ================================================ FILE: rollup.config.js ================================================ import path from 'path'; import fs from 'fs'; import babel from 'rollup-plugin-babel'; import nodeResolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; let pkg = JSON.parse(fs.readFileSync('./package.json')); let external = Object.keys(pkg.peerDependencies || {}).concat(Object.keys(pkg.dependencies || {})); export default { entry: 'src/index.js', dest: pkg.main, sourceMap: path.resolve(pkg.main), moduleName: pkg.amdName, format: 'umd', external, plugins: [ babel({ babelrc: false, comments: false, exclude: 'node_modules/**', presets: [ 'es2015-loose-rollup', 'stage-0' ], plugins: [ 'transform-class-properties', ['transform-react-jsx', { pragma: 'h' }] ] }), nodeResolve({ jsnext: true, main: true, skip: external }), commonjs({ include: 'node_modules/**', exclude: '**/*.css' }) ] }; ================================================ FILE: src/index.d.ts ================================================ export const options: {}; export class MaterialComponent extends preact.Component { component: string; js: boolean; mdlClasses: string[]; nodeName: string; propClassMapping: {[prop: string]: string}; ripple: boolean; upgradedBase: HTMLElement; createMdlClasses(props: PropsType & preact.ComponentProps): string[]; mdlRender(props: PropsType & preact.ComponentProps): preact.VNode; preserveMdlDom(base: HTMLElement, r: HTMLElement): void; render(props: PropsType & preact.ComponentProps, state: StateType): preact.VNode; } export interface HTMLProps extends JSX.HTMLAttributes { } /** Icon */ export interface IconProps extends HTMLProps { badge?: string; } export class Icon extends MaterialComponent { } /** Button */ export interface ButtonProps extends HTMLProps { accent?: boolean; colored?: boolean; fab?: boolean; "mini-fab"?: boolean; primary?: boolean; raised?: boolean; ripple?: boolean; } export class Button extends MaterialComponent { } /** Cards */ export interface CardProps extends HTMLProps { shadow?: number; } export class Card extends MaterialComponent { } export class CardActions extends MaterialComponent { } export class CardMedia extends MaterialComponent { } export class CardMenu extends MaterialComponent { } export class CardText extends MaterialComponent { } export class CardTitle extends MaterialComponent { } export class CardTitleText extends MaterialComponent { } export namespace Card { var Actions: typeof CardActions; var Media: typeof CardMedia; var Menu: typeof CardMenu; var Text: typeof CardText; var Title: typeof CardTitle; var TitleText: typeof CardTitleText; } /** Dialogs */ export class Dialog extends MaterialComponent {} export class DialogActions extends MaterialComponent { } export class DialogContent extends MaterialComponent { } export class DialogTitle extends MaterialComponent { } export namespace Dialog { var Actions: typeof DialogActions; var Content: typeof DialogContent; var Title: typeof DialogTitle; } /** Layouts */ export interface LayoutProps extends HTMLProps { "fixed-header"?: boolean; "fixed-drawer"?: boolean; "fixed-tabs"?: boolean; "overlay-drawer-button"?: boolean; } export class Layout extends MaterialComponent { } export class LayoutContent extends MaterialComponent { } export class LayoutDrawer extends MaterialComponent { } export interface LayoutHeaderProps extends HTMLProps { scroll?: boolean; waterfall?: boolean; } export class LayoutHeader extends MaterialComponent { } export class LayoutHeaderRow extends MaterialComponent { } export class LayoutSpacer extends MaterialComponent { } export interface LayoutTabProps extends HTMLProps { active?: boolean; } export class LayoutTab extends MaterialComponent { } export class LayoutTabBar extends MaterialComponent { } export interface LayoutTabPanelProps extends HTMLProps { active?: boolean; } export class LayoutTabPanel extends MaterialComponent { } export class LayoutTitle extends MaterialComponent { } export namespace Layout { var Content: typeof LayoutContent; var Drawer: typeof LayoutDrawer; var Header: typeof LayoutHeader; var HeaderRow: typeof LayoutHeaderRow; var Spacer: typeof LayoutSpacer; var Tab: typeof LayoutTab; var TabBar: typeof LayoutTabBar; var TabPanel: typeof LayoutTabPanel; var Title: typeof LayoutTitle; } /** Navigation */ export interface NavigationProps extends HTMLProps { "large-screen-only"?: boolean; } export class Navigation extends MaterialComponent { } export class NavigationLink extends MaterialComponent { handleClick(e: any): boolean|void; } export namespace Navigation { var Link: typeof NavigationLink; } /** Tabs */ export class Tabs extends MaterialComponent { } export interface TabProps extends HTMLProps { active?: boolean; } export class Tab extends MaterialComponent { } export class TabBar extends MaterialComponent { } export interface TabPanelProps extends HTMLProps { active?: boolean; } export class TabPanel extends MaterialComponent { } export namespace Tabs { var Bar: typeof TabBar; var Panel: typeof TabPanel; } /** MegaFooter */ export class MegaFooter extends MaterialComponent { } export class MegaFooterBottomSection extends MaterialComponent { } export class MegaFooterDropDownSection extends MaterialComponent { } export class MegaFooterHeading extends MaterialComponent { } export class MegaFooterLinkList extends MaterialComponent { } export class MegaFooterMiddleSection extends MaterialComponent { } export namespace MegaFooter { var BottomSection: typeof MegaFooterBottomSection; var DropDownSection: typeof MegaFooterDropDownSection; var Heading: typeof MegaFooterHeading; var LinkList: typeof MegaFooterLinkList; var MiddleSection: typeof MegaFooterMiddleSection; } /** MiniFooter */ export class MiniFooter extends MaterialComponent { } export class MiniFooterLeftSection extends MaterialComponent { } export class MiniFooterLinkList extends MaterialComponent { } export namespace MiniFooter { var LeftSection: typeof MiniFooterLeftSection; var LinkList: typeof MiniFooterLinkList; } /** Grid */ export interface GridProps extends HTMLProps { "no-spacing"?: boolean; } export class Grid extends MaterialComponent { } export class Cell extends MaterialComponent { } export namespace Grid { var Cell: any; // XXX should be typeof Cell but TypeScript complains } /** Progress */ export interface ProgressProps extends HTMLProps { indeterminate?: boolean; } export class Progress extends MaterialComponent { } /** Spinner */ export interface SpinnerProps extends HTMLProps { "single-color"?: boolean; active?: boolean; } export class Spinner extends MaterialComponent { } /** Menu */ export interface MenuProps extends HTMLProps { "bottom-left"?: boolean; "bottom-right"?: boolean; "top-left"?: boolean; "top-right"?: boolean; ripple?: boolean; } export class Menu extends MaterialComponent { } export class MenuItem extends MaterialComponent { } export namespace Menu { var Item: typeof MenuItem; } /** Slider */ export interface SliderProps extends HTMLProps { min?: number; max?: number; } export class Slider extends MaterialComponent { } /** Snackbar */ export class Snackbar extends MaterialComponent { } /** CheckBox */ export interface CheckBoxProps extends HTMLProps { ripple?: boolean; } export class CheckBox extends MaterialComponent { getValue(): boolean; } /** Radio */ export interface RadioProps extends HTMLProps { name: string; value: string; ripple?: boolean; } export class Radio extends MaterialComponent { getValue(): boolean; } /** IconToggle */ export interface IconToggleProps extends HTMLProps { ripple?: boolean; } export class IconToggle extends MaterialComponent { getValue(): boolean; } /** Switch */ export interface SwitchProps extends HTMLProps { ripple?: boolean; } export class Switch extends MaterialComponent { getValue(): boolean; } /** Table */ export interface TableProps extends HTMLProps { selectable?: boolean; shadow?: number; } export class Table extends MaterialComponent { } export interface TableCellProps extends HTMLProps { "non-numeric"?: boolean; } export class TableCell extends MaterialComponent { } export namespace Table { var Cell: typeof TableCell; } /** List */ export class List extends MaterialComponent { } export interface ListItemProps extends HTMLProps { "two-line"?: boolean; "three-line"?: boolean; } export class ListItem extends MaterialComponent { } export namespace List { var Item: typeof ListItem; } /** TextField */ export interface TextFieldProps extends HTMLProps { "floating-label"?: boolean; errorMessage?: string; expandable?: boolean; multiline?: boolean; onSearch?: ((event) => boolean|void); } export class TextField extends MaterialComponent { } /** Tooltip */ export interface TooltipProps extends HTMLProps { for: string; large?: boolean; } export class Tooltip extends MaterialComponent { } export default { options, Button, Card, Cell, CheckBox, Dialog, Grid, Icon, IconToggle, Layout, List, ListItem, Progress, MegaFooter, Menu, MiniFooter, Navigation, Radio, Slider, Snackbar, Spinner, Switch, Table, Tabs, TextField, Tooltip, }; ================================================ FILE: src/index.js ================================================ import { h, Component } from 'preact'; export const options = {}; function mdl() { return options.mdl || options.componentHandler || window.componentHandler; } const RIPPLE_CLASS = 'js-ripple-effect'; const MDL_PREFIX = s => MDL_NO_PREFIX[s] ? s : `mdl-${s}`; const MDL_NO_PREFIX = { 'is-active': true }; let uidCounter = 1; function uid() { return ++uidCounter; } function extend(base, props) { for (let i in props) if (props.hasOwnProperty(i)) base[i] = props[i]; return base; } function setClass(attributes, value, append) { let cl = getClass(attributes); if (attributes.className) delete attributes.className; if (append) value = cl ? (cl + ' ' + value) : value; attributes.class = value; } function getClass(attributes) { return attributes.class || attributes.className; } let propMaps = { disabled({ attributes }) { if (attributes.hasOwnProperty('disabled') && !attributes.disabled) { attributes.disabled = null; } }, badge({ attributes }) { attributes['data-badge'] = attributes.badge; delete attributes.badge; setClass(attributes, 'mdl-badge', true); }, active({ attributes }) { if (attributes.active) { setClass(attributes, 'is-active', true); } }, shadow({ attributes }) { let d = parseFloat(attributes.shadow)|0, c = getClass(attributes).replace(/\smdl-[^ ]+--shadow\b/g,''); setClass(attributes, c + (c ? ' ' : '') + `mdl-shadow--${d}dp`); } }; export class MaterialComponent extends Component { component = 'none'; js = false; ripple = false; mdlClasses = null; upgradedBase = null; mdlRender(props) { return
{ props.children }
; } render(props, state) { let r = this.mdlRender(props, state); if (this.nodeName) r.nodeName = this.nodeName; if (!r.attributes) r.attributes = {}; r.attributes.class = this.createMdlClasses(props).concat(r.attributes.class || [], r.attributes.className || []).join(' '); for (let i in propMaps) if (propMaps.hasOwnProperty(i)) { if (props.hasOwnProperty(i)) { propMaps[i](r); } } if (this.base && this.upgradedBase) { this.preserveMdlDom(this.base, r); } return r; } // Copy some transient properties back out of the DOM into VDOM prior to diffing so they don't get overwritten preserveMdlDom(base, r) { if (!base || !base.hasAttribute || !r) return; let c = base.childNodes, persist = [ 'mdl-js-ripple-effect--ignore-events', 'mdl-js-ripple-effect', 'is-upgraded', 'is-dirty' ], v = base.getAttribute('data-upgraded'), a = r.attributes, cl = getClass(a) || '', foundRipple = false; if (!a) a = {}; if (v) { a['data-upgraded'] = v; upgradeQueue.add(base); } if (base.hasAttribute('ink-enabled')) { if (!r.attributes) r.attributes = {}; r.attributes['ink-enabled'] = 'true'; } for (let i=0; i * @example * menu */ export class Icon extends MaterialComponent { mdlRender(props) { let c = getClass(props) || '', icon = String(props.icon || props.children).replace(/[ -]/g, '_'); delete props.icon; delete props.className; if (typeof c==='string') { c = 'material-icons ' + c; } else { c['material-icons'] = true; } return { icon }; } } /** @class Button * @desc A material button * * @example * * * @param primary = false * @param accent = false * @param colored = false * @param raised = false * @param icon = false * @param fab = false * @param mini-fab = false * @param disabled = false */ export class Button extends MaterialComponent { component = 'button'; nodeName = 'button'; js = true; ripple = true; } /** * @class Card * @desc Cards are how you represent blocks of infomation. From the Material Design Specifications: A card is a sheet of material that serves as an entry point to more detailed information. * TODO: example */ export class Card extends MaterialComponent { component = 'card'; } export class CardTitle extends MaterialComponent { component = 'card__title'; propClassMapping = { expand: 'card--expand' }; } export class CardTitleText extends MaterialComponent { component = 'card__title-text'; nodeName = 'h2'; } export class CardMedia extends MaterialComponent { component = 'card__media'; } export class CardText extends MaterialComponent { component = 'card__supporting-text'; } export class CardActions extends MaterialComponent { component = 'card__actions'; // mdlClasses = ['card--border']; } export class CardMenu extends MaterialComponent { component = 'card__menu'; } extend(Card, { Title: CardTitle, TitleText: CardTitleText, Media: CardMedia, Text: CardText, Actions: CardActions, Menu: CardMenu }); /** Dialogs */ export class Dialog extends MaterialComponent { component = 'dialog'; nodeName = 'dialog'; show = () => { this.base.show(); } showModal = () => { this.base.showModal(); } close = () => { this.base.close && this.base.close(); } } export class DialogTitle extends MaterialComponent { component = 'dialog__title'; } export class DialogContent extends MaterialComponent { component = 'dialog__content'; } export class DialogActions extends MaterialComponent { component = 'dialog__actions'; } extend(Dialog, { Title: DialogTitle, Content: DialogContent, Actions: DialogActions }); /** Layouts */ /** * @class Layout * @desc Use a layout to specify how your app will use some of material's aspects. Your app should reside in this component. If you want a fixed header, drawer, or tabs, specify them here. * @param fixed-header = false * @param fixed-drawer = false * @param overlay-drawer-button = false * @param fixed-tabs = false * * @example * * * ... * * ... * */ export class Layout extends MaterialComponent { component = 'layout'; js = true; } /** @param waterfall = false * @param scroll = false */ export class LayoutHeader extends MaterialComponent { component = 'layout__header'; nodeName = 'header'; } export class LayoutHeaderRow extends MaterialComponent { component = 'layout__header-row'; } export class LayoutTitle extends MaterialComponent { component = 'layout-title'; nodeName = 'span'; } export class LayoutSpacer extends MaterialComponent { component = 'layout-spacer'; } export class LayoutDrawer extends MaterialComponent { component = 'layout__drawer'; } export class LayoutContent extends MaterialComponent { component = 'layout__content'; nodeName = 'main'; } export class LayoutTabBar extends MaterialComponent { component = 'layout__tab-bar'; js = true; ripple = false; } /** @param active */ export class LayoutTab extends MaterialComponent { component = 'layout__tab'; nodeName = 'a'; } /** @param active */ export class LayoutTabPanel extends MaterialComponent { component = 'layout__tab-panel'; mdlRender(props) { return
{ props.children }
; } } extend(Layout, { Header: LayoutHeader, HeaderRow: LayoutHeaderRow, Title: LayoutTitle, Spacer: LayoutSpacer, Drawer: LayoutDrawer, Content: LayoutContent, TabBar: LayoutTabBar, Tab: LayoutTab, TabPanel: LayoutTabPanel }); /** @param large-screen-only = false */ export class Navigation extends MaterialComponent { component = 'navigation'; nodeName = 'nav'; propClassMapping = { 'large-screen-only': 'layout--large-screen-only' } mdlRender(props, state) { let r = super.mdlRender(props, state); r.children.forEach( item => { if (!item) return item; let c = item.attributes && getClass(item.attributes) || ''; if (!c.match(/\bmdl-navigation__link\b/g)) { if (!item.attributes) item.attributes = {}; setClass(item.attributes, ' mdl-navigation__link', true); } }); return r; } } export class NavigationLink extends MaterialComponent { component = 'navigation__link'; nodeName = 'a'; constructor(...args) { super(...args); this.handleClick = this.handleClick.bind(this); } handleClick(e) { let { route, href, onClick, onclick } = this.props; onClick = onClick || onclick; if (typeof onClick==='function' && onClick({ type: 'click', target: this })===false) { } else if (typeof route==='function') { route(href); } e.preventDefault(); return false; } mdlRender({ children, ...props }, state) { return { children }; } } Navigation.Link = NavigationLink; export class Tabs extends MaterialComponent { component = 'tabs'; js = true; ripple = false; } export class TabBar extends MaterialComponent { component = 'tabs__tab-bar'; } export class Tab extends MaterialComponent { component = 'tabs__tab'; nodeName = 'a'; } export class TabPanel extends MaterialComponent { component = 'tabs__panel'; nodeName = 'section'; } extend(Tabs, { TabBar, Bar: TabBar, Tab, TabPanel, Panel: TabPanel }); export class MegaFooter extends MaterialComponent { component = 'mega-footer'; nodeName = 'footer'; } export class MegaFooterMiddleSection extends MaterialComponent { component = 'mega-footer__middle-section'; } export class MegaFooterDropDownSection extends MaterialComponent { component = 'mega-footer__drop-down-section'; } export class MegaFooterHeading extends MaterialComponent { component = 'mega-footer__heading'; nodeName = 'h1'; } export class MegaFooterLinkList extends MaterialComponent { component = 'mega-footer__link-list'; nodeName = 'ul'; } export class MegaFooterBottomSection extends MaterialComponent { component = 'mega-footer__bottom-section'; } extend(MegaFooter, { MiddleSection: MegaFooterMiddleSection, DropDownSection: MegaFooterDropDownSection, Heading: MegaFooterHeading, LinkList: MegaFooterLinkList, BottomSection: MegaFooterBottomSection }); export class MiniFooter extends MaterialComponent { component = 'mini-footer'; nodeName = 'footer'; } export class MiniFooterLeftSection extends MaterialComponent { component = 'mini-footer__left-section'; } export class MiniFooterLinkList extends MaterialComponent { component = 'mini-footer__link-list'; nodeName = 'ul'; } extend(MiniFooter, { LeftSection: MiniFooterLeftSection, LinkList: MiniFooterLinkList }); /** Responsive Grid * @param no-spacing = false */ export class Grid extends MaterialComponent { component = 'grid'; } export class Cell extends MaterialComponent { component = 'cell'; } Grid.Cell = Cell; /** @param indeterminate = false */ export class Progress extends MaterialComponent { component = 'progress'; js = true; mdlRender(props) { return (
); } componentDidUpdate() { let api = this.base.MaterialProgress, p = this.props; if (p.progress) api.setProgress(p.progress); if (p.buffer) api.setBuffer(p.buffer); } } /** @param active = false * @param single-color = false */ export class Spinner extends MaterialComponent { component = 'spinner'; js = true; // shouldComponentUpdate = () => false; } /** @param bottom-left = true * @param bottom-right = false * @param top-left = false * @param top-right = false */ export class Menu extends MaterialComponent { component = 'menu'; nodeName = 'ul'; js = true; ripple = true; } /** @param disabled = false */ export class MenuItem extends MaterialComponent { component = 'menu__item'; nodeName = 'li'; } Menu.Item = MenuItem; /** @param min = 0 * @param max = 100 * @param value = 0 * @param tabindex = 0 * @param disabled = false */ export class Slider extends MaterialComponent { component = 'slider'; js = true; mdlRender(props) { return ; } } /** Snackbar */ export class Snackbar extends MaterialComponent { component = 'snackbar'; js = true; mdlRender(props) { return (
{props.children}
); } } /** @param checked = false * @param disabled = false */ export class CheckBox extends MaterialComponent { component = 'checkbox'; js = true; ripple = true; getValue() { return this.base.children[0].checked; } mdlRender(props) { let evt = {}; for (let i in props) if (i.match(/^on[a-z]+$/gi)) { evt[i] = props[i]; delete props[i]; } return ( ); } } /** @param name (required) * @param value (required) * @param checked = false * @param disabled = false */ export class Radio extends MaterialComponent { component = 'radio'; js = true; ripple = true; getValue() { return this.base.children[0].checked; } mdlRender(props) { return ( ); } } /** @param checked = false * @param disabled = false */ export class IconToggle extends MaterialComponent { component = 'icon-toggle'; js = true; ripple = true; getValue() { return this.base.children[0].checked; } mdlRender(props) { return ( ); } } /** @param checked = false * @param disabled = false */ export class Switch extends MaterialComponent { component = 'switch'; nodeName = 'label'; js = true; ripple = true; shouldComponentUpdate({ checked }) { if (Boolean(checked)===Boolean(this.props.checked)) return false; return true; } getValue() { return this.base.children[0].checked; } mdlRender({ ...props }) { let evt = {}; for (let i in props) if (i.match(/^on[a-z]+$/gi)) { evt[i] = props[i]; delete props[i]; } return ( ); } } /** @param selectable = false */ export class Table extends MaterialComponent { component = 'data-table'; nodeName = 'table'; js = true; } /** @param non-numeric = false */ export class TableCell extends MaterialComponent { component = 'data-table__cell'; nodeName = 'td'; } Table.Cell = TableCell; export class List extends MaterialComponent { component = 'list'; nodeName = 'ul'; } /** @param two-line = false * @param three-line = false */ export class ListItem extends MaterialComponent { component = 'list__item'; nodeName = 'li'; } List.Item = ListItem; /** @param floating-label = false * @param multiline = false * @param expandable = false * @param errorMessage = null * @param icon (used with expandable) */ export class TextField extends MaterialComponent { component = 'textfield'; js = true; constructor(...args) { super(...args); this.id = uid(); } componentDidUpdate() { let input = this.base && this.base.querySelector && this.base.querySelector('input,textarea'); if (input && input.value && input.value!==this.props.value) { input.value = this.props.value; } if (input && input.setCustomValidity) { input.setCustomValidity(this.props.errorMessage || ""); } } mdlRender(props={}) { let id = props.id || this.id, errorMessage = props.errorMessage, p = extend({}, props); delete p.class; delete p.errorMessage; let field = (
{errorMessage ? {errorMessage} : null}
); if (props.multiline) { field.children[0].nodeName = 'textarea'; // field.children[0].children = [props.value]; } if (props.expandable===true) { (field.attributes = field.attributes || {}).class = 'mdl-textfield__expandable-holder'; field = (
{ field }
); } let cl = getClass(props); if (cl) { (field.attributes = field.attributes || {}).class = cl; } return field; } } /** @param for [id] * @param large = false */ export class Tooltip extends MaterialComponent { component = 'tooltip'; } export default { options, Icon, Button, Card, Dialog, Layout, Navigation, Tabs, MegaFooter, MiniFooter, Grid, Cell, Progress, Spinner, Menu, Slider, Snackbar, CheckBox, Radio, IconToggle, Switch, Table, TextField, Tooltip, List, ListItem }; ================================================ FILE: test/_setup.js ================================================ if (!Function.prototype.bind) { Function.prototype.bind = function(context, ...curry) { return (...args) => this.call(context, ...curry, ...args); }; } ================================================ FILE: test/index.js ================================================ import { h, render, Component } from 'preact'; import assertJsx from 'preact-jsx-chai'; chai.use(assertJsx); import { Button, TextField } from '..'; /*eslint-env browser,mocha*/ /*global sinon,expect,chai*/ describe('preact-mdl', () => { let scratch; before( () => { scratch = document.createElement('div'); (document.body || document.documentElement).appendChild(scratch); }); beforeEach( () => { scratch.innerHTML = ''; }); after( () => { scratch.parentNode.removeChild(scratch); scratch = null; }); describe(' ).to.eql( ); }); }); describe('', () => { it('should add the custom class', () => { let field = ; expect(field).to.contain('custom_class'); }); }); });