Skip to content

Commit 92123fb

Browse files
committed
[add] WebField class, mixin & types
[refactor] CSSObject type & stringifyCSS function [optimize] upgrade Upstream packages
1 parent 7ea700c commit 92123fb

14 files changed

Lines changed: 241 additions & 79 deletions

.eslintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
1616
"rules": {
17-
"no-unused-vars": "warn"
17+
"no-unused-vars": "warn",
18+
"no-undef": "warn"
1819
}
1920
}

MobX/ReadMe.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
## Installation
1010

1111
```shell
12-
npm install web-cell mobx mobx-web-cell
12+
npm install web-cell mobx@5 mobx-web-cell
1313
```
1414

1515
## Usage

ReadMe.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,13 @@ npm install parcel-bundler -D
4747
`source/index.html`
4848

4949
```html
50-
<script src="https://polyfill.io/v3/polyfill.min.js?flags=gated&features=Object.fromEntries%2CArray.prototype.flat"></script>
50+
<script
51+
crossorigin
52+
src="https://polyfill.app/api/polyfill?features=es.array.flat,es.object.from-entries"
53+
></script>
5154
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2.4.4/webcomponents-bundle.min.js"></script>
5255
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2.4.4/custom-elements-es5-adapter.js"></script>
56+
<script src="https://cdn.jsdelivr.net/npm/element-internals-polyfill@0.0.23/dist/index.min.js"></script>
5357

5458
<script src="source/SubTag.tsx"></script>
5559
<script src="source/TestTag.tsx"></script>
@@ -145,6 +149,16 @@ export class TestTag extends mixin<Props, State>() {
145149
}
146150
```
147151

152+
## Basic knowledge
153+
154+
- [Web components](https://developer.mozilla.org/en-US/docs/Web/Web_Components)
155+
- [Custom elements](https://developers.google.cn/web/fundamentals/web-components/customelements)
156+
- [Shadow DOM](https://developers.google.cn/web/fundamentals/web-components/shadowdom)
157+
- [Element Internals](https://web.dev/more-capable-form-controls/)
158+
- [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables)
159+
- [ECMAScript 6+](http://es6-features.org/)
160+
- [TypeScript 4+][3]
161+
148162
## Life Cycle hooks
149163

150164
1. [`connectedCallback`](https://web-cell.dev/WebCell/interfaces/webcellcomponent.html#connectedcallback)
@@ -159,6 +173,14 @@ export class TestTag extends mixin<Props, State>() {
159173

160174
6. [`updatedCallback`](https://web-cell.dev/WebCell/interfaces/webcellcomponent.html#updatedcallback)
161175

176+
7. [`formAssociatedCallback`](https://web-cell.dev/WebCell/interfaces/webfieldcomponent.html#formassociatedcallback)
177+
178+
8. [`formDisabledCallback`](https://web-cell.dev/WebCell/interfaces/webfieldcomponent.html#formdisabledcallback)
179+
180+
9. [`formResetCallback`](https://web-cell.dev/WebCell/interfaces/webfieldcomponent.html#formresetcallback)
181+
182+
10. [`formStateRestoreCallback`](https://web-cell.dev/WebCell/interfaces/webfieldcomponent.html#formstaterestorecallback)
183+
162184
## Scaffolds
163185

164186
1. [Basic](https://github.com/EasyWebApp/scaffold)

package.json

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "web-cell",
3-
"version": "2.2.0",
3+
"version": "2.3.0-beta.0",
44
"description": "Web Components engine based on JSX & TypeScript",
55
"keywords": [
66
"web",
@@ -29,24 +29,25 @@
2929
"web-utility": "^1.8.0"
3030
},
3131
"devDependencies": {
32-
"@types/core-js": "^2.5.3",
33-
"@types/jest": "^26.0.10",
34-
"@types/jsdom": "^16.2.3",
35-
"@typescript-eslint/parser": "^3.10.1",
36-
"eslint": "^7.7.0",
37-
"eslint-config-prettier": "^6.11.0",
32+
"@types/core-js": "^2.5.4",
33+
"@types/jest": "^26.0.14",
34+
"@types/jsdom": "^16.2.4",
35+
"@typescript-eslint/parser": "^4.4.1",
36+
"element-internals-polyfill": "0.0.23",
37+
"eslint": "^7.11.0",
38+
"eslint-config-prettier": "^6.12.0",
3839
"eslint-plugin-prettier": "^3.1.4",
39-
"husky": "^4.2.5",
40-
"jest": "^26.4.2",
41-
"lint-staged": "^10.2.13",
40+
"husky": "^4.3.0",
41+
"jest": "^26.5.3",
42+
"lint-staged": "^10.4.0",
4243
"open-cli": "^6.0.1",
4344
"parcel-bundler": "^1.12.4",
44-
"prettier": "^2.1.1",
45-
"snabbdom": "^1.0.1",
46-
"ts-jest": "^26.3.0",
47-
"typedoc": "^0.18.0",
48-
"typescript": "^4.0.2",
49-
"web-utility": "^1.8.0"
45+
"prettier": "^2.1.2",
46+
"snabbdom": "^2.1.0",
47+
"ts-jest": "^26.4.1",
48+
"typedoc": "^0.19.2",
49+
"typescript": "^4.0.3",
50+
"web-utility": "^1.8.2"
5051
},
5152
"scripts": {
5253
"lint": "lint-staged",
@@ -59,16 +60,12 @@
5960
"prepublishOnly": "npm test && npm run build"
6061
},
6162
"lint-staged": {
62-
"source/**/*.{ts,tsx}": [
63-
"eslint --fix"
64-
],
63+
"source/**/*.{ts,tsx}": "eslint --fix",
6564
"*.{js,ts,tsx}": [
6665
"prettier --write",
6766
"eslint --fix --rule 'require-atomic-updates: 1'"
6867
],
69-
"*.{html,md,css,json,yml}": [
70-
"prettier --write"
71-
]
68+
"*.{html,md,css,json,yml}": "prettier --write"
7269
},
7370
"husky": {
7471
"hooks": {
@@ -83,7 +80,7 @@
8380
"tabWidth": 4
8481
},
8582
"jest": {
86-
"preset": "ts-jest",
83+
"preset": "ts-jest/presets/js-with-ts",
8784
"globals": {
8885
"ts-jest": {
8986
"tsConfig": "test/tsconfig.json",

source/WebCell.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ export interface WebCellComponent<P extends WebCellProps = WebCellProps, S = {}>
6060

6161
export interface WebCellClass<P extends WebCellProps = WebCellProps, S = {}>
6262
extends Partial<ComponentMeta> {
63-
new (): WebCellComponent<P, S>;
63+
new (options?: ShadowRootInit): WebCellComponent<P, S>;
6464
attributes?: string[];
6565
eventDelegaters?: DOMEventDelegater[];
6666
}
6767

68-
export function mixin<P = WebCellProps, S = {}>(
68+
export function mixin<P extends WebCellProps = WebCellProps, S = {}>(
6969
superClass = HTMLElement
7070
): WebCellClass<P, S> {
7171
class WebCell extends superClass implements WebCellComponent<P, S> {

source/WebField.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import type {} from 'element-internals-polyfill';
2+
import { BaseFieldProps } from 'web-utility';
3+
4+
import { WebCellProps } from './utility';
5+
import { mixin, WebCellClass, WebCellComponent } from './WebCell';
6+
import { watch, attribute } from './decorator';
7+
8+
export interface WebFieldProps extends BaseFieldProps, WebCellProps {}
9+
10+
export interface WebFieldComponent<
11+
P extends WebFieldProps = WebFieldProps,
12+
S = {}
13+
> extends WebCellComponent<P, S> {
14+
/**
15+
* Called when the browser associates the element with a form element,
16+
* or disassociates the element from a form element.
17+
*/
18+
formAssociatedCallback?(form: HTMLFormElement): void;
19+
/**
20+
* Called after the disabled state of the element changes,
21+
* either because the disabled attribute of this element was added or removed;
22+
* or because the disabled state changed on a `<fieldset>` that's an ancestor of this element.
23+
*
24+
* @param disabled This parameter represents the new disabled state of the element.
25+
*/
26+
formDisabledCallback?(disabled: boolean): void;
27+
/**
28+
* Called after the form is reset.
29+
* The element should reset itself to some kind of default state.
30+
*/
31+
formResetCallback?(): void;
32+
/**
33+
* Called in one of two circumstances:
34+
* - When the browser restores the state of the element (for example, after a navigation, or when the browser restarts). The `mode` argument is `"restore"` in this case.
35+
* - When the browser's input-assist features such as form autofilling sets a value. The `mode` argument is `"autocomplete"` in this case.
36+
*
37+
* @param state The type of this argument depends on how the `this.internals.setFormValue()` method was called.
38+
* @param mode
39+
*/
40+
formStateRestoreCallback?(
41+
state: string | File | FormData,
42+
mode: 'restore' | 'autocomplete'
43+
): void;
44+
}
45+
46+
export interface WebFieldClass<P extends WebFieldProps = WebFieldProps, S = {}>
47+
extends WebCellClass<P, S> {
48+
new (options?: ShadowRootInit): WebFieldComponent<P, S>;
49+
}
50+
51+
export function mixinForm<P extends WebFieldProps = WebFieldProps, S = {}>(
52+
superClass = HTMLElement
53+
): WebFieldClass<P, S> {
54+
class WebField
55+
extends mixin<P, S>(superClass)
56+
implements WebFieldComponent<P, S> {
57+
static formAssociated = true;
58+
59+
@attribute
60+
@watch
61+
name: string;
62+
63+
@watch
64+
value: string;
65+
66+
@attribute
67+
@watch
68+
required: boolean;
69+
70+
@attribute
71+
@watch
72+
disabled: boolean;
73+
74+
@attribute
75+
@watch
76+
placeholder: string;
77+
78+
@attribute
79+
@watch
80+
autofocus: boolean;
81+
82+
set defaultValue(raw: string) {
83+
this.setAttribute('value', raw);
84+
85+
this.props.value ?? (this.value = raw);
86+
}
87+
88+
get defaultValue() {
89+
return this.getAttribute('value');
90+
}
91+
92+
protected internals = this.attachInternals();
93+
94+
get form() {
95+
return this.internals.form;
96+
}
97+
get validity() {
98+
return this.internals.validity;
99+
}
100+
get validationMessage() {
101+
return this.internals.validationMessage;
102+
}
103+
get willValidate() {
104+
return this.internals.willValidate;
105+
}
106+
checkValidity() {
107+
return this.internals.checkValidity();
108+
}
109+
reportValidity() {
110+
return this.internals.reportValidity();
111+
}
112+
}
113+
return WebField;
114+
}

source/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './utility';
22
export * from './renderer';
33
export * from './decorator';
44
export * from './WebCell';
5+
export * from './WebField';

source/renderer.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { init } from 'snabbdom/src/package/init';
2-
import { attributesModule } from 'snabbdom/src/package/modules/attributes';
3-
import { propsModule } from 'snabbdom/src/package/modules/props';
4-
import { datasetModule } from 'snabbdom/src/package/modules/dataset';
5-
import { classModule } from 'snabbdom/src/package/modules/class';
6-
import { styleModule } from 'snabbdom/src/package/modules/style';
7-
import { eventListenersModule } from 'snabbdom/src/package/modules/eventlisteners';
8-
import { VNode } from 'snabbdom/src/package/vnode';
9-
import { toVNode } from 'snabbdom/src/package/tovnode';
10-
import { VNodeChildElement, h as createElement } from 'snabbdom/src/package/h';
1+
import { init } from 'snabbdom/build/package/init';
2+
import { attributesModule } from 'snabbdom/build/package/modules/attributes';
3+
import { propsModule } from 'snabbdom/build/package/modules/props';
4+
import { datasetModule } from 'snabbdom/build/package/modules/dataset';
5+
import { classModule } from 'snabbdom/build/package/modules/class';
6+
import { styleModule } from 'snabbdom/build/package/modules/style';
7+
import { eventListenersModule } from 'snabbdom/build/package/modules/eventlisteners';
8+
import { VNode } from 'snabbdom/build/package/vnode';
9+
import { toVNode } from 'snabbdom/build/package/tovnode';
10+
import {
11+
VNodeChildElement,
12+
h as createElement
13+
} from 'snabbdom/build/package/h';
1114

1215
import {
1316
WebCellElement,
@@ -18,8 +21,8 @@ import {
1821
} from './utility';
1922
import { WebCellClass } from './WebCell';
2023

21-
export { VNode } from 'snabbdom/src/package/vnode';
22-
export { VNodeChildElement } from 'snabbdom/src/package/h';
24+
export { VNode } from 'snabbdom/build/package/vnode';
25+
export { VNodeChildElement } from 'snabbdom/build/package/h';
2326

2427
export const patch = init([
2528
attributesModule,

source/utility/DOM.ts

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,29 @@
1-
export type CSSValue = string | number | CSSObject;
2-
3-
export interface CSSObject {
4-
[key: string]: CSSValue;
5-
}
6-
7-
export function stringifyCSS(data: CSSObject, depth = 0): string {
8-
const indent = ' '.repeat(4 * depth),
9-
[simple, nested] = Object.entries(data).reduce(
10-
([simple, nested], [key, value]) => {
11-
if (typeof value !== 'object') simple.push([key, value]);
12-
else nested.push([key, value]);
13-
14-
return [simple, nested];
15-
},
16-
[[], []] as CSSValue[][][]
17-
);
18-
19-
return simple[0]
20-
? simple.map(([key, value]) => `${indent}${key}: ${value};\n`).join('')
21-
: nested
22-
.map(
23-
([key, value]) => `${indent}${key} {
24-
${stringifyCSS(value as CSSObject, depth + 1)}${indent}}\n`
25-
)
26-
.join('');
1+
import { HTMLProps } from 'web-utility/source/DOM-type';
2+
import { toHyphenCase } from './data';
3+
4+
export type CSSRule = Record<string, HTMLProps['style']>;
5+
export type CSSObject = CSSRule | Record<string, CSSRule>;
6+
7+
export function stringifyCSS(
8+
data: HTMLProps['style'] | CSSObject,
9+
depth = 0,
10+
indent = ' '
11+
): string {
12+
const padding = indent.repeat(depth);
13+
14+
return Object.entries(data)
15+
.map(([key, value]) =>
16+
typeof value !== 'object'
17+
? `${padding}${toHyphenCase(key)}: ${value};`
18+
: `${padding}${key} {
19+
${stringifyCSS(value as CSSObject, depth + 1, indent)}
20+
${padding}}`
21+
)
22+
.join('\n');
2723
}
2824

2925
const spawn = document.createElement('template'),
30-
cache: Record<string, any> = {};
26+
cache: Record<string, Element> = {};
3127

3228
export function templateOf(tagName: string) {
3329
if (cache[tagName]) return cache[tagName];

source/utility/vDOM.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { HTMLProps } from 'web-utility/source/DOM-type';
2-
import { VNodeChildElement } from 'snabbdom/src/package/h';
2+
import { VNodeChildElement } from 'snabbdom/build/package/h';
33
import { WebCellComponent } from '../WebCell';
44

55
export interface WebCellData extends HTMLProps {

0 commit comments

Comments
 (0)