1 | # ![logo](/demo/src/assets/logo-small.png) web-ui-pack
|
2 |
|
3 | Web package with high scalable [WebComponents](#components) and [helpers](#helpers)
|
4 |
|
5 | [![npm version](https://img.shields.io/npm/v/web-ui-pack.svg?style=flat-square)](https://www.npmjs.com/package/web-ui-pack)
|
6 | [![code coverage](https://coveralls.io/repos/github/Yegorich555/web-ui-pack/badge.svg?style=flat-square)](https://coveralls.io/github/Yegorich555/web-ui-pack)
|
7 | [![install size](https://packagephobia.now.sh/badge?p=web-ui-pack)](https://packagephobia.now.sh/result?p=web-ui-pack)
|
8 | [![npm downloads](https://img.shields.io/npm/dm/web-ui-pack.svg?style=flat-square)](http://npm-stat.com/charts.html?package=web-ui-pack)
|
9 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
10 |
|
11 | ## Demo
|
12 |
|
13 | You can see demo [here](https://yegorich555.github.io/web-ui-pack) or just clone repo and run `npm i & npm start`
|
14 |
|
15 | ## Features
|
16 |
|
17 | - Possible to use **with/without** any frameworks like Angular, React, Vue etc. (because it's js-native logic)
|
18 | - Form/controls are ready to use and has built-in completed validation logic for any case that you can imagine (see [demo/controls](https://yegorich555.github.io/web-ui-pack/controls))
|
19 | - Focus on accessibility (best practices), other packages has low-accessibility support
|
20 | - High scalable and easy customizable (every component is developed to easy inherit and redefine/extend default logic)
|
21 | - Built-in css-variables to use custom color-themes with native ordinary styling (css, scss etc.)
|
22 | - Built-in Typescript (coverage types 100%)
|
23 | - Built-in `.jsx/.tsx` support (for React/Vue)
|
24 | - Supports different locales (based on [localeInfo](src/objects/localeInfo.ts) helper)
|
25 | - Well documented via JSDoc (use intellisense power of your editor to get details about each property/option/usage)
|
26 | - Optimized for webpack (build includes only used components and helpers via **side-effects** option)
|
27 | - Zero dependancy (don't need to wait for bug-fixing of other packages)
|
28 | - Always 100% test coverage via e2e and unit tests (it's must-have and always will be so)
|
29 | - Focus on performance (it's important to have low-memory consumption and fastest initialization)
|
30 |
|
31 | ## Why the package is so big
|
32 |
|
33 | It's developed with [Typescript](https://www.typescriptlang.org/) and has huge built-in documentation (JSDoc). Every method,property,event is documented well so you don't need extra resource to take an example to implement or configure elements. In build-result without comments you will see that it's small-enough
|
34 |
|
35 | ## Installing
|
36 |
|
37 | Using npm:
|
38 |
|
39 | ```npm
|
40 | npm install web-ui-pack
|
41 | ```
|
42 |
|
43 | ## TODO
|
44 |
|
45 | - [x] [Helpers](#helpers)
|
46 | - [x] [PopupElement](#example) [**demo**](https://yegorich555.github.io/web-ui-pack/popup)
|
47 | - [ ] Tooltip
|
48 | - [x] [SpinElement](src/spinElement.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/spin)
|
49 | - [x] [CircleElement](src/circleElement.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/circle)
|
50 | - [x] [FormElement](src/formElement.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/controls)
|
51 | - [x] [TextControl](src/controls/text.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/text)
|
52 | - [x] [Mask/pattern for controls](src/controls//text.mask.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/text)
|
53 | - [x] [PasswordControl](src/controls/password.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/password)
|
54 | - [x] [SwitchControl (Toggler)](src/controls/switch.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/switch)
|
55 | - [x] [CheckControl (Checkbox)](src/controls/check.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/check)
|
56 | - [ ] CheckTreeControl
|
57 | - [x] [RadioControl (RadioGroup)](src/controls/radio.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/radio)
|
58 | - [x] [SelectControl (ComboBox)](src/controls/select.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/select)
|
59 | - [x] [SelectManyControl (MultiSelect)](src/controls/selectMany.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/selectMany)
|
60 | - [x] [CalendarControl](src/controls/calendar.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/calendar)
|
61 | - [x] [DateControl](src/controls/date.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/date)
|
62 | - [x] [TimeControl](src/controls/time.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/time)
|
63 | - [ ] DateTimeControl ?
|
64 | - [x] [TextareaControl](src/controls/textarea.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/textarea)
|
65 | - [x] [NumberControl](src/controls/number.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/number)
|
66 | - [ ] SliderControl (progress bar)
|
67 | - [ ] FileControl
|
68 | - [ ] SearchControl ?
|
69 | - [ ] ImageControl (AvatarEditor)
|
70 | - [x] [DropdownElement](src/dropdownElement.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/dropdown)
|
71 | - [ ] MediaPlayer
|
72 | - [ ] ModalElement
|
73 | - [ ] ConfirmModalElement
|
74 | - [ ] FormModalElement
|
75 | - [ ] InfiniteScroll
|
76 | - [ ] VirtualScroll
|
77 | - [ ] CarouselElement (Slide show)
|
78 | - [ ] TableElement ?
|
79 |
|
80 | ## Components
|
81 |
|
82 | **Common rules**:
|
83 |
|
84 | 1. **Naming**
|
85 | - All components named as `WUP..Element`, `WUP..Control` and has `<wup-...>` html-tags
|
86 | - Public properties/options/events/methods startsWith `$...` (events `$onShow`, `$onHide`, methods `$show`, `$hide`, props like `$isShown` etc.)
|
87 | - Every component/class has static `$defaults` (common options for current class) and personal `$options` (per each component). See details in [example](#example)
|
88 | - `$options` are observed. So changing options affects on component immediately after empty timeout (every component has static `observedOptions` as set of watched options)
|
89 | - all custom `attributes` updates `$options` automatically. So `document.querySelector('wup-spin').$options.inline` equal to `<wup-spin inline />`
|
90 | 2. **Usage**
|
91 | - For webpack [sideEffects](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) switched on (for optimization). But **if you don't use webpack** don't import from `web-ui-pack` directly (due to tree-shaking can be not smart enough). Instead use `web-ui-pack/path-to-element`
|
92 | - Every component has a good JSDoc so go ahead and read details directly during the coding
|
93 | - Library compiled into ESNext. To avoid unexpected issues include this package into babel (use `exclude: /node_modules\/(?!(web-ui-pack)\/).*/` for babel-loader)
|
94 | 3. **Limitations**
|
95 | - In `jsx/tsx` instead of `className` use `class` attribute (React issue)
|
96 | - If you change custom html-attributes it will update `$options`, but if you change some option it removes related attribute (for performance reasons). Better to avoid usage attributes at all
|
97 | 4. **Inheritance**
|
98 | - Components are developed to be easy customized and inherited. Use ...$defaults of every class to configure behavior You can rewrite everything that you can imagine without digging a lot in a code. To be sure don't hesitate to take a look on \*.d.ts or source code (there are enough comments to clarify even weird/difficult cases)
|
99 | - All Components inherited from [WUPBaseElement](src/baseElement.ts) that extends default HTMLElement
|
100 | - All internal event-callbacks startsWith `got...` (gotReady, gotRemoved)
|
101 | - To redefine component just extend it and register with new html tag OR redefine default behavior via prototype functions (if $defaults are not included something). See details in [example](#example)
|
102 | - **Inheritance Tree**
|
103 | - [_HTMLElement_](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement)
|
104 | - [_BaseElement_](src/baseElement.ts)
|
105 | - [PopupElement](src/popupElement.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/popup)
|
106 | - [DropdownElement](src/dropdownElement.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/dropdown)
|
107 | - [SpinElement](src/spinElement.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/spin)
|
108 | - [CircleElement](src/circleElement.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/circle)
|
109 | - [FormElement](src/formElement.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/controls)
|
110 | - [_BaseControl_](src/controls/baseControl.ts)
|
111 | - [SwitchControl](src/controls/switch.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/switch)
|
112 | - [CheckControl](src/controls/check.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/check)
|
113 | - [RadioControl](src/controls/radio.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/radio)
|
114 | - [TextControl](src/controls/text.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/text)
|
115 | - [TextareaControl](src/controls/textarea.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/textarea)
|
116 | - [PasswordControl](src/controls/password.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/password)
|
117 | - [NumberControl](src/controls/number.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/number)
|
118 | - [_BaseComboControl_](src/controls/baseCombo.ts)
|
119 | - [SelectControl](src/controls/select.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/select)
|
120 | - [SelectManyControl](src/controls/selectMany.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/selectMany)
|
121 | - [DateControl](src/controls/date.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/date)
|
122 | - [TimeControl](src/controls/time.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/time)
|
123 | - [CalendarControl](src/controls/calendar.ts) [**demo**](https://yegorich555.github.io/web-ui-pack/control/calendar)
|
124 |
|
125 | ---
|
126 |
|
127 | ### Example
|
128 |
|
129 | Check how you can use every element/control (popupElement for example)
|
130 | Also check [code style example](/CODESTYLE.md)
|
131 |
|
132 | Typescript
|
133 |
|
134 | ```typescript
|
135 | import WUPPopupElement, { ShowCases } from "web-ui-pack/popup/popupElement";
|
136 |
|
137 | // redefine some defaults; WARN: you can change placement rules here without changing $options per each element!!!
|
138 | WUPPopupElement.$defaults.offset = [2, 2];
|
139 | WUPPopupElement.$defaults.minWidthByTarget = true;
|
140 | WUPPopupElement.$defaults.arrowEnable = true;
|
141 |
|
142 | // create element
|
143 | const el = document.createElement("wup-popup");
|
144 | // WARN el.$options is a observable-clone of WUPPopupElement.$defaults
|
145 | // WARN: ShowCases is const enum and import ShowCases available only in Typescript
|
146 | el.$options.showCase = ShowCases.onClick | ShowCases.onFocus; // show popup by target.click and/or target.focus events
|
147 | el.$options.target = document.querySelector("button");
|
148 | /*
|
149 | Placement can be $top, $right, $bottom, $left (top - above at the target etc.)
|
150 | every placement has align options: $start, $middle, $end (left - to align at start of target)
|
151 | also you can set $adjust to allow Reduce popup to fit layout
|
152 | */
|
153 | el.$options.placement = [
|
154 | WUPPopupElement.$placements.$top.$middle; // place at the top of target and align by vertical line
|
155 | WUPPopupElement.$placements.$bottom.$middle.$adjust, // adjust means 'ignore align to fit layout`
|
156 | WUPPopupElement.$placements.$bottom.$middle.$adjust.$resizeHeight, // resize means 'allow to resize to fit layout'
|
157 | ]
|
158 | document.body.append(el);
|
159 | ```
|
160 |
|
161 | HTML, JSX, TSX
|
162 |
|
163 | ```html
|
164 | <button id="btn1">Target</button>
|
165 | <!-- You can skip pointing attribute 'target' if popup appended after target -->
|
166 | <wup-popup target="#btn1" placement="top-start">Some content here</wup-popup>
|
167 | ```
|
168 |
|
169 | How to extend/override
|
170 |
|
171 | ```typescript
|
172 | /// popup.ts
|
173 |
|
174 | // you can override via prototypes
|
175 | const original = WUPPopupElement.prototype.goShow;
|
176 | WUPPopupElement.prototype.goShow = function customGoShow() {
|
177 | if (window.isBusy) {
|
178 | return null;
|
179 | }
|
180 | return original(...arguments);
|
181 | };
|
182 |
|
183 | /*** OR create extended class ***/
|
184 |
|
185 | class Popup extends WUPPopupElement {
|
186 | // take a look on definition of WUPPopupElement and you will find internals
|
187 | protected override goShow(showCase: WUPPopup.ShowCases): boolean {
|
188 | if (showCase === WUPPopup.ShowCases.onHover) {
|
189 | return false;
|
190 | }
|
191 | return super.goShow(showCase);
|
192 | }
|
193 | }
|
194 |
|
195 | const tagName = "ext-popup";
|
196 | customElements.define(tagName, Popup);
|
197 | // That's it. New Popup with custom tag 'ext-popup' is ready
|
198 |
|
199 | // add for intellisense (for *.ts only)
|
200 | declare global {
|
201 | // add element to document.createElement
|
202 | interface HTMLElementTagNameMap {
|
203 | [tagName]: Popup;
|
204 | }
|
205 |
|
206 | // add element for tsx/jsx intellisense
|
207 | namespace JSX {
|
208 | interface IntrinsicElements {
|
209 | [tagName]: IntrinsicElements["wup-popup"];
|
210 | }
|
211 | }
|
212 | }
|
213 | ```
|
214 |
|
215 | ---
|
216 |
|
217 | ### Helpers
|
218 |
|
219 | use `import focusFirst from "web-ui-pack/helpers/focusFirst"` etc.
|
220 | **WARN**: don't use `import {focusFirst} from "web-ui-pack;` because in this case the whole web-ui-pack module traps in compilation of dev-bundle and increases time of compilation
|
221 |
|
222 | - [**animateDropdown**](src/helpers/animateDropdown.ts) ⇒ `Animate (show/hide) element as dropdown via scale and counter-scale for children`
|
223 | - [**animateStack**](src/helpers/animateDropdown.ts) ⇒ `Animate (show/hide) every element via moving from target to own position`
|
224 | - [**dateCopyTime**](src/helpers/dateCopyTime.ts) ⇒ `Copy hh:mm:ss.fff part from B to A`
|
225 | - [**dateFromString**](src/helpers/dateFromString.ts) ⇒ `Returns parsed date from string based on pointed format`
|
226 | - [**dateToString**](src/helpers/dateToString.ts) ⇒ `Returns a string representation of a date-time according to pointed format`
|
227 | - [**findScrollParent**](src/helpers/findScrollParent.ts) ⇒ `Find first parent with active scroll X/Y`
|
228 | - [**findScrollParentAll**](src/helpers/findScrollParent.ts) ⇒ `Find all parents with active scroll X/Y`
|
229 | - [**focusFirst**](src/helpers/focusFirst.ts) ⇒ `Set focus on element or first possible nested element`
|
230 | - [**isIntoView**](src/helpers/isIntoView.ts) ⇒ `Check if element is visible in scrollable parents`
|
231 | - [**mathSumFloat**](src/helpers/math.ts) ⇒ `Sum without float-precision-issue`
|
232 | - [**mathScaleValue**](src/helpers/math.ts) ⇒ `Scale value from one range to another`
|
233 | - [**nestedProperty.set**](src/helpers/nestedProperty.ts) ⇒ `nestedProperty.set(obj, "value.nestedValue", 1) sets obj.value.nestedValue = 1`
|
234 | - [**nestedProperty.get**](src/helpers/nestedProperty.ts) ⇒ `nestedProperty.get(obj, "nested.val2", out?: {hasProp?: boolean} ) returns value from obj.nested.val2`
|
235 | - [**objectClone**](src/helpers/objectClone.ts) ⇒ `deep cloning object`
|
236 | - [**observer**](src/helpers/observer.md) ⇒ `converts object to observable (via Proxy) to allow listen for changes`
|
237 | - [**onEvent**](src/helpers/onEvent.ts) ⇒ `More strict (for Typescript) wrapper of addEventListener() that returns callback with removeListener()`
|
238 | - [**onFocusGot**](src/helpers/onFocusGot.ts) ⇒ `Fires when element/children takes focus once (fires again after onFocusLost on element)`
|
239 | - [**onScroll**](src/helpers/onScrollStop.ts) ⇒ `Handles wheel & touch events for custom scrolling`
|
240 | - [**onScrollStop**](src/helpers/onScrollStop.ts) ⇒ `Returns callback when scrolling is stopped (via checking scroll position every frame-render)`
|
241 | - [**onFocusLost**](src/helpers/onFocusLost.ts) ⇒ `Fires when element/children completely lost focus`
|
242 | - [**onSpy**](src/helpers/onSpy.ts) ⇒ `Spy on method-call of object`
|
243 | - [**promiseWait**](src/helpers/promiseWait.ts) ⇒ `Produce Promise during for "no less than pointed time"; it helps for avoding spinner blinking during the very fast api-request in case: pending > waitResponse > resetPending`
|
244 | - [**scrollIntoView**](src/helpers/scrollIntoView.ts) ⇒ `Scroll the HTMLElement's parent container such that the element is visible to the user and return promise by animation end`
|
245 | - [class **WUPScrolled**](src/helpers/scrolled.ts) ⇒ `Class makes pointed element scrollable and implements carousel-scroll behavior (appends new items during the scrolling). Supports swipe/pageUp/pageDown/mouseWheel events.`
|
246 | - [**stringLowerCount**](src/helpers/string.ts) ⇒ `Returns count of chars in lower case (for any language with ignoring numbers, symbols)`
|
247 | - [**stringUpperCount**](src/helpers/string.ts) ⇒ `Returns count of chars in upper case (for any language with ignoring numbers, symbols)`
|
248 | - [**stringPrettify**](src/helpers/string.ts) ⇒ `Changes camelCase, snakeCase, kebaCase text to user-friendly`
|
249 |
|
250 | ### Objects
|
251 |
|
252 | - [**localeInfo**](src/objects/localeInfo.ts) ⇒ `Locale-object with definitions related to user-locale`
|
253 | - [**TimeObject**](src/objects/timeObject.ts) ⇒ `Plane time object without date`
|
254 |
|
255 | ---
|
256 |
|
257 | ### Troubleshooting
|
258 |
|
259 | Be sure that you familiar with [common rules](#components)
|
260 |
|
261 | #### Library doesn't work in some browsers
|
262 |
|
263 | > web-ui-pack is compiled to ESNext. So some features maybe don't exist in browsers. To resolve it include the lib into babel-loader (for webpack check module.rules...exclude sections
|
264 | >
|
265 | > ```js
|
266 | > // webpack.config.js
|
267 | > {
|
268 | > test: /\.(js|jsx)$/,
|
269 | > exclude: (() => {
|
270 | > // these packages must be included to change according to browserslist
|
271 | > const include = ["web-ui-pack"];
|
272 | > return (v) => v.includes("node_modules") && !include.some((lib) => v.includes(lib));
|
273 | > })(),
|
274 | > use: [ "babel-loader", ],
|
275 | > },
|
276 | > ```
|
277 |
|
278 | #### UI doesn't recognize html tags like `<wup-popup />` etc
|
279 |
|
280 | > It's possible if you missed import or it was removed by optimizer of wepback etc. To fix this you need to force import at least once
|
281 | >
|
282 | > ```js
|
283 | > import { WUPSelectControl, WUPTextControl } from "web-ui-pack";
|
284 | >
|
285 | > // this force webpack not ignore imports (if imported used only as html-tags without direct access)
|
286 | > const sideEffect = WUPTextControl && WUPSelectControl;
|
287 | > !sideEffect && console.error("Missed"); // It's required otherwise import is ignored by webpack
|
288 | > // or
|
289 | > WUPTextControl.$defaults.validateDebounceMs = 500;
|
290 | > WUPSelectControl.$defaults.validateDebounceMs = 500;
|
291 | > // etc.
|
292 | > ```
|
293 |
|
294 | ### FAQ
|
295 |
|
296 | see [demo/faq](https://yegorich555.github.io/web-ui-pack/faq)
|