├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── wired-button.md ├── wired-calendar.md ├── wired-card.md ├── wired-checkbox.md ├── wired-combo.md ├── wired-dialog.md ├── wired-divider.md ├── wired-fab.md ├── wired-icon-button.md ├── wired-image.md ├── wired-input.md ├── wired-item.md ├── wired-link.md ├── wired-listbox.md ├── wired-progress-ring.md ├── wired-progress.md ├── wired-radio-group.md ├── wired-radio.md ├── wired-search-input.md ├── wired-slider.md ├── wired-spinner.md ├── wired-tabs.md ├── wired-textarea.md ├── wired-toggle.md └── wired-video.md ├── examples ├── button.html ├── calendar.html ├── card.html ├── checkbox.html ├── combo.html ├── dialog.html ├── divider.html ├── fab.html ├── icon-button.html ├── image.html ├── input.html ├── link.html ├── listbox.html ├── progress-ring.html ├── progress.html ├── radio.html ├── search-input.html ├── slider.html ├── spinner.html ├── tabs.html ├── textarea.html ├── toggle.html └── video.html ├── experimental ├── icon.html ├── iconset-generator.html ├── wired-icon │ ├── README.md │ ├── lib │ │ ├── WiredIcon.d.ts │ │ ├── WiredIcon.js │ │ ├── wired-icon.d.ts │ │ └── wired-icon.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── WiredIcon.ts │ │ └── wired-icon.ts │ └── tsconfig.json └── wired-mat-icon │ ├── CONTRIBUTING.md │ ├── README.md │ ├── generate │ ├── actions.svg │ ├── alert.svg │ ├── av.svg │ ├── communication.svg │ ├── content.svg │ ├── device.svg │ ├── editor.svg │ ├── file.svg │ ├── hardware.svg │ ├── image.svg │ ├── index.html │ ├── maps.svg │ ├── navigation.svg │ ├── not_converted.svg │ ├── notification.svg │ ├── places.svg │ ├── social.svg │ └── toggle.svg │ ├── lib │ ├── WiredMatIcon.d.ts │ ├── WiredMatIcon.js │ ├── iconset │ │ ├── iconset-full.d.ts │ │ ├── iconset-full.js │ │ ├── index.d.ts │ │ └── index.js │ ├── wired-mat-icon.d.ts │ └── wired-mat-icon.js │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── WiredMatIcon.ts │ ├── iconset │ │ ├── iconset-full.ts │ │ └── index.ts │ └── wired-mat-icon.ts │ └── tsconfig.json ├── package-lock.json ├── package.json ├── src ├── wired-base.ts ├── wired-button.ts ├── wired-calendar.ts ├── wired-card.ts ├── wired-checkbox.ts ├── wired-combo.ts ├── wired-dialog.ts ├── wired-divider.ts ├── wired-elements.ts ├── wired-fab.ts ├── wired-icon-button.ts ├── wired-image.ts ├── wired-input.ts ├── wired-item.ts ├── wired-lib.ts ├── wired-link.ts ├── wired-listbox.ts ├── wired-progress-ring.ts ├── wired-progress.ts ├── wired-radio-group.ts ├── wired-radio.ts ├── wired-search-input.ts ├── wired-slider.ts ├── wired-spinner.ts ├── wired-tab.ts ├── wired-tabs.ts ├── wired-textarea.ts ├── wired-toggle.ts └── wired-video.ts ├── tsconfig.json └── tslint.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: pshihn 2 | open_collective: rough 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | packages -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | examples 3 | .github 4 | tsconfig.json 5 | tslint.json 6 | packages -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [3.0.0] - April 21, 2021 6 | * Moved from lit-elment to lit 2.0 as the base Web Component class 7 | * Moved multi-package monorepo to a single package that has all the elements exported 8 | 9 | ## [2.0.0] - October 19, 2019 10 | * New component: **wired-video** Video player with the hand-drawn look 11 | * New component: **wired-calendar** is a calendar component contributed by [@elingerojo](https://github.com/elingerojo) 12 | * New component: **wired-dialog** emulates dialogs 13 | * New component: **wired-divider** Draws a sketchy horizontal line between two sections 14 | * New component: **wired-image** Image component that frames the image in a sketchy border 15 | * New component: **wired-link** Akin to `` tag, a link with href, and a sketchy underline 16 | * New component: **wired-search-input** is a text input emulating a search input 17 | * **wired-card** now supports a sketchy filled background 18 | * **wired-slider** is more accessible, now built on top of input range. 19 | * Elements are more responsive to size changes using the Resize observer 20 | * **wired-textarea** does not auto-grow anymore. 21 | 22 | 23 | ## [1.0.0] - April 24, 2019 24 | 25 | * New component: **wired-fab** mimics the floating action button proposed in Material design 26 | * New component: **wired-spinner** to show pending progress in a sketchy way 27 | * New component: **wired-tabs** 28 | ![wired tabs](https://wiredjs.github.io/wired-elements/images/tabs.png) 29 | * Selection in Combo and List is now shown with a sketchy zig-zag fill in the style of [rough.js](https://roughjs.com) 30 | ![wired combo](https://wiredjs.github.io/wired-elements/images/combo.gif) 31 | * Sketchy fill also applied to progress boxes 32 | * Better Accessibility on all components 33 | * Refactored code to use TypeScript and latest [Lit Element](https://lit-element.polymer-project.org/) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Preet Shihn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wired-elements 👉 [wiredjs.com](https://wiredjs.com) 2 | Wired Elements is a series of basic UI Elements that have a hand drawn look. These can be used for wireframes, mockups, or just the fun hand-drawn look. 3 | 4 | ![alt Preview](https://i.imgur.com/qttPllg.png) 5 | 6 | 7 | ## Try now 8 | Play with wired-elements: 9 | 10 | [Wired Elements](https://codesandbox.io/s/wired-elements-vanilla-4bpny) 11 | 12 | #### Try it with a framework 13 | 14 | [Wired Elements in React](https://codesandbox.io/s/xrll5wyl8w) 15 | 16 | [Wired Elements in Vue](https://codesandbox.io/s/vj389y9375) 17 | 18 | [Wired Elements in Svelte](https://codesandbox.io/s/wired-elements-svelte-4hfkb) 19 | 20 | 21 | ## Install 22 | 23 | The package (wired-elements) exports all components in the **_wired_** category. List of all wired elements can be found [here](https://github.com/rough-stuff/wired-elements/tree/master/src). 24 | 25 | Add wired-elements to your project: 26 | ``` 27 | npm i wired-elements 28 | ``` 29 | 30 | 31 | Or load the ES module directly through unpkg 32 | 33 | ```html 34 | 35 | ``` 36 | 37 | 38 | ## Usage 39 | 40 | Import into your module script: 41 | ```javascript 42 | import { WiredButton, WiredInput } from "wired-elements" 43 | ``` 44 | 45 | or 46 | 47 | ```javascript 48 | import { WiredButton } from 'wired-elements/lib/wired-button.js'; 49 | import { WiredInput } from 'wired-elements/lib/wired-input.js'; 50 | ``` 51 | 52 | #### Use it in your HTML: 53 | ```html 54 | 55 | Click Me 56 | ``` 57 | 58 | Learn about web components [here](https://developer.mozilla.org/en-US/docs/Web/Web_Components). 59 | 60 | ## Component API 61 | 62 | To view details of each component - properties, events, css-properties, etc, are provided in the [docs folder](https://github.com/rough-stuff/wired-elements/tree/master/docs). 63 | 64 | ## Demo 65 | 66 | Demo of all components is available at [wiredjs.com](https://wiredjs.com/showcase.html). 67 | 68 | ## Credits 69 | 70 | wired-elements was built using [RoughJS](https://roughjs.com/) and [Lit](https://lit.dev/). 71 | 72 | ## Contributors 73 | 74 | Become a sponsor of the [Rough suite of libraries](https://github.com/rough-stuff) 75 | 76 | ### Financial Contributors 77 | 78 | Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/rough/contribute)] 79 | 80 | #### Individuals 81 | 82 | 83 | 84 | #### Organizations 85 | 86 | Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/rough/contribute)] 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | ## License 100 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) 101 | -------------------------------------------------------------------------------- /docs/wired-button.md: -------------------------------------------------------------------------------- 1 | # wired-button 2 | Hand-drawn sketchy Button web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredButton } from 'wired-elements'; 17 | // or 18 | import { WiredButton } from 'wired-elements/lib/wired-button.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | Click Me 29 | Disabled 30 | Elevation 31 | ``` 32 | 33 | ## Properties 34 | 35 | **elevation** - Number between 1 and 5 (inclusive) that gives the button a sketchy height. Default value is 1. 36 | 37 | **disabled** - disables the button. Default value is false. 38 | 39 | ## Events 40 | 41 | **click** - When button is clicked/submitted 42 | 43 | 44 | ## License 45 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) 46 | -------------------------------------------------------------------------------- /docs/wired-calendar.md: -------------------------------------------------------------------------------- 1 | ![wired calendar][sample] 2 | 3 | # wired-calendar 4 | 5 | Calendar control with a hand-drawn, wireframe like, style. 6 | 7 | For demo and view the complete set of wired-elements: [wiredjs.com](http://wiredjs.com/) 8 | 9 | ## Usage 10 | 11 | Add wired-elements to your JavaScript project: 12 | ``` 13 | npm i wired-elements 14 | ``` 15 | 16 | Import module in your code: 17 | 18 | ```javascript 19 | import { WiredCalendar } from 'wired-elements/lib/wired-calendar.js'; 20 | ``` 21 | 22 | Or load directly into your HTML page: 23 | ```html 24 | 25 | ``` 26 | 27 | Use it in your HTML: 28 | ```html 29 | 30 | 31 | ``` 32 | 33 | ## Properties 34 | 35 | **elevation** - Numerical number between 1-5 (inclusive) - sets the elevation of the card. Default is 1. 36 | 37 | **selected** - Optional string value that will be parsed as Date. Pre selects a date highlighted in the calendar. 38 | 39 | **firstdate** - Optional string value that will be parsed as Date. Lower limit of valid dates. 40 | 41 | **lastdate** - Optional string value that will be parsed as Date. Higher limit of valid dates. 42 | 43 | **locale** - Optional string value to set locale used ONLY FOR RENDERING headers in calendar. Default to browser locale. Note: All internal and external dates handling are not affected by locale. 44 | 45 | **disabled** - Boolean value that disables the calendar selector. Default value is false. 46 | 47 | **initials** - Boolean value to use initials in weekdays names. Default value is false. 48 | 49 | **value** - javascript object that contains the selected Date object and the 50 | corresponding formatted text. 51 | 52 | **format** - gets/sets the javascript function to format a Date object into a 53 | formatted text. 54 | 55 | 56 | ## Custom CSS Variables 57 | 58 | **--wired-calendar-bg** Background color of the calendar. Default white. 59 | 60 | **--wired-calendar-color** Calendar sketch line color. Default black. 61 | 62 | **--wired-calendar-selected-color** Selected date sketch line color. Default red. 63 | 64 | **--wired-calendar-dimmed-color** Font color days not belonging to calendar actual month. Default gray. 65 | 66 | ## Events 67 | **selected** event fired when a date is selected by the user. 68 | 69 | 70 | ## License 71 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) 72 | 73 | #### Contributor 74 | 75 | Eduardo Martinez 76 | 77 | [sample]: https://elingerojo.github.io/wired-elements/images/WiredCalendarSample.GIF "Sample calendar" 78 | -------------------------------------------------------------------------------- /docs/wired-card.md: -------------------------------------------------------------------------------- 1 | # wired-card 2 | 3 | wired-card is a container for other web elements - with a hand-drawn, wireframe like, look. 4 | 5 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 6 | 7 | ## Usage 8 | 9 | Add wired-elements to your JavaScript project: 10 | ``` 11 | npm i wired-elements 12 | ``` 13 | 14 | Import module in your code: 15 | 16 | ```javascript 17 | import { WiredCard } from 'wired-elements'; 18 | // or 19 | import { WiredCard } from 'wired-elements/lib/wired-card.js'; 20 | ``` 21 | 22 | Or load directly into your HTML page: 23 | ```html 24 | 25 | ``` 26 | 27 | Use it in your HTML: 28 | ```html 29 | 30 |

Elevation: 1

31 |
32 | 33 | 34 |

Elevation: 3

35 |
36 | ``` 37 | 38 | ## Properties 39 | 40 | **elevation** - Numerical number between 1-5 (inclusive) - sets the elevation of the card. Default is 1. 41 | 42 | **fill** - A color to fill the background of the card in a sketchy format 43 | 44 | ## Methods 45 | 46 | **requestUpdate()** - When dynamically adding content to the card, call this method to recompute the boundaries of the card. 47 | 48 | ## License 49 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) 50 | -------------------------------------------------------------------------------- /docs/wired-checkbox.md: -------------------------------------------------------------------------------- 1 | # wired-checkbox 2 | Hand-drawn sketchy checkbox web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredCheckbox } from 'wired-elements'; 17 | // or 18 | import { WiredCheckbox } from 'wired-elements/lib/wired-checkbox.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | Checkbox One 29 | Checkbox Two 30 | Disabled checkbox 31 | ``` 32 | 33 | ## Properties 34 | 35 | **checked** - Checked state (boolean). Default is false. 36 | 37 | **disabled** - Disables the checkbox. Default value is false. 38 | 39 | ## Custom CSS Properties 40 | 41 | **--wired-checkbox-icon-color** Color of the checkbox. Default is *currentColor*. 42 | 43 | ## Events 44 | **change** event fired when state of the checkbox changes, i.e. the user checks/unchecks the box. 45 | 46 | ## License 47 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) 48 | -------------------------------------------------------------------------------- /docs/wired-combo.md: -------------------------------------------------------------------------------- 1 | # wired-combo 2 | 3 | Combobox control - similar to a native browser select element; with a hand-drawn, wireframe like, style. 4 | 5 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 6 | 7 | ## Usage 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredCombo } from 'wired-elements'; 17 | // or 18 | import { WiredCombo } from 'wired-elements/lib/wired-combo.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | 29 | Number One 30 | Number Two 31 | Number Three 32 | 33 | ``` 34 | 35 | ## Properties 36 | 37 | **disabled** - disables the combo selector. Default value is false. 38 | 39 | **selected** - Value of the selected wired-item. 40 | 41 | ## Custom CSS Variables 42 | 43 | **--wired-combo-popup-bg** Background color of the dropdown when combo selector is open. 44 | 45 | **--wired-item-selected-bg** Background color of the selected item 46 | 47 | ## Events 48 | **selected** event fired when an item is selected by the user. 49 | 50 | ## License 51 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-dialog.md: -------------------------------------------------------------------------------- 1 | # wired-dialog 2 | Hand-drawn sketchy Dialog web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredDialog } from 'wired-elements'; 17 | // or 18 | import { WiredDialog } from 'wired-elements/lib/wired-dialog.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | 28 | ```html 29 | 30 |

31 | Dialog content here 32 |

33 |
34 | Close dialog 35 |
36 |
37 | ``` 38 | 39 | ## Properties 40 | 41 | **elevation** - Number between 1 and 5 (inclusive) that gives the sketchy link underline a height. Default value is 1. 42 | 43 | **open** - Boolean value telling dialog if it's showing or not. 44 | 45 | ## Custom CSS Properties 46 | 47 | **--wired-dialog-z-index** - Sets the `z-index` of the dialog 48 | 49 | 50 | ## License 51 | [MIT License](https://github.com/wiredjs/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-divider.md: -------------------------------------------------------------------------------- 1 | # wired-divider 2 | Hand-drawn sketchy line to divide sections 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | AAdd wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredDivider } from 'wired-elements'; 17 | // or 18 | import { WiredDivider } from 'wired-elements/lib/wired-divider.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

29 | 30 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

31 | ``` 32 | 33 | ## Properties 34 | 35 | **elevation** - Number between 1 and 5 (inclusive) represents number of lines drawn. Default value is 1. 36 | 37 | 38 | ## License 39 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-fab.md: -------------------------------------------------------------------------------- 1 | # wired-fab 2 | Hand-drawn sketchy Floating Action Button (FAB) 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | AAdd wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredFab } from 'wired-elements'; 17 | // or 18 | import { WiredFab } from 'wired-elements/lib/wired-fab.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | 29 | favorite 30 | 31 | 32 | close 33 | 34 | ``` 35 | 36 | ## Properties 37 | 38 | **disabled** - disables the button. Default value is false. 39 | 40 | ## Custom CSS Properties 41 | 42 | **--wired-fab-bg-color** - Background color of the fab. Default value is #018786. Foreground color is set by setting the **color** css property. 43 | 44 | ## Events 45 | 46 | **click** - When button is clicked/submitted 47 | 48 | ## License 49 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-icon-button.md: -------------------------------------------------------------------------------- 1 | # wired-icon-button 2 | 3 | This is a hand-drawn sketchy round button with an image placed at the center. Image could also be in icon, like [@material/mwc-icon](https://www.npmjs.com/package/@material/mwc-icon). 4 | 5 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 6 | 7 | ## Usage 8 | 9 | Add wired-elements to your JavaScript project: 10 | ``` 11 | npm i wired-elements 12 | ``` 13 | 14 | Import module in your code: 15 | 16 | ```javascript 17 | import { WiredIconButton } from 'wired-elements'; 18 | // or 19 | import { WiredIconButton } from 'wired-elements/lib/wired-icon-button.js'; 20 | ``` 21 | 22 | Or load directly into your HTML page: 23 | ```html 24 | 25 | ``` 26 | 27 | Use it in your HTML: 28 | ```html 29 | 30 | favorite 31 | 32 | 33 | favorite 34 | 35 | ``` 36 | 37 | ## Properties 38 | 39 | **disabled** - disables the button. Default value is false. 40 | 41 | ## Custom CSS Variables 42 | 43 | **--wired-icon-size** Numeric size of the icon. Default is 24 (px). 44 | 45 | **--wired-icon-bg-color** Background color. 46 | 47 | ## Events 48 | 49 | **click** - When button is clicked/submitted 50 | 51 | ## License 52 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-image.md: -------------------------------------------------------------------------------- 1 | # wired-image 2 | 3 | wired-image displays an image and draws a sketchy border around it. 4 | 5 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 6 | 7 | ## Usage 8 | 9 | Add wired-elements to your JavaScript project: 10 | ``` 11 | npm i wired-elements 12 | ``` 13 | 14 | Import module in your code: 15 | 16 | ```javascript 17 | import { WiredImage } from 'wired-elements'; 18 | // or 19 | import { WiredImage } from 'wired-elements/lib/wired-image.js'; 20 | ``` 21 | 22 | Or load directly into your HTML page: 23 | ```html 24 | 25 | ``` 26 | 27 | Use it in your HTML: 28 | ```html 29 | 30 | 31 | ``` 32 | 33 | ## Properties 34 | 35 | **src** - URL of the image. 36 | 37 | **elevation** - Numerical number between 1-5 (inclusive) - sets the elevation of the card. Default is 1. 38 | 39 | ## License 40 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-input.md: -------------------------------------------------------------------------------- 1 | # wired-input 2 | Hand-drawn sketchy text input web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | AAdd wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredInput } from 'wired-elements'; 17 | // or 18 | import { WiredInput } from 'wired-elements/lib/wired-input.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | 29 | 30 | 31 | 32 | ``` 33 | 34 | ## Properties 35 | 36 | **placeholder** - Placeholder text for the input. 37 | 38 | **disabled** - disables the control 39 | 40 | **type** - Input type e.g. password 41 | 42 | **value** - Value of the text. 43 | 44 | ## Events 45 | 46 | Fires all events a standard `` element fires. 47 | 48 | ## License 49 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-item.md: -------------------------------------------------------------------------------- 1 | # wired-item 2 | 3 | wired-item is a list item used by Wired components like wired-combo and wired-list-box 4 | 5 | [wiredjs.com](https://wiredjs.com) 6 | 7 | ## License 8 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-link.md: -------------------------------------------------------------------------------- 1 | # wired-link 2 | Hand-drawn sketchy Anchor/Link web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredLink } from 'wired-elements'; 17 | // or 18 | import { WiredLink } from 'wired-elements/lib/wired-link.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | Learn more 29 | Elevation 30 | ``` 31 | 32 | ## Properties 33 | 34 | **elevation** - Number between 1 and 5 (inclusive) that gives the sketchy link underline a height. Default value is 1. 35 | 36 | **href** - URL of the page to link to 37 | 38 | **target** - Similar to the target property of ``, the target window of this link. 39 | 40 | ## Custom CSS Properties 41 | 42 | **--wired-link-decoration-color** - Color of the sketchy underline of the link. Default value is blue. Foreground color is set by setting the **color** css property. 43 | 44 | 45 | ## License 46 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-listbox.md: -------------------------------------------------------------------------------- 1 | # wired-listbox 2 | 3 | A listbox control with Wired hand-drawn styling. The selected item is highlighted. Can be vertical (default) or horizontal. 4 | 5 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 6 | 7 | ## Usage 8 | 9 | Add wired-elements to your JavaScript project: 10 | ``` 11 | npm i wired-elements 12 | ``` 13 | 14 | Import module in your code: 15 | 16 | ```javascript 17 | import { WiredListbox } from 'wired-elements'; 18 | // or 19 | import { WiredListbox } from 'wired-elements/lib/wired-listbox.js'; 20 | ``` 21 | 22 | Or load directly into your HTML page: 23 | ```html 24 | 25 | ``` 26 | 27 | Use it in your HTML: 28 | ```html 29 | 30 | Number One 31 | Number Two 32 | Number Three 33 | 34 | 35 | 37 | Number One 38 | Number Two 39 | Number Three 40 | 41 | ``` 42 | 43 | ## Properties 44 | 45 | **horizontal** - Boolean indicated if the items are layed out horizontally. Default is false. 46 | 47 | **selected** - Value of the selected item. 48 | 49 | ## Custom CSS Variables 50 | 51 | **--wired-item-selected-bg** Background color of the selected item. 52 | 53 | **--wired-item-selected-color** Text color of the selected item. 54 | 55 | 56 | ## Events 57 | **selected** event fired when an item is selected by the user. 58 | 59 | ## License 60 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-progress-ring.md: -------------------------------------------------------------------------------- 1 | # wired-progress-ring 2 | Hand-drawn sketchy progress-ring web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredProgressRing } from 'wired-elements'; 17 | // or 18 | import { WiredProgressRing } from 'wired-elements/lib/wired-progress-ring.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | 29 | 30 | ``` 31 | 32 | ## Properties 33 | 34 | **value** - Numeric value of the progress. 35 | 36 | **min** - Minimum value. Default is 0. 37 | 38 | **max** - Maximum value. Default is 100. 39 | 40 | **hideLabel** - Hide the label in the center of the ring. This label shows the current value. Default is `false`. 41 | 42 | **showLabelAsPercent** - When showing the label, show the value as a percentage. Default value is `false`. 43 | 44 | **precision** - When showing the label as a percentage, this value can be set to specify the precision to round the value to. By default, the value rounded to a whole number. 45 | 46 | ## Custom CSS Variables 47 | 48 | **--wired-progress-color** Color of the progress bar. Default is `blue`. 49 | 50 | The font and color of the label can be set the by styling the `wired-progress-ring` element. 51 | 52 | ## License 53 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-progress.md: -------------------------------------------------------------------------------- 1 | # wired-progress 2 | Hand-drawn sketchy progress bar web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredProgress } from 'wired-elements'; 17 | // or 18 | import { WiredProgress } from 'wired-elements/lib/wired-progress.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | 29 | 30 | ``` 31 | 32 | ## Properties 33 | 34 | **value** - Numeric value of the progress. 35 | 36 | **min** - Minimum value. Default is 0. 37 | 38 | **max** - Maximum value. Default is 100. 39 | 40 | **percentage** - Boolean indicating if the label should show a % symbol. 41 | 42 | ## Custom CSS Variables 43 | 44 | **--wired-progress-label-color** Color of the label. Default is *black*. 45 | 46 | **--wired-progress-label-background** Backgroind of label. Default is *rgba(255,255,255,0.9)*. 47 | 48 | **--wired-progress-font-size** Font size of the label. Default is *14px* 49 | 50 | **--wired-progress-color** Color of the progress bar. Default is *rgba(0, 0, 200, 0.8)*. 51 | 52 | ## License 53 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-radio-group.md: -------------------------------------------------------------------------------- 1 | # wired-radio-group 2 | Allows user to select at most one radio button from a set. Works with `wired-radio`. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredRadioGroup, WiredRadio } from 'wired-elements'; 17 | // or 18 | import { WiredRadioGroup } from 'wired-elements/lib/wired-radio-group.js'; 19 | ``` 20 | 21 | Use it in your HTML: 22 | ```html 23 | 24 | One 25 | Two 26 | Three 27 | Four 28 | 29 | ``` 30 | 31 | ## Properties 32 | 33 | **selected** - Named of the selected radio button. 34 | 35 | ## Events 36 | 37 | **selected** Event fired when user changes selection 38 | 39 | ## License 40 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-radio.md: -------------------------------------------------------------------------------- 1 | # wired-radio 2 | Hand-drawn sketchy radio button web component. Usually used with `wired-radio-group`. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | AAdd wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredRadio } from 'wired-elements'; 17 | // or 18 | import { WiredRadio } from 'wired-elements/lib/wired-radio.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | Radio One 29 | Radio Two 30 | Disabled Radio 31 | ``` 32 | 33 | ## Properties 34 | 35 | **checked** - Checked state (boolean) of the radio button. Default is false. 36 | 37 | **disabled** - disables the radio button. Default value is false. 38 | 39 | **text** - Text associated with the radio button. 40 | 41 | **name** - A name associated with the radio inside a radio-group. 42 | 43 | ## Custom CSS Variables 44 | 45 | **--wired-radio-icon-color** Color of the radio button. Default is *currentColor*. 46 | 47 | ## Events 48 | **change** - event fired when state of the radio changes, i.e. the user checks/unchecks the radio. 49 | 50 | ## License 51 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-search-input.md: -------------------------------------------------------------------------------- 1 | # wired-search-input 2 | Hand-drawn sketchy search text input web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | AAdd wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredSearchInput } from 'wired-elements'; 17 | // or 18 | import { WiredSearchInput } from 'wired-elements/lib/wired-search-input.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | 29 | 30 | ``` 31 | 32 | ## Properties 33 | 34 | **placeholder** - Placeholder text for the input. 35 | 36 | **disabled** - disables the control 37 | 38 | **value** - Value of the text. 39 | 40 | ## Events 41 | 42 | Fires all events a standard `` element fires. 43 | 44 | ## License 45 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-slider.md: -------------------------------------------------------------------------------- 1 | # wired-slider 2 | 3 | Hand-drawn sketchy slider web component which allows user to select a value from a range by moving the slider thumb. 4 | 5 | Range can be set using the min, max value. Default range is 0-100. 6 | 7 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 8 | 9 | ## Usage 10 | 11 | Add wired-elements to your JavaScript project: 12 | ``` 13 | npm i wired-elements 14 | ``` 15 | 16 | Import module in your code: 17 | 18 | ```javascript 19 | import { WiredSlider } from 'wired-elements'; 20 | // or 21 | import { WiredSlider } from 'wired-elements/lib/wired-slider.js'; 22 | ``` 23 | 24 | Or load directly into your HTML page: 25 | ```html 26 | 27 | ``` 28 | 29 | Use it in your HTML: 30 | ```html 31 | 32 | 33 | 34 | ``` 35 | 36 | ## Properties 37 | 38 | **value** - Numeric value of the slider. 39 | 40 | **min** - Minimum value of the slider. Default is 0. 41 | 42 | **max** - Maximum value of the slider. Default ia 100. 43 | 44 | ## Custom CSS Variables 45 | 46 | **--wired-slider-knob-zero-color** Color of the knob when the value is at minimum. 47 | 48 | **--wired-slider-knob-color** Color of the knob when the value is NOT at minimum. 49 | 50 | **--wired-slider-bar-color** Color of the bar on which the knob slides. 51 | 52 | ## Events 53 | 54 | **change** event fired when the user changes the slider value. 55 | 56 | ## License 57 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-spinner.md: -------------------------------------------------------------------------------- 1 | # wired-spinner 2 | Hand-drawn sketchy spinner to show progress or a pending task. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredSpinner } from 'wired-elements'; 17 | // or 18 | import { WiredSpinner } from 'wired-elements/lib/wired-spinner.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | 29 | 30 | ``` 31 | 32 | ## Properties 33 | 34 | **spinning** - Is the spinner spinning. Default is *false*. 35 | 36 | **duration** - Time in milliseconds to complete one complete spin. Default is *1500* 37 | 38 | ## Styling 39 | 40 | Change the **color** style of the spinner element to change its color. 41 | 42 | ## License 43 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-tabs.md: -------------------------------------------------------------------------------- 1 | # wired-tabs 2 | Hand-drawn sketchy Tabs web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredTabs, WiredTab } from 'wired-elements'; 17 | // or 18 | import { WiredTabs, WiredTab } from 'wired-elements/lib/wired-tabs.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | 29 | 30 |

Card 1

31 |

32 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore 33 | magna aliqua. 34 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 35 |

36 |
37 | 38 |

Card 2

39 |

40 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore 41 | magna aliqua. 42 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 43 |

44 |
45 | 46 |

Card 3

47 |

48 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore 49 | magna aliqua. 50 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 51 |

52 |
53 |
54 | ``` 55 | 56 | ## WiredTabs Properties 57 | 58 | **selected** - Name of the currently selected tab 59 | 60 | ## WiredTab Properties 61 | 62 | **name** - Unique identifier for that tab. Used for selection. 63 | 64 | **label** - Text to show in the tab. Defaults to the **name** property. 65 | 66 | ## Custom CSS Variables 67 | 68 | **--wired-item-selected-bg** Background color of the selected tab. 69 | 70 | **--wired-item-selected-color** Text color of the selected tab. 71 | 72 | ## License 73 | [MIT License](https://github.com/rough-stuff/wired-elements/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) -------------------------------------------------------------------------------- /docs/wired-textarea.md: -------------------------------------------------------------------------------- 1 | # wired-textarea 2 | Hand-drawn sketchy multi-line text input web component. 3 | 4 | For demo and view the complete set of wired-elememts: [wiredjs.com](http://wiredjs.com/) 5 | 6 | ## Usage 7 | 8 | Add wired-elements to your JavaScript project: 9 | ``` 10 | npm i wired-elements 11 | ``` 12 | 13 | Import module in your code: 14 | 15 | ```javascript 16 | import { WiredTextarea } from 'wired-elements'; 17 | // or 18 | import { WiredTextarea } from 'wired-elements/lib/wired-textarea.js'; 19 | ``` 20 | 21 | Or load directly into your HTML page: 22 | ```html 23 | 24 | ``` 25 | 26 | Use it in your HTML: 27 | ```html 28 | 29 | ``` 30 | 31 | ## Properties 32 | 33 | **rows** - Initial number of rows in textarea. 34 | 35 | **maxrows** - Max number of rows textarea grows to. Then scrollbars appear. 36 | 37 | **value** - Text value. 38 | 39 | **disabled** - Disabled the control. 40 | 41 | **placeholder** - Placeholder text for the input. 42 | 43 | ## Events 44 | 45 | Fires all the events the native ` 29 | 30 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/generate/navigation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/generate/toggle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/lib/WiredMatIcon.d.ts: -------------------------------------------------------------------------------- 1 | import { LitElement, TemplateResult } from 'lit-element'; 2 | import 'wired-icon'; 3 | export declare class WiredMatIcon extends LitElement { 4 | static get styles(): import("lit-element").CSSResult; 5 | private _icon; 6 | private _path; 7 | config: Object; 8 | get icon(): string; 9 | set icon(value: string); 10 | render(): TemplateResult; 11 | } 12 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/lib/WiredMatIcon.js: -------------------------------------------------------------------------------- 1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 5 | return c > 3 && r && Object.defineProperty(target, key, r), r; 6 | }; 7 | var __metadata = (this && this.__metadata) || function (k, v) { 8 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 9 | }; 10 | import { property, LitElement, html, css } from 'lit-element'; 11 | import 'wired-icon'; 12 | import { iconsetLoader } from './iconset'; 13 | import { ICON_SET } from './iconset/iconset-full'; 14 | const findSvgPath = iconsetLoader(ICON_SET); 15 | export class WiredMatIcon extends LitElement { 16 | constructor() { 17 | super(...arguments); 18 | this._icon = ''; 19 | this._path = ''; 20 | this.config = {}; 21 | } 22 | static get styles() { 23 | return css ` 24 | :host { 25 | display: block; 26 | } 27 | `; 28 | } 29 | get icon() { 30 | return this._icon; 31 | } 32 | set icon(value) { 33 | this._icon = value; 34 | this._path = findSvgPath(this.icon); 35 | } 36 | render() { 37 | return html ` 38 | 39 | 40 | ${this.icon} 41 | 42 | 43 | 44 | `; 45 | } 46 | } 47 | __decorate([ 48 | property({ type: Object, reflect: true }), 49 | __metadata("design:type", Object) 50 | ], WiredMatIcon.prototype, "config", void 0); 51 | __decorate([ 52 | property({ type: String, reflect: true }), 53 | __metadata("design:type", String), 54 | __metadata("design:paramtypes", [String]) 55 | ], WiredMatIcon.prototype, "icon", null); 56 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/lib/iconset/iconset-full.d.ts: -------------------------------------------------------------------------------- 1 | import { SvgIconSet } from './index'; 2 | export declare const ICON_SET: SvgIconSet; 3 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/lib/iconset/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * key is the name of the icon 3 | * value is the path for the svg (d attribute on path svg element) 4 | */ 5 | export declare type SvgIconSet = { 6 | [key: string]: string; 7 | }; 8 | /** 9 | * Returns a utility function to browse an iconset 10 | * @param ICON_SET the iconset to be used 11 | */ 12 | export declare const iconsetLoader: (ICON_SET: SvgIconSet) => (iconName?: string | undefined) => string; 13 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/lib/iconset/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns a utility function to browse an iconset 3 | * @param ICON_SET the iconset to be used 4 | */ 5 | export const iconsetLoader = (ICON_SET) => (iconName) => { 6 | if (iconName) { 7 | return ICON_SET[iconName]; 8 | } 9 | return ''; 10 | }; 11 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/lib/wired-mat-icon.d.ts: -------------------------------------------------------------------------------- 1 | import { WiredMatIcon } from './WiredMatIcon'; 2 | export { WiredMatIcon }; 3 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/lib/wired-mat-icon.js: -------------------------------------------------------------------------------- 1 | import { customElement } from 'lit-element'; 2 | import { WiredMatIcon } from './WiredMatIcon'; 3 | window.customElements.get('wired-mat-icon') || customElement('wired-mat-icon')(WiredMatIcon); 4 | export { WiredMatIcon }; 5 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wired-mat-icon", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "lit-element": { 8 | "version": "2.3.1", 9 | "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-2.3.1.tgz", 10 | "integrity": "sha512-tOcUAmeO3BzwiQ7FGWdsshNvC0HVHcTFYw/TLIImmKwXYoV0E7zCBASa8IJ7DiP4cen/Yoj454gS0qqTnIGsFA==", 11 | "requires": { 12 | "lit-html": "^1.1.1" 13 | }, 14 | "dependencies": { 15 | "lit-html": { 16 | "version": "1.2.1", 17 | "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.2.1.tgz", 18 | "integrity": "sha512-GSJHHXMGLZDzTRq59IUfL9FCdAlGfqNp/dEa7k7aBaaWD+JKaCjsAk9KYm2V12ItonVaYx2dprN66Zdm1AuBTQ==" 19 | } 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wired-mat-icon", 3 | "version": "0.0.1", 4 | "description": "Sketchy material icon component and iconset", 5 | "keywords": [ 6 | "Wired", 7 | "material", 8 | "icon" 9 | ], 10 | "author": "apennamen ", 11 | "homepage": "https://github.com/wiredjs/wired-elements/tree/master/packages/wired-mat-icon#readme", 12 | "license": "MIT", 13 | "main": "lib/wired-mat-icon.js", 14 | "directories": { 15 | "lib": "lib", 16 | "test": "__tests__" 17 | }, 18 | "files": [ 19 | "lib" 20 | ], 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/wiredjs/wired-elements.git" 24 | }, 25 | "scripts": { 26 | "build": "rm -rf lib && tsc", 27 | "tsc:watch": "tsc --watch", 28 | "generate": "serve ./generate" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/wiredjs/wired-elements/issues" 32 | }, 33 | "dependencies": { 34 | "lit-element": "^2.3.1", 35 | "wired-icon": "^0.0.1" 36 | }, 37 | "devDependencies": { 38 | "serve": "^11.3.0" 39 | } 40 | } -------------------------------------------------------------------------------- /experimental/wired-mat-icon/src/WiredMatIcon.ts: -------------------------------------------------------------------------------- 1 | import { property, LitElement, html, TemplateResult, css } from 'lit-element'; 2 | import 'wired-icon'; 3 | import { iconsetLoader } from './iconset'; 4 | import { ICON_SET } from './iconset/iconset-full'; 5 | 6 | const findSvgPath = iconsetLoader(ICON_SET); 7 | 8 | export class WiredMatIcon extends LitElement { 9 | static get styles() { 10 | return css` 11 | :host { 12 | display: block; 13 | } 14 | `; 15 | } 16 | 17 | private _icon: string = ''; 18 | private _path: string = ''; 19 | 20 | @property({ type: Object, reflect: true }) config: Object = {}; 21 | 22 | @property({ type: String, reflect: true }) 23 | get icon(): string { 24 | return this._icon; 25 | } 26 | 27 | set icon(value: string) { 28 | this._icon = value; 29 | this._path = findSvgPath(this.icon); 30 | } 31 | 32 | render(): TemplateResult { 33 | return html` 34 | 35 | 36 | ${this.icon} 37 | 38 | 39 | 40 | `; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/src/iconset/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * key is the name of the icon 3 | * value is the path for the svg (d attribute on path svg element) 4 | */ 5 | export type SvgIconSet = { 6 | [key: string]: string 7 | }; 8 | 9 | /** 10 | * Returns a utility function to browse an iconset 11 | * @param ICON_SET the iconset to be used 12 | */ 13 | export const iconsetLoader = (ICON_SET: SvgIconSet) => (iconName?: string): string => { 14 | if (iconName) { 15 | return ICON_SET[iconName]; 16 | } 17 | return ''; 18 | } 19 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/src/wired-mat-icon.ts: -------------------------------------------------------------------------------- 1 | import { customElement } from 'lit-element'; 2 | import { WiredMatIcon } from './WiredMatIcon'; 3 | 4 | window.customElements.get('wired-mat-icon') || customElement('wired-mat-icon')(WiredMatIcon); 5 | 6 | export { WiredMatIcon }; 7 | -------------------------------------------------------------------------------- /experimental/wired-mat-icon/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./lib" 5 | }, 6 | "include": [ 7 | "./src" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wired-elements", 3 | "version": "3.0.0-rc.7", 4 | "description": "Collection of hand-drawn sketchy web components", 5 | "type": "module", 6 | "main": "lib/wired-elements.js", 7 | "module": "lib/wired-elements.js", 8 | "types": "lib/wired-elements.d.ts", 9 | "scripts": { 10 | "build": "rm -rf lib && tsc" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/wiredjs/wired-elements.git" 15 | }, 16 | "keywords": [ 17 | "webcomponent", 18 | "web component", 19 | "rough", 20 | "sketchy", 21 | "hand-drawn", 22 | "hand drawn", 23 | "wireframe" 24 | ], 25 | "author": "Preet Shihn (https://twitter.com/preetster)", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/wiredjs/wired-elements/issues" 29 | }, 30 | "homepage": "https://github.com/wiredjs/wired-elements#readme", 31 | "devDependencies": { 32 | "tslint": "^6.1.3", 33 | "typescript": "^4.2.4" 34 | }, 35 | "dependencies": { 36 | "lit": "^2.0.0-rc.1", 37 | "roughjs": "^4.3.1" 38 | } 39 | } -------------------------------------------------------------------------------- /src/wired-base.ts: -------------------------------------------------------------------------------- 1 | import { LitElement, css, PropertyValues } from 'lit'; 2 | import { query } from 'lit/decorators.js'; 3 | 4 | export type Point = [number, number]; 5 | 6 | export const BaseCSS = css` 7 | :host { 8 | opacity: 0; 9 | } 10 | :host(.wired-rendered) { 11 | opacity: 1; 12 | } 13 | #overlay { 14 | position: absolute; 15 | top: 0; 16 | left: 0; 17 | right: 0; 18 | bottom: 0; 19 | pointer-events: none; 20 | } 21 | svg { 22 | display: block; 23 | } 24 | path { 25 | stroke: currentColor; 26 | stroke-width: 0.7; 27 | fill: transparent; 28 | } 29 | .hidden { 30 | display: none !important; 31 | } 32 | `; 33 | 34 | export abstract class WiredBase extends LitElement { 35 | @query('svg') protected svg?: SVGSVGElement; 36 | 37 | protected lastSize: Point = [0, 0]; 38 | protected seed = Math.floor(Math.random() * 2 ** 31); 39 | 40 | updated(_changed?: PropertyValues) { 41 | this.wiredRender(); 42 | } 43 | 44 | wiredRender(force = false) { 45 | if (this.svg) { 46 | const size = this.canvasSize(); 47 | if ((!force) && (size[0] === this.lastSize[0]) && (size[1] === this.lastSize[1])) { 48 | return; 49 | } 50 | while (this.svg.hasChildNodes()) { 51 | this.svg.removeChild(this.svg.lastChild!); 52 | } 53 | this.svg.setAttribute('width', `${size[0]}`); 54 | this.svg.setAttribute('height', `${size[1]}`); 55 | this.draw(this.svg, size); 56 | this.lastSize = size; 57 | this.classList.add('wired-rendered'); 58 | } 59 | } 60 | 61 | fire(name: string, detail?: any) { 62 | fireEvent(this, name, detail); 63 | } 64 | 65 | protected abstract canvasSize(): Point; 66 | protected abstract draw(svg: SVGSVGElement, size: Point): void; 67 | } 68 | 69 | export function randomSeed(): number { 70 | return Math.floor(Math.random() * 2 ** 31); 71 | } 72 | 73 | export function fireEvent(e: HTMLElement, name: string, detail?: any) { 74 | e.dispatchEvent(new CustomEvent(name, { 75 | composed: true, 76 | bubbles: true, 77 | detail 78 | })); 79 | } -------------------------------------------------------------------------------- /src/wired-button.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle, line } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-button') 7 | export class WiredButton extends WiredBase { 8 | @property({ type: Number }) elevation = 1; 9 | @property({ type: Boolean, reflect: true }) disabled = false; 10 | 11 | @query('button') private button?: HTMLButtonElement; 12 | private ro?: ResizeObserver; 13 | private roAttached = false; 14 | 15 | constructor() { 16 | super(); 17 | if ((window as any).ResizeObserver) { 18 | this.ro = new (window as any).ResizeObserver(() => { 19 | if (this.svg) { 20 | this.wiredRender(true); 21 | } 22 | }); 23 | } 24 | } 25 | 26 | static get styles(): CSSResultArray { 27 | return [ 28 | BaseCSS, 29 | css` 30 | :host { 31 | display: inline-block; 32 | font-size: 14px; 33 | } 34 | path { 35 | transition: transform 0.05s ease; 36 | } 37 | button { 38 | position: relative; 39 | user-select: none; 40 | border: none; 41 | background: none; 42 | font-family: inherit; 43 | font-size: inherit; 44 | cursor: pointer; 45 | letter-spacing: 1.25px; 46 | text-transform: uppercase; 47 | text-align: center; 48 | padding: 10px; 49 | color: inherit; 50 | outline: none; 51 | } 52 | button[disabled] { 53 | opacity: 0.6 !important; 54 | background: rgba(0, 0, 0, 0.07); 55 | cursor: default; 56 | pointer-events: none; 57 | } 58 | button:active path { 59 | transform: scale(0.97) translate(1.5%, 1.5%); 60 | } 61 | button:focus path { 62 | stroke-width: 1.5; 63 | } 64 | button::-moz-focus-inner { 65 | border: 0; 66 | } 67 | ` 68 | ]; 69 | } 70 | 71 | render(): TemplateResult { 72 | return html` 73 | 79 | `; 80 | } 81 | 82 | focus() { 83 | if (this.button) { 84 | this.button.focus(); 85 | } else { 86 | super.focus(); 87 | } 88 | } 89 | 90 | protected canvasSize(): Point { 91 | if (this.button) { 92 | const size = this.button.getBoundingClientRect(); 93 | const elev = Math.min(Math.max(1, this.elevation), 5); 94 | const w = size.width + ((elev - 1) * 2); 95 | const h = size.height + ((elev - 1) * 2); 96 | return [w, h]; 97 | } 98 | return this.lastSize; 99 | } 100 | 101 | protected draw(svg: SVGSVGElement, size: Point) { 102 | const elev = Math.min(Math.max(1, this.elevation), 5); 103 | const s = { 104 | width: size[0] - ((elev - 1) * 2), 105 | height: size[1] - ((elev - 1) * 2) 106 | }; 107 | rectangle(svg, 0, 0, s.width, s.height, this.seed); 108 | for (let i = 1; i < elev; i++) { 109 | (line(svg, (i * 2), s.height + (i * 2), s.width + (i * 2), s.height + (i * 2), this.seed)).style.opacity = `${(75 - (i * 10)) / 100}`; 110 | (line(svg, s.width + (i * 2), s.height + (i * 2), s.width + (i * 2), i * 2, this.seed)).style.opacity = `${(75 - (i * 10)) / 100}`; 111 | (line(svg, (i * 2), s.height + (i * 2), s.width + (i * 2), s.height + (i * 2), this.seed)).style.opacity = `${(75 - (i * 10)) / 100}`; 112 | (line(svg, s.width + (i * 2), s.height + (i * 2), s.width + (i * 2), i * 2, this.seed)).style.opacity = `${(75 - (i * 10)) / 100}`; 113 | } 114 | } 115 | 116 | updated() { 117 | super.updated(); 118 | if (!this.roAttached) { 119 | this.attachResizeListener(); 120 | } 121 | } 122 | 123 | disconnectedCallback() { 124 | this.detachResizeListener(); 125 | } 126 | 127 | private attachResizeListener() { 128 | if (this.button && this.ro) { 129 | this.ro.observe(this.button); 130 | this.roAttached = true; 131 | } 132 | } 133 | 134 | private detachResizeListener() { 135 | if (this.button && this.ro) { 136 | this.ro.unobserve(this.button); 137 | } 138 | this.roAttached = false; 139 | } 140 | } -------------------------------------------------------------------------------- /src/wired-card.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle, line, hachureFill, } from './wired-lib'; 3 | import { css, TemplateResult, html, PropertyValues } from 'lit'; 4 | import { customElement, property } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-card') 7 | export class WiredCard extends WiredBase { 8 | @property({ type: Number }) elevation = 1; 9 | @property({ type: String }) fill?: string; 10 | private resizeObserver?: ResizeObserver; 11 | private windowResizeHandler?: EventListenerOrEventListenerObject; 12 | private roAttached = false; 13 | 14 | constructor() { 15 | super(); 16 | if ((window as any).ResizeObserver) { 17 | this.resizeObserver = new (window as any).ResizeObserver(() => { 18 | if (this.svg) { 19 | this.wiredRender(); 20 | } 21 | }); 22 | } 23 | } 24 | 25 | static get styles() { 26 | return [ 27 | BaseCSS, 28 | css` 29 | :host { 30 | display: inline-block; 31 | position: relative; 32 | padding: 10px; 33 | } 34 | path.cardFill { 35 | stroke-width: 3.5; 36 | stroke: var(--wired-card-background-fill); 37 | } 38 | path { 39 | stroke: var(--wired-card-background-fill, currentColor); 40 | } 41 | ` 42 | ]; 43 | } 44 | 45 | render(): TemplateResult { 46 | return html` 47 |
48 |
49 | 50 |
51 | `; 52 | } 53 | 54 | updated(changed: PropertyValues) { 55 | const force = changed.has('fill'); 56 | this.wiredRender(force); 57 | this.attachResizeListener(); 58 | } 59 | 60 | disconnectedCallback() { 61 | this.detachResizeListener(); 62 | } 63 | 64 | private attachResizeListener() { 65 | if (!this.roAttached) { 66 | if (this.resizeObserver) { 67 | this.resizeObserver.observe(this); 68 | } else if (!this.windowResizeHandler) { 69 | this.windowResizeHandler = () => this.wiredRender(); 70 | window.addEventListener('resize', this.windowResizeHandler, { passive: true }); 71 | } 72 | this.roAttached = true; 73 | } 74 | } 75 | 76 | private detachResizeListener() { 77 | if (this.resizeObserver) { 78 | this.resizeObserver.unobserve(this); 79 | } 80 | if (this.windowResizeHandler) { 81 | window.removeEventListener('resize', this.windowResizeHandler); 82 | } 83 | this.roAttached = false; 84 | } 85 | 86 | protected canvasSize(): Point { 87 | const s = this.getBoundingClientRect(); 88 | const elev = Math.min(Math.max(1, this.elevation), 5); 89 | const w = s.width + ((elev - 1) * 2); 90 | const h = s.height + ((elev - 1) * 2); 91 | return [w, h]; 92 | } 93 | 94 | protected draw(svg: SVGSVGElement, size: Point) { 95 | const elev = Math.min(Math.max(1, this.elevation), 5); 96 | const s = { 97 | width: size[0] - ((elev - 1) * 2), 98 | height: size[1] - ((elev - 1) * 2) 99 | }; 100 | if (this.fill && this.fill.trim()) { 101 | const fillNode = hachureFill([ 102 | [2, 2], 103 | [s.width - 4, 2], 104 | [s.width - 2, s.height - 4], 105 | [2, s.height - 4] 106 | ], this.seed); 107 | fillNode.classList.add('cardFill'); 108 | svg.style.setProperty('--wired-card-background-fill', this.fill.trim()); 109 | svg.appendChild(fillNode); 110 | } 111 | rectangle(svg, 2, 2, s.width - 4, s.height - 4, this.seed); 112 | for (let i = 1; i < elev; i++) { 113 | (line(svg, (i * 2), s.height - 4 + (i * 2), s.width - 4 + (i * 2), s.height - 4 + (i * 2), this.seed)).style.opacity = `${(85 - (i * 10)) / 100}`; 114 | (line(svg, s.width - 4 + (i * 2), s.height - 4 + (i * 2), s.width - 4 + (i * 2), i * 2, this.seed)).style.opacity = `${(85 - (i * 10)) / 100}`; 115 | (line(svg, (i * 2), s.height - 4 + (i * 2), s.width - 4 + (i * 2), s.height - 4 + (i * 2), this.seed)).style.opacity = `${(85 - (i * 10)) / 100}`; 116 | (line(svg, s.width - 4 + (i * 2), s.height - 4 + (i * 2), s.width - 4 + (i * 2), i * 2, this.seed)).style.opacity = `${(85 - (i * 10)) / 100}`; 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /src/wired-checkbox.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle, line, svgNode } from './wired-lib'; 3 | import { css, TemplateResult, html } from 'lit'; 4 | import { customElement, property, query, state } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-checkbox') 7 | export class WiredCheckbox extends WiredBase { 8 | @property({ type: Boolean }) checked = false; 9 | @property({ type: Boolean, reflect: true }) disabled = false; 10 | @state() private focused = false; 11 | 12 | @query('input') private input?: HTMLInputElement; 13 | 14 | private svgCheck?: SVGElement; 15 | 16 | static get styles() { 17 | return [ 18 | BaseCSS, 19 | css` 20 | :host { 21 | display: inline-block; 22 | font-family: inherit; 23 | } 24 | :host([disabled]) { 25 | opacity: 0.6 !important; 26 | cursor: default; 27 | pointer-events: none; 28 | } 29 | :host([disabled]) svg { 30 | background: rgba(0, 0, 0, 0.07); 31 | } 32 | 33 | #container { 34 | display: flex; 35 | flex-direction: row; 36 | position: relative; 37 | user-select: none; 38 | min-height: 24px; 39 | cursor: pointer; 40 | } 41 | span { 42 | margin-left: 1.5ex; 43 | line-height: 24px; 44 | } 45 | input { 46 | opacity: 0; 47 | } 48 | path { 49 | stroke: var(--wired-checkbox-icon-color, currentColor); 50 | stroke-width: var(--wired-checkbox-default-swidth, 0.7); 51 | } 52 | g path { 53 | stroke-width: 2.5; 54 | } 55 | #container.focused { 56 | --wired-checkbox-default-swidth: 1.5; 57 | } 58 | ` 59 | ]; 60 | } 61 | 62 | focus() { 63 | if (this.input) { 64 | this.input.focus(); 65 | } else { 66 | super.focus(); 67 | } 68 | } 69 | 70 | wiredRender(force = false) { 71 | super.wiredRender(force); 72 | this.refreshCheckVisibility(); 73 | } 74 | 75 | render(): TemplateResult { 76 | return html` 77 | 85 | `; 86 | } 87 | 88 | private onChange() { 89 | this.checked = this.input!.checked; 90 | this.refreshCheckVisibility(); 91 | this.fire('change', { checked: this.checked }); 92 | } 93 | 94 | protected canvasSize(): Point { 95 | return [24, 24]; 96 | } 97 | 98 | protected draw(svg: SVGSVGElement, size: Point) { 99 | rectangle(svg, 0, 0, size[0], size[1], this.seed); 100 | this.svgCheck = svgNode('g'); 101 | svg.appendChild(this.svgCheck); 102 | line(this.svgCheck, size[0] * 0.3, size[1] * 0.4, size[0] * 0.5, size[1] * 0.7, this.seed); 103 | line(this.svgCheck, size[0] * 0.5, size[1] * 0.7, size[0] + 5, -5, this.seed); 104 | } 105 | 106 | private refreshCheckVisibility() { 107 | if (this.svgCheck) { 108 | this.svgCheck.style.display = this.checked ? '' : 'none'; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /src/wired-dialog.ts: -------------------------------------------------------------------------------- 1 | import { css, TemplateResult, html, LitElement } from 'lit'; 2 | import { customElement, property, query } from 'lit/decorators.js'; 3 | import { WiredCard } from './wired-card'; 4 | 5 | @customElement('wired-dialog') 6 | export class WiredDialog extends LitElement { 7 | @property({ type: Number }) elevation = 5; 8 | @property({ type: Boolean, reflect: true }) open = false; 9 | 10 | @query('wired-card') private card?: WiredCard; 11 | 12 | static get styles() { 13 | return css` 14 | #container { 15 | position: fixed; 16 | top: 0; 17 | left: 0; 18 | right: 0; 19 | bottom: 0; 20 | pointer-events: none; 21 | z-index: var(--wired-dialog-z-index, 100); 22 | } 23 | #container::before { 24 | content: ''; 25 | position: absolute; 26 | top: 0; 27 | left: 0; 28 | right: 0; 29 | bottom: 0; 30 | background: rgba(0,0,0,0.4); 31 | opacity: 0; 32 | transition: opacity 0.5s ease; 33 | } 34 | #overlay { 35 | position: absolute; 36 | top: 0; 37 | left: 0; 38 | right: 0; 39 | bottom: 0; 40 | opacity: 0; 41 | transform: translateY(150px); 42 | transition: transform 0.5s ease, opacity 0.5s ease; 43 | } 44 | .layout.vertical { 45 | display: -ms-flexbox; 46 | display: -webkit-flex; 47 | display: flex; 48 | -ms-flex-direction: column; 49 | -webkit-flex-direction: column; 50 | flex-direction: column; 51 | } 52 | .flex { 53 | -ms-flex: 1 1 0.000000001px; 54 | -webkit-flex: 1; 55 | flex: 1; 56 | -webkit-flex-basis: 0.000000001px; 57 | flex-basis: 0.000000001px; 58 | } 59 | wired-card { 60 | display: inline-block; 61 | background: white; 62 | text-align: left; 63 | } 64 | 65 | :host([open]) #container { 66 | pointer-events: auto; 67 | } 68 | :host([open]) #container::before { 69 | opacity: 1; 70 | } 71 | :host([open]) #overlay { 72 | opacity: 1; 73 | transform: none; 74 | } 75 | `; 76 | } 77 | 78 | render(): TemplateResult { 79 | return html` 80 |
81 |
82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 | `; 90 | } 91 | 92 | updated() { 93 | if (this.card) { 94 | this.card.wiredRender(true); 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /src/wired-divider.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { line } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-divider') 7 | export class WiredDivider extends WiredBase { 8 | @property({ type: Number }) elevation = 1; 9 | 10 | private resizeObserver?: ResizeObserver; 11 | private windowResizeHandler?: EventListenerOrEventListenerObject; 12 | private roAttached = false; 13 | 14 | static get styles(): CSSResultArray { 15 | return [ 16 | BaseCSS, 17 | css` 18 | :host { 19 | display: block; 20 | position: relative; 21 | } 22 | ` 23 | ]; 24 | } 25 | 26 | render(): TemplateResult { 27 | return html``; 28 | } 29 | 30 | protected canvasSize(): Point { 31 | const size = this.getBoundingClientRect(); 32 | const elev = Math.min(Math.max(1, this.elevation), 5); 33 | return [size.width, elev * 6]; 34 | } 35 | 36 | protected draw(svg: SVGSVGElement, size: Point) { 37 | const elev = Math.min(Math.max(1, this.elevation), 5); 38 | for (let i = 0; i < elev; i++) { 39 | line(svg, 0, (i * 6) + 3, size[0], (i * 6) + 3, this.seed); 40 | } 41 | } 42 | 43 | updated() { 44 | super.updated(); 45 | this.attachResizeListener(); 46 | } 47 | 48 | disconnectedCallback() { 49 | this.detachResizeListener(); 50 | } 51 | 52 | private attachResizeListener() { 53 | if (!this.roAttached) { 54 | if (this.resizeObserver) { 55 | this.resizeObserver.observe(this); 56 | } else if (!this.windowResizeHandler) { 57 | this.windowResizeHandler = () => this.wiredRender(); 58 | window.addEventListener('resize', this.windowResizeHandler, { passive: true }); 59 | } 60 | this.roAttached = true; 61 | } 62 | } 63 | 64 | private detachResizeListener() { 65 | if (this.resizeObserver) { 66 | this.resizeObserver.unobserve(this); 67 | } 68 | if (this.windowResizeHandler) { 69 | window.removeEventListener('resize', this.windowResizeHandler); 70 | } 71 | this.roAttached = false; 72 | } 73 | } -------------------------------------------------------------------------------- /src/wired-elements.ts: -------------------------------------------------------------------------------- 1 | export * from './wired-button'; 2 | export * from './wired-card'; 3 | export * from './wired-checkbox'; 4 | export * from './wired-combo'; 5 | export * from './wired-dialog'; 6 | export * from './wired-divider'; 7 | export * from './wired-fab'; 8 | export * from './wired-icon-button'; 9 | export * from './wired-image'; 10 | export * from './wired-input'; 11 | export * from './wired-item'; 12 | export * from './wired-link'; 13 | export * from './wired-listbox'; 14 | export * from './wired-progress'; 15 | export * from './wired-radio'; 16 | export * from './wired-radio-group'; 17 | export * from './wired-search-input'; 18 | export * from './wired-slider'; 19 | export * from './wired-spinner'; 20 | export * from './wired-tab'; 21 | export * from './wired-tabs'; 22 | export * from './wired-textarea'; 23 | export * from './wired-toggle'; 24 | export * from './wired-video'; 25 | export * from './wired-progress-ring'; -------------------------------------------------------------------------------- /src/wired-fab.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { hachureEllipseFill } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-fab') 7 | export class WiredFab extends WiredBase { 8 | @property({ type: Boolean, reflect: true }) disabled = false; 9 | @query('button') private button?: HTMLButtonElement; 10 | 11 | static get styles(): CSSResultArray { 12 | return [ 13 | BaseCSS, 14 | css` 15 | :host { 16 | display: inline-block; 17 | font-size: 14px; 18 | color: #fff; 19 | } 20 | button { 21 | position: relative; 22 | user-select: none; 23 | border: none; 24 | background: none; 25 | font-family: inherit; 26 | font-size: inherit; 27 | cursor: pointer; 28 | letter-spacing: 1.25px; 29 | text-transform: uppercase; 30 | text-align: center; 31 | padding: 16px; 32 | color: inherit; 33 | outline: none; 34 | border-radius: 50%; 35 | } 36 | button[disabled] { 37 | opacity: 0.6 !important; 38 | background: rgba(0, 0, 0, 0.07); 39 | cursor: default; 40 | pointer-events: none; 41 | } 42 | button::-moz-focus-inner { 43 | border: 0; 44 | } 45 | button ::slotted(*) { 46 | position: relative; 47 | font-size: var(--wired-icon-size, 24px); 48 | transition: transform 0.2s ease, opacity 0.2s ease; 49 | opacity: 0.85; 50 | } 51 | path { 52 | stroke: var(--wired-fab-bg-color, #018786); 53 | stroke-width: 3; 54 | fill: transparent; 55 | } 56 | 57 | button:focus ::slotted(*) { 58 | opacity: 1; 59 | } 60 | button:active ::slotted(*) { 61 | opacity: 1; 62 | transform: scale(1.15); 63 | } 64 | ` 65 | ]; 66 | } 67 | 68 | render(): TemplateResult { 69 | return html` 70 | 76 | `; 77 | } 78 | 79 | protected canvasSize(): Point { 80 | if (this.button) { 81 | const size = this.button.getBoundingClientRect(); 82 | return [size.width, size.height]; 83 | } 84 | return this.lastSize; 85 | } 86 | 87 | protected draw(svg: SVGSVGElement, size: Point) { 88 | const min = Math.min(size[0], size[1]); 89 | const g = hachureEllipseFill(min / 2, min / 2, min, min, this.seed); 90 | svg.appendChild(g); 91 | } 92 | } -------------------------------------------------------------------------------- /src/wired-icon-button.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { ellipse } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-icon-button') 7 | export class WiredIconButton extends WiredBase { 8 | @property({ type: Boolean, reflect: true }) disabled = false; 9 | @query('button') private button?: HTMLButtonElement; 10 | 11 | static get styles(): CSSResultArray { 12 | return [ 13 | BaseCSS, 14 | css` 15 | :host { 16 | display: inline-block; 17 | font-size: 14px; 18 | } 19 | path { 20 | transition: transform 0.05s ease; 21 | } 22 | button { 23 | position: relative; 24 | user-select: none; 25 | border: none; 26 | background: none; 27 | font-family: inherit; 28 | font-size: inherit; 29 | cursor: pointer; 30 | letter-spacing: 1.25px; 31 | text-transform: uppercase; 32 | text-align: center; 33 | padding: 10px; 34 | color: inherit; 35 | outline: none; 36 | border-radius: 50%; 37 | } 38 | button[disabled] { 39 | opacity: 0.6 !important; 40 | background: rgba(0, 0, 0, 0.07); 41 | cursor: default; 42 | pointer-events: none; 43 | } 44 | button:active path { 45 | transform: scale(0.97) translate(1.5%, 1.5%); 46 | } 47 | button:focus path { 48 | stroke-width: 1.5; 49 | } 50 | button::-moz-focus-inner { 51 | border: 0; 52 | } 53 | button ::slotted(*) { 54 | position: relative; 55 | font-size: var(--wired-icon-size, 24px); 56 | } 57 | ` 58 | ]; 59 | } 60 | 61 | render(): TemplateResult { 62 | return html` 63 | 69 | `; 70 | } 71 | 72 | protected canvasSize(): Point { 73 | if (this.button) { 74 | const size = this.button.getBoundingClientRect(); 75 | return [size.width, size.height]; 76 | } 77 | return this.lastSize; 78 | } 79 | 80 | protected draw(svg: SVGSVGElement, size: Point) { 81 | const min = Math.min(size[0], size[1]); 82 | svg.setAttribute('width', `${min}`); 83 | svg.setAttribute('height', `${min}`); 84 | ellipse(svg, min / 2, min / 2, min, min, this.seed); 85 | } 86 | } -------------------------------------------------------------------------------- /src/wired-image.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle, line } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property } from 'lit/decorators.js'; 5 | 6 | const EMPTY_IMAGE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; 7 | 8 | @customElement('wired-image') 9 | export class WiredImage extends WiredBase { 10 | @property({ type: Number }) elevation = 1; 11 | @property({ type: String }) src: string = EMPTY_IMAGE; 12 | private resizeObserver?: ResizeObserver; 13 | private windowResizeHandler?: EventListenerOrEventListenerObject; 14 | private roAttached = false; 15 | 16 | constructor() { 17 | super(); 18 | if ((window as any).ResizeObserver) { 19 | this.resizeObserver = new (window as any).ResizeObserver(() => { 20 | if (this.svg) { 21 | this.wiredRender(); 22 | } 23 | }); 24 | } 25 | } 26 | 27 | static get styles(): CSSResultArray { 28 | return [ 29 | BaseCSS, 30 | css` 31 | :host { 32 | display: inline-block; 33 | position: relative; 34 | line-height: 1; 35 | padding: 3px; 36 | } 37 | img { 38 | display: block; 39 | box-sizing: border-box; 40 | max-width: 100%; 41 | max-height: 100%; 42 | } 43 | path { 44 | stroke-width: 1; 45 | } 46 | ` 47 | ]; 48 | } 49 | 50 | render(): TemplateResult { 51 | return html` 52 | 53 |
54 | `; 55 | } 56 | 57 | updated() { 58 | super.updated(); 59 | this.attachResizeListener(); 60 | } 61 | 62 | disconnectedCallback() { 63 | this.detachResizeListener(); 64 | } 65 | 66 | private attachResizeListener() { 67 | if (!this.roAttached) { 68 | if (this.resizeObserver && this.resizeObserver.observe) { 69 | this.resizeObserver.observe(this); 70 | } else if (!this.windowResizeHandler) { 71 | this.windowResizeHandler = () => this.wiredRender(); 72 | window.addEventListener('resize', this.windowResizeHandler, { passive: true }); 73 | } 74 | this.roAttached = true; 75 | } 76 | } 77 | 78 | private detachResizeListener() { 79 | if (this.resizeObserver && this.resizeObserver.unobserve) { 80 | this.resizeObserver.unobserve(this); 81 | } 82 | if (this.windowResizeHandler) { 83 | window.removeEventListener('resize', this.windowResizeHandler); 84 | } 85 | this.roAttached = false; 86 | } 87 | 88 | protected canvasSize(): Point { 89 | const s = this.getBoundingClientRect(); 90 | const elev = Math.min(Math.max(1, this.elevation), 5); 91 | const w = s.width + ((elev - 1) * 2); 92 | const h = s.height + ((elev - 1) * 2); 93 | return [w, h]; 94 | } 95 | 96 | protected draw(svg: SVGSVGElement, size: Point) { 97 | const elev = Math.min(Math.max(1, this.elevation), 5); 98 | const s = { 99 | width: size[0] - ((elev - 1) * 2), 100 | height: size[1] - ((elev - 1) * 2) 101 | }; 102 | rectangle(svg, 2, 2, s.width - 4, s.height - 4, this.seed); 103 | for (let i = 1; i < elev; i++) { 104 | (line(svg, (i * 2), s.height - 4 + (i * 2), s.width - 4 + (i * 2), s.height - 4 + (i * 2), this.seed)).style.opacity = `${(85 - (i * 10)) / 100}`; 105 | (line(svg, s.width - 4 + (i * 2), s.height - 4 + (i * 2), s.width - 4 + (i * 2), i * 2, this.seed)).style.opacity = `${(85 - (i * 10)) / 100}`; 106 | (line(svg, (i * 2), s.height - 4 + (i * 2), s.width - 4 + (i * 2), s.height - 4 + (i * 2), this.seed)).style.opacity = `${(85 - (i * 10)) / 100}`; 107 | (line(svg, s.width - 4 + (i * 2), s.height - 4 + (i * 2), s.width - 4 + (i * 2), i * 2, this.seed)).style.opacity = `${(85 - (i * 10)) / 100}`; 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /src/wired-input.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-input') 7 | export class WiredInput extends WiredBase { 8 | @property({ type: Boolean, reflect: true }) disabled = false; 9 | @property({ type: String }) placeholder = ''; 10 | @property({ type: String }) name?: string; 11 | @property({ type: String }) min?: string; 12 | @property({ type: String }) max?: string; 13 | @property({ type: String }) step?: string; 14 | @property({ type: String }) type = 'text'; 15 | @property({ type: String }) autocomplete = ''; 16 | @property({ type: String }) autocapitalize = ''; 17 | @property({ type: String }) autocorrect = ''; 18 | @property({ type: Boolean }) required = false; 19 | @property({ type: Boolean }) autofocus = false; 20 | @property({ type: Boolean }) readonly = false; 21 | @property({ type: Number }) minlength?: number; 22 | @property({ type: Number }) maxlength?: number; 23 | @property({ type: Number }) size?: number; 24 | 25 | @query('input') private textInput?: HTMLInputElement; 26 | private pendingValue?: string; 27 | private resizeObserver?: ResizeObserver; 28 | private roAttached = false; 29 | 30 | constructor() { 31 | super(); 32 | if ((window as any).ResizeObserver) { 33 | this.resizeObserver = new (window as any).ResizeObserver(() => { 34 | if (this.svg) { 35 | this.wiredRender(true); 36 | } 37 | }); 38 | } 39 | } 40 | 41 | static get styles(): CSSResultArray { 42 | return [ 43 | BaseCSS, 44 | css` 45 | :host { 46 | display: inline-block; 47 | position: relative; 48 | padding: 5px; 49 | font-family: sans-serif; 50 | width: 150px; 51 | outline: none; 52 | } 53 | :host([disabled]) { 54 | opacity: 0.6 !important; 55 | cursor: default; 56 | pointer-events: none; 57 | } 58 | :host([disabled]) svg { 59 | background: rgba(0, 0, 0, 0.07); 60 | } 61 | input { 62 | display: block; 63 | width: 100%; 64 | box-sizing: border-box; 65 | outline: none; 66 | border: none; 67 | font-family: inherit; 68 | font-size: inherit; 69 | font-weight: inherit; 70 | color: inherit; 71 | padding: 6px; 72 | } 73 | input:focus + div path { 74 | stroke-width: 1.5; 75 | } 76 | ` 77 | ]; 78 | } 79 | 80 | render(): TemplateResult { 81 | return html` 82 | 87 |
88 | 89 |
90 | `; 91 | } 92 | 93 | get input(): HTMLInputElement | undefined { 94 | return this.textInput; 95 | } 96 | 97 | get value(): string { 98 | const input = this.input; 99 | return (input && input.value) || ''; 100 | } 101 | 102 | set value(v: string) { 103 | if (this.shadowRoot) { 104 | const input = this.input; 105 | if (input) { 106 | input.value = v; 107 | return; 108 | } 109 | } 110 | this.pendingValue = v; 111 | } 112 | 113 | firstUpdated() { 114 | this.value = this.pendingValue || this.value || this.getAttribute('value') || ''; 115 | delete this.pendingValue; 116 | } 117 | 118 | protected canvasSize(): Point { 119 | const s = this.getBoundingClientRect(); 120 | return [s.width, s.height]; 121 | } 122 | 123 | protected draw(svg: SVGSVGElement, size: Point) { 124 | rectangle(svg, 2, 2, size[0] - 2, size[1] - 2, this.seed); 125 | } 126 | 127 | private refire(event: Event) { 128 | event.stopPropagation(); 129 | this.fire(event.type, { sourceEvent: event }); 130 | } 131 | 132 | focus() { 133 | if (this.textInput) { 134 | this.textInput.focus(); 135 | } else { 136 | super.focus(); 137 | } 138 | } 139 | 140 | updated() { 141 | super.updated(); 142 | this.attachResizeListener(); 143 | } 144 | 145 | disconnectedCallback() { 146 | this.detachResizeListener(); 147 | } 148 | 149 | private attachResizeListener() { 150 | if (!this.roAttached) { 151 | if (this.textInput && this.resizeObserver) { 152 | this.resizeObserver.observe(this.textInput); 153 | } 154 | this.roAttached = true; 155 | } 156 | } 157 | 158 | private detachResizeListener() { 159 | if (this.textInput && this.resizeObserver) { 160 | this.resizeObserver.unobserve(this.textInput); 161 | } 162 | this.roAttached = false; 163 | } 164 | } -------------------------------------------------------------------------------- /src/wired-item.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | import { hachureFill } from './wired-lib'; 5 | 6 | @customElement('wired-item') 7 | export class WiredItem extends WiredBase { 8 | @property() value = ''; 9 | @property() name = ''; 10 | @property({ type: Boolean }) selected = false; 11 | 12 | static get styles(): CSSResultArray { 13 | return [ 14 | BaseCSS, 15 | css` 16 | :host { 17 | display: inline-block; 18 | font-size: 14px; 19 | text-align: left; 20 | } 21 | button { 22 | cursor: pointer; 23 | outline: none; 24 | overflow: hidden; 25 | color: inherit; 26 | user-select: none; 27 | position: relative; 28 | font-family: inherit; 29 | text-align: inherit; 30 | font-size: inherit; 31 | letter-spacing: 1.25px; 32 | padding: 1px 10px; 33 | min-height: 36px; 34 | text-transform: inherit; 35 | background: none; 36 | border: none; 37 | transition: background-color 0.3s ease, color 0.3s ease; 38 | width: 100%; 39 | box-sizing: border-box; 40 | white-space: nowrap; 41 | } 42 | button.selected { 43 | color: var(--wired-item-selected-color, #fff); 44 | } 45 | button::before { 46 | content: ''; 47 | position: absolute; 48 | top: 0; 49 | left: 0; 50 | width: 100%; 51 | height: 100%; 52 | background: currentColor; 53 | opacity: 0; 54 | } 55 | button span { 56 | display: inline-block; 57 | transition: transform 0.2s ease; 58 | position: relative; 59 | } 60 | button:active span { 61 | transform: scale(1.02); 62 | } 63 | #overlay { 64 | display: none; 65 | } 66 | button.selected #overlay { 67 | display: block; 68 | } 69 | svg path { 70 | stroke: var(--wired-item-selected-bg, #000); 71 | stroke-width: 2.75; 72 | fill: transparent; 73 | transition: transform 0.05s ease; 74 | } 75 | @media (hover: hover) { 76 | button:hover::before { 77 | opacity: 0.05; 78 | } 79 | } 80 | ` 81 | ]; 82 | } 83 | 84 | render(): TemplateResult { 85 | return html` 86 | `; 90 | } 91 | 92 | protected canvasSize(): Point { 93 | const s = this.getBoundingClientRect(); 94 | return [s.width, s.height]; 95 | } 96 | 97 | protected draw(svg: SVGSVGElement, size: Point) { 98 | const g = hachureFill([ 99 | [0, 0], 100 | [size[0], 0], 101 | [size[0], size[1]], 102 | [0, size[1]] 103 | ], this.seed); 104 | svg.appendChild(g); 105 | } 106 | } -------------------------------------------------------------------------------- /src/wired-lib.ts: -------------------------------------------------------------------------------- 1 | import { Point } from 'roughjs/bin/geometry.js'; 2 | import { ResolvedOptions, OpSet, Op } from 'roughjs/bin/core'; 3 | import { 4 | line as roughLine, 5 | rectangle as roughRectangle, 6 | ellipse as roughEllipse, 7 | polygon as roughPolygon, 8 | arc as roughArc, 9 | doubleLineFillOps, 10 | generateEllipseParams 11 | } from 'roughjs/bin/renderer'; 12 | import { ZigZagFiller } from 'roughjs/bin/fillers/zigzag-filler'; 13 | import { RenderHelper } from 'roughjs/bin/fillers/filler-interface'; 14 | 15 | type Params = { [name: string]: string }; 16 | 17 | const fillHelper: RenderHelper = { 18 | randOffset(x: number, _o: ResolvedOptions): number { 19 | return x; 20 | }, 21 | randOffsetWithRange(min: number, max: number, _o: ResolvedOptions): number { 22 | return (min + max) / 2; 23 | }, 24 | ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet { 25 | return roughEllipse(x, y, width, height, o); 26 | }, 27 | doubleLineOps(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[] { 28 | return doubleLineFillOps(x1, y1, x2, y2, o); 29 | } 30 | }; 31 | 32 | function options(seed: number): ResolvedOptions { 33 | return { 34 | maxRandomnessOffset: 2, 35 | roughness: 1, 36 | bowing: 0.85, 37 | stroke: '#000', 38 | strokeWidth: 1.5, 39 | curveTightness: 0, 40 | curveFitting: 0.95, 41 | curveStepCount: 9, 42 | fillStyle: 'hachure', 43 | fillWeight: 3.5, 44 | hachureAngle: -41, 45 | hachureGap: 5, 46 | dashOffset: -1, 47 | dashGap: -1, 48 | zigzagOffset: 0, 49 | combineNestedSvgPaths: false, 50 | disableMultiStroke: false, 51 | disableMultiStrokeFill: false, 52 | seed 53 | }; 54 | } 55 | 56 | function opsToPath(drawing: OpSet, joinPaths: boolean): string { 57 | let path = ''; 58 | for (const item of drawing.ops) { 59 | const data = item.data; 60 | switch (item.op) { 61 | case 'move': 62 | if (joinPaths && path) { 63 | break; 64 | } 65 | path += `M${data[0]} ${data[1]} `; 66 | break; 67 | case 'bcurveTo': 68 | path += `C${data[0]} ${data[1]}, ${data[2]} ${data[3]}, ${data[4]} ${data[5]} `; 69 | break; 70 | case 'lineTo': 71 | path += `L${data[0]} ${data[1]} `; 72 | break; 73 | } 74 | } 75 | return path.trim(); 76 | } 77 | 78 | export function svgNode(tagName: string, attributes?: Params): SVGElement { 79 | const n = document.createElementNS('http://www.w3.org/2000/svg', tagName); 80 | if (attributes) { 81 | for (const p in attributes) { 82 | n.setAttributeNS(null, p, attributes[p]); 83 | } 84 | } 85 | return n; 86 | } 87 | 88 | function createPathNode(ops: OpSet, parent: SVGElement | null, joinPaths = false): SVGPathElement { 89 | const path = svgNode('path', { d: opsToPath(ops, joinPaths) }); 90 | if (parent) { 91 | parent.appendChild(path); 92 | } 93 | return path as SVGPathElement; 94 | } 95 | 96 | export function rectangle(parent: SVGElement, x: number, y: number, width: number, height: number, seed: number): SVGElement { 97 | return createPathNode(roughRectangle(x + 2, y + 2, width - 4, height - 4, options(seed)), parent); 98 | } 99 | 100 | export function line(parent: SVGElement, x1: number, y1: number, x2: number, y2: number, seed: number): SVGElement { 101 | return createPathNode(roughLine(x1, y1, x2, y2, options(seed)), parent); 102 | } 103 | 104 | export function polygon(parent: SVGElement, vertices: Point[], seed: number): SVGElement { 105 | return createPathNode(roughPolygon(vertices, options(seed)), parent, true); 106 | } 107 | 108 | export function ellipse(parent: SVGElement, x: number, y: number, width: number, height: number, seed: number): SVGElement { 109 | width = Math.max(width > 10 ? width - 4 : width - 1, 1); 110 | height = Math.max(height > 10 ? height - 4 : height - 1, 1); 111 | return createPathNode(roughEllipse(x, y, width, height, options(seed)), parent); 112 | } 113 | 114 | export function arc(parent: SVGElement, x: number, y: number, width: number, height: number, start: number, stop: number, seed: number): SVGElement { 115 | width = Math.max(width > 10 ? width - 4 : width - 1, 1); 116 | height = Math.max(height > 10 ? height - 4 : height - 1, 1); 117 | return createPathNode(roughArc(x, y, width, height, start, stop, false, false, options(seed)), parent); 118 | } 119 | 120 | export function hachureFill(points: Point[], seed: number): SVGElement { 121 | const hf = new ZigZagFiller(fillHelper); 122 | const ops = hf.fillPolygon(points, options(seed)); 123 | return createPathNode(ops, null); 124 | } 125 | 126 | export function hachureEllipseFill(cx: number, cy: number, width: number, height: number, seed: number): SVGElement { 127 | const o = options(seed); 128 | const ep = generateEllipseParams(width, height, o); 129 | const vertices: Point[] = []; 130 | let angle = 0; 131 | while (angle <= (Math.PI * 2)) { 132 | vertices.push([ 133 | cx + ep.rx * Math.cos(angle), 134 | cy + ep.ry * Math.sin(angle) 135 | ]); 136 | angle += ep.increment; 137 | } 138 | return hachureFill(vertices, seed); 139 | } -------------------------------------------------------------------------------- /src/wired-link.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { line } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-link') 7 | export class WiredLink extends WiredBase { 8 | @property({ type: Number }) elevation = 1; 9 | @property({ type: String }) href?: string; 10 | @property({ type: String }) target?: string; 11 | @query('a') private anchor?: HTMLAnchorElement; 12 | 13 | static get styles(): CSSResultArray { 14 | return [ 15 | BaseCSS, 16 | css` 17 | :host { 18 | display: inline-block; 19 | position: relative; 20 | } 21 | a, a:hover, a:visited { 22 | color: inherit; 23 | outline: none; 24 | display: inline-block; 25 | white-space: nowrap; 26 | text-decoration: none; 27 | border: none; 28 | } 29 | path { 30 | stroke: var(--wired-link-decoration-color, blue); 31 | stroke-opacity: 0.45; 32 | } 33 | a:focus path { 34 | stroke-opacity: 1; 35 | } 36 | ` 37 | ]; 38 | } 39 | 40 | render(): TemplateResult { 41 | return html` 42 |
43 | 44 |
45 |
46 | `; 47 | } 48 | 49 | focus() { 50 | if (this.anchor) { 51 | this.anchor.focus(); 52 | } else { 53 | super.focus(); 54 | } 55 | } 56 | 57 | protected canvasSize(): Point { 58 | if (this.anchor) { 59 | const size = this.anchor.getBoundingClientRect(); 60 | const elev = Math.min(Math.max(1, this.elevation), 5); 61 | const w = size.width; 62 | const h = size.height + ((elev - 1) * 2); 63 | return [w, h]; 64 | } 65 | return this.lastSize; 66 | } 67 | 68 | protected draw(svg: SVGSVGElement, size: Point) { 69 | const elev = Math.min(Math.max(1, this.elevation), 5); 70 | const s = { 71 | width: size[0], 72 | height: size[1] - ((elev - 1) * 2) 73 | }; 74 | for (let i = 0; i < elev; i++) { 75 | line(svg, 0, s.height + (i * 2) - 2, s.width, s.height + (i * 2) - 2, this.seed); 76 | line(svg, 0, s.height + (i * 2) - 2, s.width, s.height + (i * 2) - 2, this.seed); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/wired-progress-ring.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { ellipse, arc } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-progress-ring') 7 | export class WiredProgressRing extends WiredBase { 8 | @property({ type: Number }) value = 0; 9 | @property({ type: Number }) min = 0; 10 | @property({ type: Number }) max = 100; 11 | @property({ type: Boolean }) hideLabel = false; 12 | @property({ type: Boolean }) showLabelAsPercent = false; 13 | @property({ type: Number }) precision = 0; 14 | 15 | private progArc?: SVGElement; 16 | 17 | static get styles(): CSSResultArray { 18 | return [ 19 | BaseCSS, 20 | css` 21 | :host { 22 | display: inline-block; 23 | position: relative; 24 | width: 200px; 25 | font-family: sans-serif; 26 | } 27 | #overlay { 28 | position: relative; 29 | } 30 | path.progressArc { 31 | stroke-width: 10px; 32 | stroke: var(--wired-progress-color, blue); 33 | } 34 | #labelPanel { 35 | position: absolute; 36 | top: 0; 37 | left: 0; 38 | width: 100%; 39 | height: 100%; 40 | display: grid; 41 | align-content: center; 42 | align-items: center; 43 | justify-content: center; 44 | justify-items: center; 45 | } 46 | ` 47 | ]; 48 | } 49 | 50 | render(): TemplateResult { 51 | let label = `${this.value}`; 52 | if (this.showLabelAsPercent) { 53 | const pct = 100 * Math.min(1, Math.max(0, (this.value - this.min) / (this.max - this.min))); 54 | if (this.precision) { 55 | label = `${pct.toPrecision(this.precision)}%`; 56 | } else { 57 | label = `${Math.round(pct)}%`; 58 | } 59 | } 60 | return html` 61 |
62 | 63 |
64 | 65 | ${this.hideLabel ? '' : html` 66 |
67 |
${label}
68 |
69 | `} 70 | 71 | `; 72 | } 73 | 74 | wiredRender(force = false) { 75 | super.wiredRender(force); 76 | this.refreshProgressFill(); 77 | } 78 | 79 | protected canvasSize(): Point { 80 | const s = this.getBoundingClientRect(); 81 | return [s.width, s.width]; 82 | } 83 | 84 | protected draw(svg: SVGSVGElement, size: Point) { 85 | const [x, y, w, h] = [size[0] / 2, size[1] / 2, size[0] - 10, size[1] - 10]; 86 | ellipse(svg, x, y, w, h, this.seed); 87 | 88 | } 89 | 90 | private refreshProgressFill() { 91 | if (this.progArc) { 92 | if (this.progArc.parentElement) { 93 | this.progArc.parentElement.removeChild(this.progArc); 94 | } 95 | this.progArc = undefined; 96 | } 97 | if (this.svg) { 98 | const size = this.canvasSize(); 99 | const [x, y, w, h] = [size[0] / 2, size[1] / 2, size[0] - 10, size[1] - 10]; 100 | const pct = Math.min(1, Math.max(0, (this.value - this.min) / (this.max - this.min))); 101 | if (pct) { 102 | this.progArc = arc(this.svg, x, y, w, h, -Math.PI / 2, 2 * Math.PI * pct - Math.PI / 2, this.seed); 103 | this.progArc.classList.add('progressArc'); 104 | } 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /src/wired-progress.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle, hachureFill } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-progress') 7 | export class WiredProgress extends WiredBase { 8 | @property({ type: Number }) value = 0; 9 | @property({ type: Number }) min = 0; 10 | @property({ type: Number }) max = 100; 11 | @property({ type: Boolean }) percentage = false; 12 | 13 | private progBox?: SVGElement; 14 | 15 | static get styles(): CSSResultArray { 16 | return [ 17 | BaseCSS, 18 | css` 19 | :host { 20 | display: inline-block; 21 | position: relative; 22 | width: 400px; 23 | height: 42px; 24 | font-family: sans-serif; 25 | } 26 | .labelContainer { 27 | display: flex; 28 | align-items: center; 29 | justify-content: center; 30 | } 31 | .progressLabel { 32 | color: var(--wired-progress-label-color, #000); 33 | font-size: var(--wired-progress-font-size, 14px); 34 | background: var(--wired-progress-label-background, rgba(255,255,255,0.9)); 35 | padding: 2px 6px; 36 | border-radius: 4px; 37 | letter-spacing: 1.25px; 38 | } 39 | path.progbox { 40 | stroke: var(--wired-progress-color, rgba(0, 0, 200, 0.8)); 41 | stroke-width: 2.75; 42 | fill: none; 43 | } 44 | .overlay { 45 | position: absolute; 46 | top: 0; 47 | left: 0; 48 | right: 0; 49 | bottom: 0; 50 | pointer-events: none; 51 | } 52 | ` 53 | ]; 54 | } 55 | 56 | render(): TemplateResult { 57 | return html` 58 |
59 | 60 |
61 |
62 |
${this.getProgressLabel()}
63 |
64 | `; 65 | } 66 | 67 | private getProgressLabel(): string { 68 | if (this.percentage) { 69 | if (this.max === this.min) { 70 | return '%'; 71 | } else { 72 | const pct = Math.floor(((this.value - this.min) / (this.max - this.min)) * 100); 73 | return (pct + '%'); 74 | } 75 | } else { 76 | return ('' + this.value); 77 | } 78 | } 79 | 80 | wiredRender(force = false) { 81 | super.wiredRender(force); 82 | this.refreshProgressFill(); 83 | } 84 | 85 | protected canvasSize(): Point { 86 | const s = this.getBoundingClientRect(); 87 | return [s.width, s.height]; 88 | } 89 | 90 | protected draw(svg: SVGSVGElement, size: Point) { 91 | rectangle(svg, 2, 2, size[0] - 2, size[1] - 2, this.seed); 92 | } 93 | 94 | private refreshProgressFill() { 95 | if (this.progBox) { 96 | if (this.progBox.parentElement) { 97 | this.progBox.parentElement.removeChild(this.progBox); 98 | } 99 | this.progBox = undefined; 100 | } 101 | if (this.svg) { 102 | let pct = 0; 103 | const s = this.getBoundingClientRect(); 104 | if (this.max > this.min) { 105 | pct = (this.value - this.min) / (this.max - this.min); 106 | const progWidth = s.width * Math.max(0, Math.min(pct, 100)); 107 | this.progBox = hachureFill([ 108 | [0, 0], 109 | [progWidth, 0], 110 | [progWidth, s.height], 111 | [0, s.height] 112 | ], this.seed); 113 | this.svg!.appendChild(this.progBox); 114 | this.progBox.classList.add('progbox'); 115 | } 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /src/wired-radio-group.ts: -------------------------------------------------------------------------------- 1 | import { fireEvent } from './wired-base'; 2 | import { css, TemplateResult, html, LitElement } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | 5 | interface RadioItem extends HTMLElement { 6 | name: string; 7 | checked: boolean; 8 | } 9 | 10 | @customElement('wired-radio-group') 11 | export class WiredRadioGroup extends LitElement { 12 | @property({ type: String }) selected?: string; 13 | private radioNodes: RadioItem[] = []; 14 | private checkListener = this.handleChecked.bind(this); 15 | 16 | static get styles() { 17 | return css` 18 | :host { 19 | display: inline-block; 20 | font-family: inherit; 21 | outline: none; 22 | } 23 | :host ::slotted(*) { 24 | padding: var(--wired-radio-group-item-padding, 5px); 25 | } 26 | `; 27 | } 28 | 29 | render(): TemplateResult { 30 | return html``; 31 | } 32 | 33 | connectedCallback() { 34 | super.connectedCallback(); 35 | this.addEventListener('change', this.checkListener); 36 | } 37 | 38 | disconnectedCallback() { 39 | super.disconnectedCallback(); 40 | this.removeEventListener('change', this.checkListener); 41 | } 42 | 43 | private handleChecked(event: Event) { 44 | const checked = (event as CustomEvent).detail.checked; 45 | const item = event.target as any as RadioItem; 46 | const name = item.name || ''; 47 | if (!checked) { 48 | item.checked = true; 49 | } else { 50 | this.selected = (checked && name) || ''; 51 | this.fireSelected(); 52 | } 53 | } 54 | 55 | slotChange() { 56 | this.requestUpdate(); 57 | } 58 | 59 | firstUpdated() { 60 | this.setAttribute('role', 'radiogroup'); 61 | this.tabIndex = +(this.getAttribute('tabindex') || 0); 62 | this.addEventListener('keydown', (event) => { 63 | switch (event.keyCode) { 64 | case 37: 65 | case 38: 66 | event.preventDefault(); 67 | this.selectPrevious(); 68 | break; 69 | case 39: 70 | case 40: 71 | event.preventDefault(); 72 | this.selectNext(); 73 | break; 74 | } 75 | }); 76 | } 77 | 78 | updated() { 79 | const slot = this.shadowRoot!.getElementById('slot') as HTMLSlotElement; 80 | const nodes = slot.assignedNodes(); 81 | this.radioNodes = []; 82 | if (nodes && nodes.length) { 83 | for (let i = 0; i < nodes.length; i++) { 84 | const element = nodes[i] as RadioItem; 85 | if (element.tagName === 'WIRED-RADIO') { 86 | this.radioNodes.push(element); 87 | const name = element.name || ''; 88 | if (this.selected && (name === this.selected)) { 89 | element.checked = true; 90 | } else { 91 | element.checked = false; 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | private selectPrevious() { 99 | const list = this.radioNodes; 100 | if (list.length) { 101 | let radio = null; 102 | let index = -1; 103 | if (this.selected) { 104 | for (let i = 0; i < list.length; i++) { 105 | const n = list[i]; 106 | if (n.name === this.selected) { 107 | index = i; 108 | break; 109 | } 110 | } 111 | if (index < 0) { 112 | radio = list[0]; 113 | } else { 114 | index--; 115 | if (index < 0) { 116 | index = list.length - 1; 117 | } 118 | radio = list[index]; 119 | } 120 | } else { 121 | radio = list[0]; 122 | } 123 | if (radio) { 124 | radio.focus(); 125 | this.selected = radio.name; 126 | this.fireSelected(); 127 | } 128 | } 129 | } 130 | 131 | private selectNext() { 132 | const list = this.radioNodes; 133 | if (list.length) { 134 | let radio = null; 135 | let index = -1; 136 | if (this.selected) { 137 | for (let i = 0; i < list.length; i++) { 138 | const n = list[i]; 139 | if (n.name === this.selected) { 140 | index = i; 141 | break; 142 | } 143 | } 144 | if (index < 0) { 145 | radio = list[0]; 146 | } else { 147 | index++; 148 | if (index >= list.length) { 149 | index = 0; 150 | } 151 | radio = list[index]; 152 | } 153 | } else { 154 | radio = list[0]; 155 | } 156 | if (radio) { 157 | radio.focus(); 158 | this.selected = radio.name; 159 | this.fireSelected(); 160 | } 161 | } 162 | } 163 | 164 | private fireSelected() { 165 | fireEvent(this, 'selected', { selected: this.selected }); 166 | } 167 | } -------------------------------------------------------------------------------- /src/wired-radio.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { ellipse, svgNode } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-radio') 7 | export class WiredRadio extends WiredBase { 8 | @property({ type: Boolean }) checked = false; 9 | @property({ type: Boolean, reflect: true }) disabled = false; 10 | @property({ type: String }) name?: string; 11 | @property() private focused = false; 12 | 13 | @query('input') private input?: HTMLInputElement; 14 | 15 | private svgCheck?: SVGElement; 16 | 17 | static get styles(): CSSResultArray { 18 | return [ 19 | BaseCSS, 20 | css` 21 | :host { 22 | display: inline-block; 23 | font-family: inherit; 24 | } 25 | :host([disabled]) { 26 | opacity: 0.6 !important; 27 | cursor: default; 28 | pointer-events: none; 29 | } 30 | :host([disabled]) svg { 31 | background: rgba(0, 0, 0, 0.07); 32 | } 33 | 34 | #container { 35 | display: flex; 36 | flex-direction: row; 37 | position: relative; 38 | user-select: none; 39 | min-height: 24px; 40 | cursor: pointer; 41 | } 42 | span { 43 | margin-left: 1.5ex; 44 | line-height: 24px; 45 | } 46 | input { 47 | opacity: 0; 48 | } 49 | path { 50 | stroke: var(--wired-radio-icon-color, currentColor); 51 | stroke-width: var(--wired-radio-default-swidth, 0.7); 52 | } 53 | g path { 54 | stroke-width: 0; 55 | fill: var(--wired-radio-icon-color, currentColor); 56 | } 57 | #container.focused { 58 | --wired-radio-default-swidth: 1.5; 59 | } 60 | ` 61 | ]; 62 | } 63 | 64 | focus() { 65 | if (this.input) { 66 | this.input.focus(); 67 | } else { 68 | super.focus(); 69 | } 70 | } 71 | 72 | wiredRender(force = false) { 73 | super.wiredRender(force); 74 | this.refreshCheckVisibility(); 75 | } 76 | 77 | render(): TemplateResult { 78 | return html` 79 | 87 | `; 88 | } 89 | 90 | private onChange() { 91 | this.checked = this.input!.checked; 92 | this.refreshCheckVisibility(); 93 | this.fire('change', { checked: this.checked }); 94 | } 95 | 96 | protected canvasSize(): Point { 97 | return [24, 24]; 98 | } 99 | 100 | protected draw(svg: SVGSVGElement, size: Point) { 101 | ellipse(svg, size[0] / 2, size[1] / 2, size[0], size[1], this.seed); 102 | this.svgCheck = svgNode('g'); 103 | svg.appendChild(this.svgCheck); 104 | const iw = Math.max(size[0] * 0.6, 5); 105 | const ih = Math.max(size[1] * 0.6, 5); 106 | ellipse(this.svgCheck, size[0] / 2, size[1] / 2, iw, ih, this.seed); 107 | } 108 | 109 | private refreshCheckVisibility() { 110 | if (this.svgCheck) { 111 | this.svgCheck.style.display = this.checked ? '' : 'none'; 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /src/wired-search-input.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle, line, svgNode, ellipse } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-search-input') 7 | export class WiredSearchInput extends WiredBase { 8 | @property({ type: Boolean, reflect: true }) disabled = false; 9 | @property({ type: String }) placeholder = ''; 10 | @property({ type: String }) autocomplete = ''; 11 | @property({ type: String }) autocorrect = ''; 12 | @property({ type: Boolean }) autofocus = false; 13 | 14 | @query('input') private textInput?: HTMLInputElement; 15 | 16 | private pendingValue?: string; 17 | private searchIcon?: SVGElement; 18 | private closeIcon?: SVGElement; 19 | 20 | static get styles(): CSSResultArray { 21 | return [ 22 | BaseCSS, 23 | css` 24 | :host { 25 | display: inline-block; 26 | position: relative; 27 | padding: 10px 40px 10px 5px; 28 | font-family: sans-serif; 29 | width: 180px; 30 | outline: none; 31 | } 32 | :host([disabled]) { 33 | opacity: 0.6 !important; 34 | cursor: default; 35 | pointer-events: none; 36 | } 37 | :host([disabled]) svg { 38 | background: rgba(0, 0, 0, 0.07); 39 | } 40 | input { 41 | display: block; 42 | width: 100%; 43 | box-sizing: border-box; 44 | outline: none; 45 | border: none; 46 | font-family: inherit; 47 | font-size: inherit; 48 | font-weight: inherit; 49 | color: inherit; 50 | padding: 6px; 51 | } 52 | 53 | input[type=search]::-ms-clear { display: none; width : 0; height: 0; } 54 | input[type=search]::-ms-reveal { display: none; width : 0; height: 0; } 55 | input[type="search"]::-webkit-search-decoration, 56 | input[type="search"]::-webkit-search-cancel-button, 57 | input[type="search"]::-webkit-search-results-button, 58 | input[type="search"]::-webkit-search-results-decoration { 59 | display: none; 60 | } 61 | 62 | .thicker path { 63 | stroke-width: 1.5; 64 | } 65 | 66 | button { 67 | position: absolute; 68 | top: 0; 69 | right: 2px; 70 | width: 32px; 71 | height: 100%; 72 | box-sizing: border-box; 73 | background: none; 74 | border: none; 75 | cursor: pointer; 76 | outline: none; 77 | opacity: 0; 78 | } 79 | ` 80 | ]; 81 | } 82 | 83 | render(): TemplateResult { 84 | return html` 85 | 89 |
90 | 91 |
92 | 93 | `; 94 | } 95 | 96 | get input(): HTMLInputElement | undefined { 97 | return this.textInput; 98 | } 99 | 100 | get value(): string { 101 | const input = this.input; 102 | return (input && input.value) || ''; 103 | } 104 | 105 | set value(v: string) { 106 | if (this.shadowRoot) { 107 | const input = this.input; 108 | if (input) { 109 | input.value = v; 110 | } 111 | this.refreshIconState(); 112 | } else { 113 | this.pendingValue = v; 114 | } 115 | } 116 | 117 | wiredRender(force = false) { 118 | super.wiredRender(force); 119 | this.refreshIconState(); 120 | } 121 | 122 | firstUpdated() { 123 | this.value = this.pendingValue || this.value || this.getAttribute('value') || ''; 124 | delete this.pendingValue; 125 | } 126 | 127 | protected canvasSize(): Point { 128 | const s = this.getBoundingClientRect(); 129 | return [s.width, s.height]; 130 | } 131 | 132 | protected draw(svg: SVGSVGElement, size: Point) { 133 | rectangle(svg, 2, 2, size[0] - 2, size[1] - 2, this.seed); 134 | 135 | this.searchIcon = svgNode('g'); 136 | this.searchIcon.classList.add('thicker'); 137 | svg.appendChild(this.searchIcon); 138 | ellipse(this.searchIcon, size[0] - 30, (size[1] - 30) / 2 + 10, 20, 20, this.seed); 139 | line(this.searchIcon, size[0] - 10, (size[1] - 30) / 2 + 30, size[0] - 25, (size[1] - 30) / 2 + 15, this.seed); 140 | 141 | this.closeIcon = svgNode('g'); 142 | this.closeIcon.classList.add('thicker'); 143 | svg.appendChild(this.closeIcon); 144 | line(this.closeIcon, size[0] - 33, (size[1] - 30) / 2 + 2, size[0] - 7, (size[1] - 30) / 2 + 28, this.seed); 145 | line(this.closeIcon, size[0] - 7, (size[1] - 30) / 2 + 2, size[0] - 33, (size[1] - 30) / 2 + 28, this.seed); 146 | } 147 | 148 | private refreshIconState() { 149 | if (this.searchIcon && this.closeIcon) { 150 | this.searchIcon.style.display = this.value.trim() ? 'none' : ''; 151 | this.closeIcon.style.display = this.value.trim() ? '' : 'none'; 152 | } 153 | } 154 | 155 | private refire(event: Event) { 156 | this.refreshIconState(); 157 | event.stopPropagation(); 158 | this.fire(event.type, { sourceEvent: event }); 159 | } 160 | } -------------------------------------------------------------------------------- /src/wired-slider.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { line, ellipse } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-slider') 7 | export class WiredSlider extends WiredBase { 8 | @property({ type: Number }) min = 0; 9 | @property({ type: Number }) max = 100; 10 | @property({ type: Number }) step = 1; 11 | @property({ type: Boolean, reflect: true }) disabled = false; 12 | 13 | @query('input') private input?: HTMLInputElement; 14 | 15 | private knob?: SVGElement; 16 | private canvasWidth = 300; 17 | private pendingValue?: number; 18 | 19 | static get styles(): CSSResultArray { 20 | return [ 21 | BaseCSS, 22 | css` 23 | :host { 24 | display: inline-block; 25 | position: relative; 26 | width: 300px; 27 | box-sizing: border-box; 28 | } 29 | :host([disabled]) { 30 | opacity: 0.45 !important; 31 | cursor: default; 32 | pointer-events: none; 33 | background: rgba(0, 0, 0, 0.07); 34 | border-radius: 5px; 35 | } 36 | input[type=range] { 37 | width: 100%; 38 | height: 40px; 39 | box-sizing: border-box; 40 | margin: 0; 41 | -webkit-appearance: none; 42 | background: transparent; 43 | outline: none; 44 | position: relative; 45 | } 46 | input[type=range]:focus { 47 | outline: none; 48 | } 49 | input[type=range]::-ms-track { 50 | width: 100%; 51 | cursor: pointer; 52 | background: transparent; 53 | border-color: transparent; 54 | color: transparent; 55 | } 56 | input[type=range]::-moz-focus-outer { 57 | outline: none; 58 | border: 0; 59 | } 60 | input[type=range]::-moz-range-thumb { 61 | border-radius: 50px; 62 | background: none; 63 | cursor: pointer; 64 | border: none; 65 | margin: 0; 66 | height: 24px; 67 | width: 24px; 68 | line-height: 1; 69 | } 70 | input[type=range]::-webkit-slider-thumb { 71 | -webkit-appearance: none; 72 | border-radius: 50px; 73 | background: none; 74 | cursor: pointer; 75 | border: none; 76 | height: 24px; 77 | width: 24px; 78 | margin: 0; 79 | line-height: 1; 80 | } 81 | .knob{ 82 | fill: var(--wired-slider-knob-color, rgb(51, 103, 214)); 83 | stroke: var(--wired-slider-knob-color, rgb(51, 103, 214)); 84 | } 85 | .bar { 86 | stroke: var(--wired-slider-bar-color, rgb(0, 0, 0)); 87 | stroke-width: 1; 88 | } 89 | input:focus + div svg .knob { 90 | stroke: var(--wired-slider-knob-outline-color, #000); 91 | fill-opacity: 0.8; 92 | } 93 | ` 94 | ]; 95 | } 96 | 97 | get value(): number { 98 | if (this.input) { 99 | return +this.input.value; 100 | } 101 | return this.min; 102 | } 103 | 104 | set value(v: number) { 105 | if (this.input) { 106 | this.input.value = `${v}`; 107 | } else { 108 | this.pendingValue = v; 109 | } 110 | this.updateThumbPosition(); 111 | } 112 | 113 | firstUpdated() { 114 | this.value = this.pendingValue || +(this.getAttribute('value') || this.value || this.min); 115 | delete this.pendingValue; 116 | } 117 | 118 | 119 | render(): TemplateResult { 120 | return html` 121 |
122 | 128 |
129 | 130 |
131 |
132 | `; 133 | } 134 | 135 | focus() { 136 | if (this.input) { 137 | this.input.focus(); 138 | } else { 139 | super.focus(); 140 | } 141 | } 142 | 143 | private onInput(e: Event) { 144 | e.stopPropagation(); 145 | this.updateThumbPosition(); 146 | if (this.input) { 147 | this.fire('change', { value: +this.input.value }); 148 | } 149 | } 150 | 151 | wiredRender(force = false) { 152 | super.wiredRender(force); 153 | this.updateThumbPosition(); 154 | } 155 | 156 | protected canvasSize(): Point { 157 | const s = this.getBoundingClientRect(); 158 | return [s.width, s.height]; 159 | } 160 | 161 | protected draw(svg: SVGSVGElement, size: Point) { 162 | this.canvasWidth = size[0]; 163 | const midY = Math.round(size[1] / 2); 164 | line(svg, 0, midY, size[0], midY, this.seed).classList.add('bar'); 165 | this.knob = ellipse(svg, 12, midY, 24, 24, this.seed); 166 | this.knob.classList.add('knob'); 167 | } 168 | 169 | private updateThumbPosition() { 170 | if (this.input) { 171 | const value = +this.input!.value; 172 | const delta = Math.max(this.step, this.max - this.min); 173 | const pct = (value - this.min) / delta; 174 | if (this.knob) { 175 | this.knob.style.transform = `translateX(${pct * (this.canvasWidth - 24)}px)`; 176 | } 177 | } 178 | } 179 | } -------------------------------------------------------------------------------- /src/wired-spinner.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { ellipse, hachureEllipseFill } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-spinner') 7 | export class WiredSpinner extends WiredBase { 8 | @property({ type: Boolean }) spinning = false; 9 | @property({ type: Number }) duration = 1500; 10 | 11 | private knob?: SVGElement; 12 | private value = 0; 13 | private timerstart = 0; 14 | private frame = 0; 15 | 16 | static get styles(): CSSResultArray { 17 | return [ 18 | BaseCSS, 19 | css` 20 | :host { 21 | display: inline-block; 22 | position: relative; 23 | } 24 | path { 25 | stroke: currentColor; 26 | stroke-opacity: 0.65; 27 | stroke-width: 1.5; 28 | fill: none; 29 | } 30 | .knob { 31 | stroke-width: 2.8 !important; 32 | stroke-opacity: 1; 33 | } 34 | ` 35 | ]; 36 | } 37 | 38 | render(): TemplateResult { 39 | return html``; 40 | } 41 | 42 | protected canvasSize(): Point { 43 | return [76, 76]; 44 | } 45 | 46 | protected draw(svg: SVGSVGElement, size: Point) { 47 | ellipse(svg, size[0] / 2, size[1] / 2, Math.floor(size[0] * 0.8), Math.floor(0.8 * size[1]), this.seed); 48 | this.knob = hachureEllipseFill(0, 0, 20, 20, this.seed); 49 | this.knob.classList.add('knob'); 50 | svg.appendChild(this.knob); 51 | this.updateCursor(); 52 | } 53 | 54 | private updateCursor() { 55 | if (this.knob) { 56 | const position: Point = [ 57 | Math.round(38 + 25 * Math.cos(this.value * Math.PI * 2)), 58 | Math.round(38 + 25 * Math.sin(this.value * Math.PI * 2)) 59 | ]; 60 | this.knob.style.transform = `translate3d(${position[0]}px, ${position[1]}px, 0) rotateZ(${Math.round(this.value * 360 * 2)}deg)`; 61 | } 62 | } 63 | 64 | updated() { 65 | super.updated(); 66 | if (this.spinning) { 67 | this.startSpinner(); 68 | } else { 69 | this.stopSpinner(); 70 | } 71 | } 72 | 73 | private startSpinner() { 74 | this.stopSpinner(); 75 | this.value = 0; 76 | this.timerstart = 0; 77 | this.nextTick(); 78 | } 79 | 80 | private stopSpinner() { 81 | if (this.frame) { 82 | window.cancelAnimationFrame(this.frame); 83 | this.frame = 0; 84 | } 85 | } 86 | 87 | private nextTick() { 88 | this.frame = window.requestAnimationFrame((t) => this.tick(t)); 89 | } 90 | 91 | private tick(t: number) { 92 | if (this.spinning) { 93 | if (!this.timerstart) { 94 | this.timerstart = t; 95 | } 96 | this.value = Math.min(1, (t - this.timerstart) / this.duration); 97 | this.updateCursor(); 98 | if (this.value >= 1) { 99 | this.value = 0; 100 | this.timerstart = 0; 101 | } 102 | this.nextTick(); 103 | } else { 104 | this.frame = 0; 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /src/wired-tab.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property } from 'lit/decorators.js'; 5 | 6 | import './wired-item.js'; 7 | 8 | @customElement('wired-tab') 9 | export class WiredTab extends WiredBase { 10 | @property({ type: String }) name = ''; 11 | @property({ type: String }) label = ''; 12 | private resizeObserver?: ResizeObserver; 13 | private windowResizeHandler?: EventListenerOrEventListenerObject; 14 | 15 | constructor() { 16 | super(); 17 | if ((window as any).ResizeObserver) { 18 | this.resizeObserver = new (window as any).ResizeObserver(() => { 19 | if (this.svg) { 20 | this.wiredRender(); 21 | } 22 | }); 23 | } 24 | } 25 | 26 | static get styles(): CSSResultArray { 27 | return [ 28 | BaseCSS, 29 | css` 30 | :host { 31 | display: inline-block; 32 | position: relative; 33 | padding: 10px; 34 | } 35 | ` 36 | ]; 37 | } 38 | 39 | render(): TemplateResult { 40 | return html` 41 |
42 | 43 |
44 |
45 | `; 46 | } 47 | 48 | updated() { 49 | super.updated(); 50 | this.attachResizeListener(); 51 | } 52 | 53 | disconnectedCallback() { 54 | this.detachResizeListener(); 55 | } 56 | 57 | private attachResizeListener() { 58 | if (this.resizeObserver && this.resizeObserver.observe) { 59 | this.resizeObserver.observe(this); 60 | } else if (!this.windowResizeHandler) { 61 | this.windowResizeHandler = () => this.wiredRender(); 62 | window.addEventListener('resize', this.windowResizeHandler, { passive: true }); 63 | } 64 | } 65 | 66 | private detachResizeListener() { 67 | if (this.resizeObserver && this.resizeObserver.unobserve) { 68 | this.resizeObserver.unobserve(this); 69 | } 70 | if (this.windowResizeHandler) { 71 | window.removeEventListener('resize', this.windowResizeHandler); 72 | } 73 | } 74 | 75 | protected canvasSize(): Point { 76 | const s = this.getBoundingClientRect(); 77 | return [s.width, s.height]; 78 | } 79 | 80 | protected draw(svg: SVGSVGElement, s: Point) { 81 | rectangle(svg, 2, 2, s[0] - 4, s[1] - 4, this.seed); 82 | } 83 | } -------------------------------------------------------------------------------- /src/wired-tabs.ts: -------------------------------------------------------------------------------- 1 | import { BaseCSS } from './wired-base'; 2 | import { css, TemplateResult, html, CSSResultArray, LitElement } from 'lit'; 3 | import { customElement, property, query } from 'lit/decorators.js'; 4 | 5 | interface WiredTabItem extends HTMLElement { 6 | name: string; 7 | label: string; 8 | wiredRender(force?: boolean): void; 9 | } 10 | 11 | @customElement('wired-tabs') 12 | export class WiredTabs extends LitElement { 13 | @property({ type: String }) selected?: string; 14 | @query('slot') private slotElement?: HTMLSlotElement; 15 | 16 | private pages: WiredTabItem[] = []; 17 | private pageMap = new Map(); 18 | private current?: WiredTabItem; 19 | 20 | static get styles(): CSSResultArray { 21 | return [ 22 | BaseCSS, 23 | css` 24 | :host { 25 | display: block; 26 | opacity: 1; 27 | } 28 | ::slotted(.hidden) { 29 | display: none !important; 30 | } 31 | 32 | :host ::slotted(.hidden) { 33 | display: none !important; 34 | } 35 | #bar { 36 | display: -ms-flexbox; 37 | display: -webkit-flex; 38 | display: flex; 39 | -ms-flex-direction: row; 40 | -webkit-flex-direction: row; 41 | flex-direction: row; 42 | } 43 | ` 44 | ]; 45 | } 46 | 47 | render(): TemplateResult { 48 | return html` 49 |
50 | ${this.pages.map((p) => html` 51 | ${p.label || p.name} 53 | `)} 54 |
55 |
56 | 57 |
58 | `; 59 | } 60 | 61 | private mapPages() { 62 | this.pages = []; 63 | this.pageMap.clear(); 64 | if (this.slotElement) { 65 | const assigned = this.slotElement.assignedNodes(); 66 | if (assigned && assigned.length) { 67 | for (let i = 0; i < assigned.length; i++) { 68 | const n = assigned[i]; 69 | if (n.nodeType === Node.ELEMENT_NODE && (n as HTMLElement).tagName.toLowerCase() === 'wired-tab') { 70 | const e = n as WiredTabItem; 71 | this.pages.push(e); 72 | const name = e.getAttribute('name') || ''; 73 | if (name) { 74 | name.trim().split(' ').forEach((nameSegment) => { 75 | if (nameSegment) { 76 | this.pageMap.set(nameSegment, e); 77 | } 78 | }); 79 | } 80 | } 81 | } 82 | if (!this.selected) { 83 | if (this.pages.length) { 84 | this.selected = this.pages[0].name; 85 | } 86 | } 87 | this.requestUpdate(); 88 | } 89 | } 90 | } 91 | 92 | firstUpdated() { 93 | this.mapPages(); 94 | this.tabIndex = +((this.getAttribute('tabindex') || 0)); 95 | this.addEventListener('keydown', (event) => { 96 | switch (event.keyCode) { 97 | case 37: 98 | case 38: 99 | event.preventDefault(); 100 | this.selectPrevious(); 101 | break; 102 | case 39: 103 | case 40: 104 | event.preventDefault(); 105 | this.selectNext(); 106 | break; 107 | } 108 | }); 109 | } 110 | 111 | updated() { 112 | const newPage = this.getElement(); 113 | for (let i = 0; i < this.pages.length; i++) { 114 | const p = this.pages[i]; 115 | if (p === newPage as any) { 116 | p.classList.remove('hidden'); 117 | } else { 118 | p.classList.add('hidden'); 119 | } 120 | } 121 | this.current = newPage || undefined; 122 | if (this.current && this.current.wiredRender) { 123 | requestAnimationFrame(() => requestAnimationFrame(() => this.current!.wiredRender())); 124 | } 125 | } 126 | 127 | private getElement(): WiredTabItem | null { 128 | let e: WiredTabItem | undefined = undefined; 129 | if (this.selected) { 130 | e = this.pageMap.get(this.selected); 131 | } 132 | if (!e) { 133 | e = this.pages[0]; 134 | } 135 | return e || null; 136 | } 137 | 138 | private selectPrevious() { 139 | const list = this.pages; 140 | if (list.length) { 141 | let index = -1; 142 | for (let i = 0; i < list.length; i++) { 143 | if (list[i] === this.current) { 144 | index = i; 145 | break; 146 | } 147 | } 148 | if (index < 0) { 149 | index = 0; 150 | } else if (index === 0) { 151 | index = list.length - 1; 152 | } else { 153 | index--; 154 | } 155 | this.selected = list[index].name || ''; 156 | } 157 | } 158 | 159 | private selectNext() { 160 | const list = this.pages; 161 | if (list.length) { 162 | let index = -1; 163 | for (let i = 0; i < list.length; i++) { 164 | if (list[i] === this.current) { 165 | index = i; 166 | break; 167 | } 168 | } 169 | if (index < 0) { 170 | index = 0; 171 | } else if (index >= (list.length - 1)) { 172 | index = 0; 173 | } else { 174 | index++; 175 | } 176 | this.selected = list[index].name || ''; 177 | } 178 | } 179 | } -------------------------------------------------------------------------------- /src/wired-textarea.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-textarea') 7 | export class WiredTextarea extends WiredBase { 8 | @property({ type: Boolean, reflect: true }) disabled = false; 9 | @property({ type: Number }) rows = 2; 10 | @property({ type: Number }) maxrows = 0; 11 | @property({ type: String }) autocomplete = ''; 12 | @property({ type: Boolean }) autofocus = false; 13 | @property({ type: String }) inputmode = ''; 14 | @property({ type: String }) placeholder = ''; 15 | @property({ type: Boolean }) required = false; 16 | @property({ type: Boolean }) readonly = false; 17 | @property({ type: Number }) minlength?: number; 18 | @property({ type: Number }) maxlength?: number; 19 | 20 | @query('textarea') private textareaInput?: HTMLTextAreaElement; 21 | private pendingValue?: string; 22 | 23 | static get styles(): CSSResultArray { 24 | return [ 25 | BaseCSS, 26 | css` 27 | :host { 28 | display: inline-block; 29 | position: relative; 30 | font-family: sans-serif; 31 | width: 400px; 32 | outline: none; 33 | padding: 4px; 34 | } 35 | :host([disabled]) { 36 | opacity: 0.6 !important; 37 | cursor: default; 38 | pointer-events: none; 39 | } 40 | :host([disabled]) svg { 41 | background: rgba(0, 0, 0, 0.07); 42 | } 43 | textarea { 44 | position: relative; 45 | outline: none; 46 | border: none; 47 | resize: none; 48 | background: inherit; 49 | color: inherit; 50 | width: 100%; 51 | font-size: inherit; 52 | font-family: inherit; 53 | line-height: inherit; 54 | text-align: inherit; 55 | padding: 10px; 56 | box-sizing: border-box; 57 | } 58 | ` 59 | ]; 60 | } 61 | 62 | render(): TemplateResult { 63 | return html` 64 | 68 |
69 | 70 |
71 | `; 72 | } 73 | 74 | get textarea(): HTMLTextAreaElement | undefined { 75 | return this.textareaInput; 76 | } 77 | 78 | get value(): string { 79 | const input = this.textarea; 80 | return (input && input.value) || ''; 81 | } 82 | 83 | set value(v: string) { 84 | if (this.shadowRoot) { 85 | const input = this.textarea; 86 | if (input) { 87 | input.value = v; 88 | return; 89 | } 90 | } 91 | this.pendingValue = v; 92 | } 93 | 94 | firstUpdated() { 95 | this.value = this.pendingValue || this.value || this.getAttribute('value') || ''; 96 | delete this.pendingValue; 97 | } 98 | 99 | protected canvasSize(): Point { 100 | const s = this.getBoundingClientRect(); 101 | return [s.width, s.height]; 102 | } 103 | 104 | protected draw(svg: SVGSVGElement, size: Point) { 105 | rectangle(svg, 4, 4, size[0] - 4, size[1] - 4, this.seed); 106 | } 107 | 108 | private refire(event: Event) { 109 | event.stopPropagation(); 110 | this.fire(event.type, { sourceEvent: event }); 111 | } 112 | } -------------------------------------------------------------------------------- /src/wired-toggle.ts: -------------------------------------------------------------------------------- 1 | import { WiredBase, BaseCSS, Point } from './wired-base'; 2 | import { rectangle, hachureEllipseFill, ellipse, svgNode } from './wired-lib'; 3 | import { css, TemplateResult, html, CSSResultArray } from 'lit'; 4 | import { customElement, property, query } from 'lit/decorators.js'; 5 | 6 | @customElement('wired-toggle') 7 | export class WiredToggle extends WiredBase { 8 | @property({ type: Boolean }) checked = false; 9 | @property({ type: Boolean, reflect: true }) disabled = false; 10 | 11 | @query('input') private input?: HTMLInputElement; 12 | 13 | private knob?: SVGElement; 14 | 15 | static get styles(): CSSResultArray { 16 | return [ 17 | BaseCSS, 18 | css` 19 | :host { 20 | display: inline-block; 21 | cursor: pointer; 22 | position: relative; 23 | outline: none; 24 | } 25 | :host([disabled]) { 26 | opacity: 0.4 !important; 27 | cursor: default; 28 | pointer-events: none; 29 | } 30 | :host([disabled]) svg { 31 | background: rgba(0, 0, 0, 0.07); 32 | } 33 | input { 34 | position: absolute; 35 | top: 0; 36 | left: 0; 37 | width: 100%; 38 | height: 100%; 39 | box-sizing: border-box; 40 | cursor: pointer; 41 | opacity: 0; 42 | } 43 | .knob { 44 | transition: transform 0.3s ease; 45 | } 46 | .knob path { 47 | stroke-width: 0.7; 48 | } 49 | .knob.checked { 50 | transform: translateX(48px); 51 | } 52 | path.knobfill { 53 | stroke-width: 3 !important; 54 | fill: transparent; 55 | } 56 | .knob.unchecked path.knobfill { 57 | stroke: var(--wired-toggle-off-color, gray); 58 | } 59 | .knob.checked path.knobfill { 60 | stroke: var(--wired-toggle-on-color, rgb(63, 81, 181)); 61 | } 62 | ` 63 | ]; 64 | } 65 | 66 | render(): TemplateResult { 67 | return html` 68 |
69 | 70 | 71 |
72 | `; 73 | } 74 | 75 | focus() { 76 | if (this.input) { 77 | this.input.focus(); 78 | } else { 79 | super.focus(); 80 | } 81 | } 82 | 83 | wiredRender(force = false) { 84 | super.wiredRender(force); 85 | this.refreshKnob(); 86 | } 87 | 88 | private onChange() { 89 | this.checked = this.input!.checked; 90 | this.refreshKnob(); 91 | this.fire('change', { checked: this.checked }); 92 | } 93 | 94 | protected canvasSize(): Point { 95 | return [80, 34]; 96 | } 97 | 98 | protected draw(svg: SVGSVGElement, size: Point) { 99 | const rect = rectangle(svg, 16, 8, size[0] - 32, 18, this.seed); 100 | rect.classList.add('toggle-bar'); 101 | this.knob = svgNode('g'); 102 | this.knob.classList.add('knob'); 103 | svg.appendChild(this.knob); 104 | const knobFill = hachureEllipseFill(16, 16, 32, 32, this.seed); 105 | knobFill.classList.add('knobfill'); 106 | this.knob.appendChild(knobFill); 107 | ellipse(this.knob, 16, 16, 32, 32, this.seed); 108 | } 109 | 110 | private refreshKnob() { 111 | if (this.knob) { 112 | const cl = this.knob.classList; 113 | if (this.checked) { 114 | cl.remove('unchecked'); 115 | cl.add('checked'); 116 | } else { 117 | cl.remove('checked'); 118 | cl.add('unchecked'); 119 | } 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "es2015", 5 | "moduleResolution": "node", 6 | "lib": [ 7 | "es2017", 8 | "dom" 9 | ], 10 | "declaration": true, 11 | "outDir": "./lib", 12 | "baseUrl": ".", 13 | "strict": true, 14 | "strictNullChecks": true, 15 | "noImplicitAny": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "noImplicitReturns": true, 19 | "noFallthroughCasesInSwitch": true, 20 | "experimentalDecorators": true, 21 | "emitDecoratorMetadata": true, 22 | "skipLibCheck": true 23 | }, 24 | "include": [ 25 | "src/**/*.ts" 26 | ] 27 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "arrow-parens": true, 4 | "class-name": true, 5 | "indent": [ 6 | true, 7 | "spaces", 8 | 2 9 | ], 10 | "prefer-const": true, 11 | "no-duplicate-variable": true, 12 | "no-eval": true, 13 | "no-internal-module": true, 14 | "no-trailing-whitespace": false, 15 | "no-var-keyword": true, 16 | "one-line": [ 17 | true, 18 | "check-open-brace", 19 | "check-whitespace" 20 | ], 21 | "quotemark": [ 22 | true, 23 | "single", 24 | "avoid-escape" 25 | ], 26 | "semicolon": [ 27 | true, 28 | "always" 29 | ], 30 | "trailing-comma": [ 31 | true, 32 | "multiline" 33 | ], 34 | "triple-equals": [ 35 | true, 36 | "allow-null-check" 37 | ], 38 | "typedef-whitespace": [ 39 | true, 40 | { 41 | "call-signature": "nospace", 42 | "index-signature": "nospace", 43 | "parameter": "nospace", 44 | "property-declaration": "nospace", 45 | "variable-declaration": "nospace" 46 | } 47 | ], 48 | "variable-name": [ 49 | true, 50 | "ban-keywords" 51 | ], 52 | "whitespace": [ 53 | true, 54 | "check-branch", 55 | "check-decl", 56 | "check-operator", 57 | "check-separator", 58 | "check-type" 59 | ] 60 | } 61 | } --------------------------------------------------------------------------------