├── .eslintignore
├── demo
├── src
│ ├── index.ts
│ ├── vite-env.d.ts
│ ├── App.css
│ ├── index.css
│ └── App.tsx
├── vite.config.ts
├── package.json
├── public
│ ├── index.html
│ ├── vite.svg
│ ├── jwc.svg
│ └── jwc-dark.svg
├── index.html
└── LICENSE
├── packages
├── shared
│ ├── index.ts
│ ├── src
│ │ ├── constants
│ │ │ ├── index.ts
│ │ │ └── metas.constant.ts
│ │ ├── index.ts
│ │ └── types
│ │ │ ├── index.ts
│ │ │ ├── watcher.interface.ts
│ │ │ ├── jwc-element.interface.ts
│ │ │ └── local-jsx.ts
│ └── package.json
├── runtime
│ ├── v-dom
│ │ ├── fragment.ts
│ │ ├── index.ts
│ │ ├── h.ts
│ │ ├── vnode.ts
│ │ ├── patch.ts
│ │ └── diff.ts
│ ├── dom
│ │ ├── index.ts
│ │ ├── attrs.ts
│ │ └── dom.ts
│ ├── index.ts
│ ├── utils
│ │ ├── others.ts
│ │ └── attrs.ts
│ ├── package.json
│ └── LICENSE
├── core
│ ├── src
│ │ ├── index.ts
│ │ ├── decorators
│ │ │ ├── props.ts
│ │ │ ├── event.ts
│ │ │ ├── index.ts
│ │ │ └── component.ts
│ │ └── wc-class.ts
│ ├── index.ts
│ ├── rollup.config.mjs
│ ├── LICENSE
│ └── package.json
└── reactively
│ ├── index.ts
│ ├── src
│ ├── css-tag.ts
│ ├── reactive.ts
│ ├── call-reactive.ts
│ └── define-reactive.ts
│ ├── package.json
│ └── LICENSE
├── pnpm-workspace.yaml
├── .prettierrc
├── .vscode
└── launch.json
├── .gitignore
├── .github
├── renovate.json
└── workflows
│ └── release.yml
├── .eslintrc.js
├── tsconfig.json
├── package.json
├── README.md
├── LICENSE
└── CHANGELOG.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/dist/**
--------------------------------------------------------------------------------
/demo/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./App";
2 |
--------------------------------------------------------------------------------
/packages/shared/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./src";
2 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - packages/*
3 | - demo
--------------------------------------------------------------------------------
/demo/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/runtime/v-dom/fragment.ts:
--------------------------------------------------------------------------------
1 | export const Fragment = "Fragment";
2 |
--------------------------------------------------------------------------------
/packages/shared/src/constants/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./metas.constant";
2 |
--------------------------------------------------------------------------------
/packages/runtime/dom/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./attrs";
2 | export * from "./dom";
3 |
--------------------------------------------------------------------------------
/packages/shared/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./constants";
2 | export * from "./types";
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "useTabs": true,
4 | "endOfLine": "auto"
5 | }
--------------------------------------------------------------------------------
/packages/runtime/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./v-dom";
2 | export * from "./utils/attrs";
3 | export * from "./dom";
4 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | import "reflect-metadata";
2 |
3 | export * from "./wc-class";
4 | export * from "./decorators";
5 |
--------------------------------------------------------------------------------
/packages/shared/src/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./jwc-element.interface";
2 | export * from "./watcher.interface";
3 | export * from "./local-jsx";
4 |
--------------------------------------------------------------------------------
/packages/runtime/utils/others.ts:
--------------------------------------------------------------------------------
1 | export function camelToDash(str: string) {
2 | return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
3 | }
4 |
--------------------------------------------------------------------------------
/packages/core/index.ts:
--------------------------------------------------------------------------------
1 | export * from "@jwcjs/runtime";
2 | export * from "@jwcjs/shared";
3 | export * from "@jwcjs/reactively";
4 | export * from "./src";
5 |
--------------------------------------------------------------------------------
/packages/runtime/v-dom/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./diff";
2 | export * from "./h";
3 | export * from "./fragment";
4 | export * from "./vnode";
5 | export * from "./patch";
6 |
--------------------------------------------------------------------------------
/packages/reactively/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./src/call-reactive";
2 | export * from "./src/define-reactive";
3 | export * from "./src/reactive";
4 | export * from "./src/css-tag";
5 |
--------------------------------------------------------------------------------
/demo/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | export default defineConfig({
3 | resolve: {
4 | alias: {
5 | jwcjs: "../../packages/core/index.ts",
6 | },
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/packages/runtime/v-dom/h.ts:
--------------------------------------------------------------------------------
1 | import { VNode } from "./vnode";
2 |
3 | export function h(tag: string, props: any, ...children: any[]): VNode {
4 | return new VNode(tag, props, children);
5 | }
6 |
--------------------------------------------------------------------------------
/packages/shared/src/constants/metas.constant.ts:
--------------------------------------------------------------------------------
1 | export const COMPONENT_PROP_METADATA_KEY = "jwc:component:props";
2 | export const COMPONENT_WATCHER_METADATA_KEY = "jwc:component:watchers";
3 | export const COMPONENT_EVENT_METADATA_KEY = "jwc:component:events";
4 |
--------------------------------------------------------------------------------
/packages/runtime/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jwcjs/runtime",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC"
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "type": "chrome",
6 | "request": "launch",
7 | "name": "针对 localhost 启动 Chrome",
8 | "url": "http://localhost:5173",
9 | "webRoot": "${workspaceFolder}"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/packages/core/src/decorators/props.ts:
--------------------------------------------------------------------------------
1 | import { defineProps } from "@jwcjs/reactively";
2 | import { PropOptions } from "@jwcjs/shared";
3 |
4 | export function Prop(options: PropOptions): PropertyDecorator {
5 | return (target, key) => {
6 | defineProps(target, String(key), options);
7 | };
8 | }
9 |
--------------------------------------------------------------------------------
/packages/core/src/decorators/event.ts:
--------------------------------------------------------------------------------
1 | import { defineEvent } from "@jwcjs/reactively";
2 |
3 | export function Event(event: string): MethodDecorator {
4 | return (target, key, descriptor) => {
5 | defineEvent(target, {
6 | name: event,
7 | handler: descriptor.value,
8 | });
9 | };
10 | }
11 |
--------------------------------------------------------------------------------
/packages/reactively/src/css-tag.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Create a style element with the given CSS text.
3 | * Returns a DOM element.
4 | *
5 | * @param {string} css
6 | */
7 | export function createCSSElement(css: string) {
8 | const style = document.createElement("style");
9 | style.appendChild(document.createTextNode(css));
10 | return style;
11 | }
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/dist
2 | **/build
3 | **/out
4 | **/node_modules
5 | **/bower_components
6 | **/jspm_packages
7 | **/flow-typed
8 | **/coverage
9 | **/target
10 | **/.idea
11 | **/vendor
12 | **/tmp
13 | **/*.log
14 | **/*.log.*
15 | **/*.swp
16 | **/*.swo
17 | **/*.swn
18 | ~$*
19 | *.~
20 | *.bak
21 | *.tmp
22 | **/.DS_Store
23 | **/Thumbs.db
24 | **/Desktop.ini
25 |
--------------------------------------------------------------------------------
/packages/reactively/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jwcjs/reactively",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@jwcjs/shared": "workspace:*"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "vite": "^4.0.1",
16 | "jwcjs": "workspace:*"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/runtime/dom/attrs.ts:
--------------------------------------------------------------------------------
1 | export function updateAttributes(el: Node, attributes: { [key: string]: any }) {
2 | for (const key in attributes) {
3 | if (key === "style") {
4 | for (const styleKey in attributes[key]) {
5 | (el as HTMLElement).style[styleKey] = attributes[key][styleKey];
6 | }
7 | } else {
8 | (el as HTMLElement).setAttribute(key, attributes[key]);
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/shared/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jwcjs/shared",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "@jwcjs/runtime": "workspace:*",
14 | "csstype": "^3.1.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + Jwc.js App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + Jwc.js App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/shared/src/types/watcher.interface.ts:
--------------------------------------------------------------------------------
1 | export interface WatcherOptions {
2 | /**
3 | * Whether the watcher should be called immediately.
4 | */
5 | immediate?: boolean;
6 |
7 | /**
8 | * Callback function.
9 | */
10 | callback?: any;
11 |
12 | /**
13 | * Callback name.
14 | * if handler is not provided, the callback will be called.
15 | */
16 | callbackName?: string;
17 |
18 | /**
19 | * deep watch
20 | */
21 | deep?: boolean;
22 | }
23 |
--------------------------------------------------------------------------------
/packages/core/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "rollup";
2 | import nodeResolve from "@rollup/plugin-node-resolve";
3 | import typescript from "rollup-plugin-ts";
4 | import { minify } from "rollup-plugin-esbuild";
5 |
6 | export default defineConfig({
7 | input: "./index.ts",
8 | output: {
9 | file: "./dist/index.js",
10 | },
11 | plugins: [
12 | nodeResolve(),
13 | typescript({
14 | tsconfig: "../../tsconfig.json",
15 | }),
16 | minify(),
17 | ],
18 | });
19 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "labels": ["dependencies"],
4 | "extends": [
5 | "config:base",
6 | ":automergePatch",
7 | ":automergeTypes",
8 | ":automergeTesters",
9 | ":automergeLinters",
10 | ":rebaseStalePrs"
11 | ],
12 | "packageRules": [
13 | {
14 | "updateTypes": ["major"],
15 | "labels": ["dependencies", "UPDATE-MAJOR"]
16 | }
17 | ],
18 | "enabled": true
19 | }
20 |
--------------------------------------------------------------------------------
/packages/core/src/decorators/index.ts:
--------------------------------------------------------------------------------
1 | import { defineWatcher } from "@jwcjs/reactively";
2 | import { WatcherOptions } from "@jwcjs/shared";
3 |
4 | export * from "./props";
5 | export * from "./event";
6 | export * from "./component";
7 |
8 | export function Watcher(options: WatcherOptions): MethodDecorator {
9 | return (target, key, descriptor) => {
10 | defineWatcher(target, {
11 | ...options,
12 | callback: descriptor.value,
13 | callbackName: String(key),
14 | });
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/packages/runtime/utils/attrs.ts:
--------------------------------------------------------------------------------
1 | import { VNode } from "../v-dom";
2 |
3 | export function removeAttrs(
4 | vnode: VNode | VNode[],
5 | attrs: string[] = ["isNew"]
6 | ) {
7 | if (vnode instanceof Array) {
8 | for (const node of vnode) {
9 | removeAttrs(node, attrs);
10 | }
11 | return vnode;
12 | }
13 | for (const attr of attrs) {
14 | vnode[attr] ? (vnode[attr] = false) : null;
15 | }
16 | if (vnode.children) {
17 | for (const child of Object.values(vnode.children)) {
18 | removeAttrs(child, attrs);
19 | }
20 | }
21 | return vnode;
22 | }
23 |
--------------------------------------------------------------------------------
/packages/core/src/decorators/component.ts:
--------------------------------------------------------------------------------
1 | import { CustomElementProps } from "@jwcjs/shared";
2 |
3 | export function Component(options: CustomElementProps) {
4 | return function (_class: any) {
5 | // set the default value for the isMounted option
6 | if (options.isMounted === undefined) options.isMounted = true;
7 | if (customElements.get(options.name)) {
8 | console.warn(`The component ${options.name} already exists.`);
9 | }
10 | _class.$options = options; // add the options to the class
11 | customElements.define(options.name, _class, options.options || {});
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/packages/reactively/src/reactive.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * We use getter and setter to make sure that the value is always
3 | * up to date. This is important because we want to be able to
4 | * use the value in a template.
5 | *
6 | * We use Reflect.get and Reflect.set to make sure that we can
7 | * use the value in a template.
8 | */
9 | export function reactive(target: T): T {
10 | // console.log(target);
11 |
12 | return new Proxy(target, {
13 | get(target, key) {
14 | return Reflect.get(target, key);
15 | },
16 | set(target, key, value) {
17 | Reflect.set(target, key, value);
18 | return true;
19 | },
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/packages/runtime/v-dom/vnode.ts:
--------------------------------------------------------------------------------
1 | export class VNode {
2 | tagName: string;
3 | attributes: {
4 | [key: string]: any;
5 | };
6 | children: VNode[];
7 |
8 | // to assign status to the vnode
9 | isNew: boolean;
10 | isUpdated: boolean;
11 | isDeleted: boolean;
12 |
13 | // to record the dom node
14 | el: Node | null;
15 |
16 | constructor(
17 | tagName: string,
18 | attributes: { [key: string]: any },
19 | children: VNode[]
20 | ) {
21 | this.tagName = tagName;
22 | this.attributes = attributes;
23 | this.children = children;
24 | this.isNew = true;
25 | this.isUpdated = false;
26 | this.isDeleted = false;
27 | this.el = document.createElement(tagName);
28 | }
29 |
30 | public static isVNode(vnode: any): vnode is VNode {
31 | return vnode instanceof VNode;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-undef
2 | module.exports = {
3 | env: {
4 | es2021: true,
5 | },
6 | extends: [
7 | "eslint:recommended",
8 | "plugin:@typescript-eslint/recommended",
9 | "plugin:prettier/recommended",
10 | ],
11 | overrides: [],
12 | parser: "@typescript-eslint/parser",
13 | parserOptions: {
14 | ecmaVersion: "latest",
15 | sourceType: "module",
16 | },
17 | plugins: ["@typescript-eslint"],
18 | rules: {
19 | "@typescript-eslint/no-unused-vars": "off",
20 | "@typescript-eslint/no-explicit-any": "off",
21 | "@typescript-eslint/no-non-null-assertion": "off",
22 | "@typescript-eslint/ban-ts-comment": "off",
23 | "@typescript-eslint/ban-types": "off",
24 | "@typescript-eslint/no-this-alias": "off",
25 | "@typescript-eslint/no-empty-function": "off",
26 | "no-self-assign": "off",
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/packages/reactively/src/call-reactive.ts:
--------------------------------------------------------------------------------
1 | import { CustomElementProps } from "@jwcjs/shared";
2 | import { reactive } from "./reactive";
3 |
4 | /**
5 | * There are two types of reactive functions:
6 | * 1. data
7 | * 2. event
8 | */
9 | export type ReactiveData = CustomElementProps;
10 |
11 | export type ReactiveEvent = {
12 | name: any;
13 | handler: any;
14 | };
15 |
16 | export function defineProxy(target: any, propertyKey: string, value: any) {
17 | const reactiveValue = reactive(value);
18 | Object.defineProperty(target, propertyKey, {
19 | get() {
20 | return reactiveValue.value;
21 | },
22 | set(newValue) {
23 | reactiveValue.value = newValue;
24 | this.shouldUpdate = true;
25 | // callback the new value to the watcher
26 | this.watchersMap.get(propertyKey)?.forEach((watcher: any) => {
27 | watcher.callback.call(this, newValue, value);
28 | });
29 | this.updateDiff();
30 | },
31 | });
32 | }
33 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "alwaysStrict": true,
4 | "allowSyntheticDefaultImports": true,
5 | "allowUnreachableCode": false,
6 | "declaration": true,
7 | "experimentalDecorators": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "jsx": "react",
10 | "jsxFactory": "h",
11 | "jsxFragmentFactory": "Fragment",
12 | "lib": ["dom", "es2018", "esnext.array"],
13 | "module": "esnext",
14 | "moduleResolution": "node",
15 | "noImplicitAny": false,
16 | "noImplicitOverride": true,
17 | "emitDecoratorMetadata": true,
18 | "noImplicitReturns": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": false,
21 | "outDir": "build",
22 | "pretty": true,
23 | "resolveJsonModule": true,
24 | "sourceMap": true,
25 | "target": "ES6",
26 | "useUnknownInCatchVariables": true,
27 | "baseUrl": "."
28 | },
29 | "exclude": ["**/dist/**"]
30 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jwcjs/monorepo",
3 | "author": "wibus-wee ",
4 | "version": "0.1.0-alpha.1",
5 | "license": "MIT",
6 | "private": true,
7 | "scripts": {
8 | "lint": "eslint ./packages",
9 | "build:core": "pnpm -C packages/core run build",
10 | "build-packages": "npm run build:core",
11 | "bump": "bump-monorepo",
12 | "prepare": "npm run build-packages"
13 | },
14 | "devDependencies": {
15 | "@types/node": "^18.11.17",
16 | "@typescript-eslint/eslint-plugin": "^5.46.1",
17 | "@typescript-eslint/parser": "^5.46.1",
18 | "@wibus/bump-monorepo": "1.0.0-alpha.2",
19 | "eslint": "^8.29.0",
20 | "eslint-config-prettier": "^8.5.0",
21 | "eslint-plugin-prettier": "^4.2.1",
22 | "husky": "^8.0.2",
23 | "lint-staged": "^13.1.0",
24 | "pnpm": "^7.18.2",
25 | "prettier": "^2.8.1",
26 | "rimraf": "^4.0.0",
27 | "tslib": "^2.4.1",
28 | "typescript": "^4.9.4"
29 | },
30 | "bump": {
31 | "activePackages": [
32 | "core"
33 | ],
34 | "publish": true,
35 | "message": "release: %s"
36 | }
37 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | ### Simple. JSX. Web Components.
13 |
14 | [](https://www.npmjs.com/package/jwcjs)
15 |
16 | 🎨 A JavaScript framework for using JSX to write web components on the web.
17 |
18 |
19 |
20 | ## Getting Started
21 |
22 | Follow the documentation at [jwc.js.org](https://jwc.js.org)!
23 |
24 | > _**This project is still under development, so use it with caution in production**_
25 |
26 |
27 | ## License
28 |
29 | [MIT](https://opensource.org/licenses/MIT)
30 |
31 | Made with ❤️ by Wibus and AkaraChen
32 |
33 | Copyright © 2022-PRESENT Jwc.js
34 |
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Jwc.js
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 |
--------------------------------------------------------------------------------
/demo/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Jwc.js
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 |
--------------------------------------------------------------------------------
/packages/core/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Jwc.js
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 |
--------------------------------------------------------------------------------
/packages/runtime/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Jwc.js
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 |
--------------------------------------------------------------------------------
/packages/reactively/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Jwc.js
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 |
--------------------------------------------------------------------------------
/demo/src/App.css:
--------------------------------------------------------------------------------
1 |
2 | :host {
3 | max-width: 1280px;
4 | margin: 0 auto;
5 | padding: 2rem;
6 | text-align: center;
7 | }
8 |
9 | .logo {
10 | height: 6em;
11 | padding: 1.5em;
12 | will-change: filter;
13 | transition: filter 0.25s;
14 | }
15 | .logo:hover {
16 | filter: drop-shadow(0 0 2em #646cffaa);
17 | }
18 | .logo.jwc:hover {
19 | filter: drop-shadow(0 0 2em rgb(0, 0, 0));
20 | }
21 |
22 | .card {
23 | padding: 2em;
24 | }
25 |
26 | .read-the-docs {
27 | color: #888;
28 | }
29 |
30 | @media (prefers-color-scheme: dark) {
31 | .logo.jwc:hover {
32 | filter: drop-shadow(0 0 2em rgb(255, 255, 255));
33 | }
34 | }
35 |
36 |
37 | button {
38 | border-radius: 8px;
39 | border: 1px solid transparent;
40 | padding: 0.6em 1.2em;
41 | font-size: 1em;
42 | font-weight: 500;
43 | font-family: inherit;
44 | background-color: #1a1a1a;
45 | cursor: pointer;
46 | transition: border-color 0.25s;
47 | }
48 | button:hover {
49 | border-color: #646cff;
50 | }
51 | button:focus,
52 | button:focus-visible {
53 | outline: 4px auto -webkit-focus-ring-color;
54 | }
55 |
56 |
57 | @media (prefers-color-scheme: light) {
58 | button {
59 | background-color: #f9f9f9;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/demo/src/index.css:
--------------------------------------------------------------------------------
1 |
2 | :root {
3 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
4 | font-size: 16px;
5 | line-height: 24px;
6 | font-weight: 400;
7 |
8 | color-scheme: light dark;
9 | color: rgba(255, 255, 255, 0.87);
10 | background-color: #242424;
11 |
12 | font-synthesis: none;
13 | text-rendering: optimizeLegibility;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | -webkit-text-size-adjust: 100%;
17 | }
18 |
19 | a {
20 | font-weight: 500;
21 | color: #646cff;
22 | text-decoration: inherit;
23 | }
24 | a:hover {
25 | color: #535bf2;
26 | }
27 |
28 | body {
29 | margin: 0;
30 | display: flex;
31 | place-items: center;
32 | min-width: 320px;
33 | min-height: 100vh;
34 | }
35 |
36 |
37 | h1 {
38 | font-size: 3.2em;
39 | line-height: 1.1;
40 | }
41 |
42 |
43 | @media (prefers-color-scheme: light) {
44 | :root {
45 | color: #213547;
46 | background-color: #ffffff;
47 | }
48 | a:hover {
49 | color: #747bff;
50 | }
51 | button {
52 | background-color: #f9f9f9;
53 | }
54 | }
55 |
56 |
57 | #root {
58 | max-width: 1280px;
59 | margin: 0 auto;
60 | padding: 2rem;
61 | text-align: center;
62 | }
63 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags: ['v*']
4 |
5 | name: Release
6 |
7 | jobs:
8 | release:
9 | name: Create Release
10 | strategy:
11 | matrix:
12 | os: [ubuntu-latest]
13 | runs-on: ${{ matrix.os }}
14 | # outputs:
15 | # release_url: ${{ steps.create_release.outputs.upload_url }}
16 | steps:
17 | - name: Checkout code
18 | uses: actions/checkout@v3
19 | with:
20 | fetch-depth: 0
21 | - uses: actions/setup-node@v3
22 | with:
23 | node-version: 16.x
24 | - name: Cache pnpm modules
25 | uses: actions/cache@v3
26 | env:
27 | cache-name: cache-pnpm-modules
28 | with:
29 | path: ~/.pnpm-store
30 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }}
31 | restore-keys: |
32 | ${{ runner.os }}-build-${{ env.cache-name }}-${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }}
33 | - uses: pnpm/action-setup@v2.2.4
34 | with:
35 | version: latest
36 | run_install: true
37 |
38 | - name: Build
39 | run: |
40 | npm run build
41 |
42 | - name: Create Release
43 | id: create_release
44 | run: |
45 | npx changelogithub
46 | env:
47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/demo/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/runtime/v-dom/patch.ts:
--------------------------------------------------------------------------------
1 | import { createElement } from "../dom";
2 | import { updateAttributes } from "../dom/attrs";
3 |
4 | import { VNode } from "./vnode";
5 |
6 | export function patch(
7 | host: Node,
8 | vnode: VNode | VNode[],
9 | old: VNode | VNode[],
10 | index: number
11 | ) {
12 | if (Array.isArray(vnode) && Array.isArray(old)) {
13 | vnode.forEach((node, index) => {
14 | patch(host, node, old[index], index);
15 | });
16 | return;
17 | }
18 | vnode = vnode as VNode;
19 | old = old as VNode;
20 | if (vnode.isUpdated) {
21 | // update the attributes of the dom node
22 | updateAttributes(vnode.el, vnode.attributes);
23 | // update the children of the dom node
24 | // if the children is a string, update the textContent of the dom node
25 | if (typeof vnode.children === "string") {
26 | vnode.el.textContent = vnode.children;
27 | }
28 | host.parentNode?.replaceChild(createElement(vnode), host);
29 | } else if (vnode.isNew) {
30 | host.appendChild(createElement(vnode));
31 | } else if (vnode.isDeleted) {
32 | host.removeChild(host.childNodes[index]);
33 | }
34 |
35 | // update the children of the dom node
36 | if (vnode.children instanceof Array) {
37 | for (let index = 0; index < vnode.children.length; index++) {
38 | // find the dom node in the host node
39 | const child = host.childNodes[index];
40 | patch(child, vnode.children[index], old?.children[index], index);
41 | continue;
42 | }
43 | }
44 | return;
45 | }
46 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jwcjs",
3 | "version": "0.1.0-alpha.1",
4 | "description": "Jwc.js is a JavaScript framework for using JSX to write web components on the web.",
5 | "main": "./dist/index.js",
6 | "exports": {
7 | ".": "./dist/index.js"
8 | },
9 | "publishConfig": {
10 | "access": "public"
11 | },
12 | "types": "./dist/index.d.ts",
13 | "files": [
14 | "dist",
15 | "package.json",
16 | "LICENSE"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/jwcjs/core.git"
21 | },
22 | "homepage": "https://jwc.js.org",
23 | "bugs": {
24 | "url": "https://github.com/jwcjs/core/issues"
25 | },
26 | "readme": "https://github.com/jwcjs/core#readme",
27 | "scripts": {
28 | "prepublish": "npm run build",
29 | "build": "rimraf dist && rollup -c rollup.config.mjs"
30 | },
31 | "keywords": [
32 | "jsx",
33 | "web components",
34 | "framework",
35 | "jwcjs"
36 | ],
37 | "author": "JwcJS",
38 | "license": "MIT",
39 | "devDependencies": {
40 | "@rollup/plugin-node-resolve": "^15.0.1",
41 | "esbuild": "^0.17.0",
42 | "rollup": "^3.7.4",
43 | "rollup-plugin-esbuild": "^5.0.0",
44 | "vite": "^4.0.1",
45 | "@jwcjs/reactively": "workspace:^1.0.0",
46 | "@jwcjs/runtime": "workspace:^1.0.0",
47 | "@jwcjs/shared": "workspace:^1.0.0",
48 | "reflect-metadata": "^0.1.13",
49 | "rollup-plugin-ts": "^3.0.2"
50 | },
51 | "bump": {
52 | "publish": true,
53 | "changelog": true
54 | }
55 | }
--------------------------------------------------------------------------------
/packages/reactively/src/define-reactive.ts:
--------------------------------------------------------------------------------
1 | import {
2 | PropOptions,
3 | WatcherOptions,
4 | COMPONENT_EVENT_METADATA_KEY,
5 | COMPONENT_PROP_METADATA_KEY,
6 | } from "@jwcjs/shared";
7 | import { ReactiveEvent } from "./call-reactive";
8 |
9 | /**
10 | * A universal function for defining props and state
11 | */
12 | function definePropsOrState(target: any, attr: string, options: PropOptions) {
13 | const { default: v, required } = options;
14 | const keys = Reflect.getMetadata(COMPONENT_PROP_METADATA_KEY, target) || [];
15 |
16 | keys.push({
17 | attr,
18 | default: v,
19 | required,
20 | });
21 | Reflect.defineMetadata(COMPONENT_PROP_METADATA_KEY, keys, target);
22 | }
23 |
24 | /**
25 | * Define props for component
26 | */
27 | export function defineProps(target: any, attr: string, options: PropOptions) {
28 | definePropsOrState(target, attr, options);
29 | }
30 |
31 | /**
32 | * Define state for component
33 | */
34 | export function defineState(target: any, attr: string, options: PropOptions) {
35 | definePropsOrState(target, attr, options);
36 | }
37 |
38 | /**
39 | * Define event for component
40 | */
41 | export function defineEvent(target: any, event: ReactiveEvent) {
42 | const keys =
43 | Reflect.getMetadata(COMPONENT_EVENT_METADATA_KEY, target) || [];
44 | keys.push(event);
45 | Reflect.defineMetadata(COMPONENT_EVENT_METADATA_KEY, keys, target);
46 | }
47 |
48 | /**
49 | * Define watcher for component
50 | */
51 | export function defineWatcher(target: any, options: WatcherOptions) {
52 | const keys =
53 | Reflect.getMetadata(COMPONENT_EVENT_METADATA_KEY, target) || [];
54 | keys.push(options);
55 | Reflect.defineMetadata(COMPONENT_EVENT_METADATA_KEY, keys, target);
56 | }
57 |
--------------------------------------------------------------------------------
/demo/public/jwc.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/public/jwc-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { JwcComponent, Component, Prop, h, Fragment } from "jwcjs";
2 | import styles from "./App.css?inline";
3 |
4 | @Component({
5 | name: "app-element",
6 | css: styles,
7 | })
8 | export class App extends JwcComponent {
9 | constructor() {
10 | super();
11 | }
12 |
13 | @Prop({ default: 0, attr: "count" })
14 | public count = 0;
15 |
16 | public onClick = () => {
17 | this.count++;
18 | };
19 |
20 | // get media
21 | public getSystemColorScheme() {
22 | return window.matchMedia("(prefers-color-scheme: dark)").matches
23 | ? "dark"
24 | : "light";
25 | }
26 |
27 | public override connectedCallback() {
28 | super.connectedCallback();
29 | window
30 | .matchMedia("(prefers-color-scheme: dark)")
31 | .addEventListener("change", () => {
32 | this.updateDiff();
33 | console.log("change");
34 | });
35 | }
36 |
37 | public override disconnectedCallback() {
38 | super.disconnectedCallback();
39 | window
40 | .matchMedia("(prefers-color-scheme: dark)")
41 | .removeEventListener("change", () => {
42 | this.updateDiff();
43 | });
44 | }
45 |
46 | public override render() {
47 | return (
48 | <>
49 |
65 |
66 |
69 |
70 | {Array.from({ length: this.count }).map((_, i) => (
71 |
72 | 点击按钮,此处应随着按钮的点击次数而变化 {String(i + 1)}
73 |
74 | ))}
75 | >
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/packages/shared/src/types/jwc-element.interface.ts:
--------------------------------------------------------------------------------
1 | import { VNode } from "@jwcjs/runtime";
2 |
3 | export interface CustomElementProps {
4 | /**
5 | * The component's name.
6 | */
7 | name: string;
8 |
9 | /**
10 | * The component's styles.
11 | */
12 | css?: any;
13 |
14 | /**
15 | * Options for the component.
16 | */
17 | options?: ElementDefinitionOptions; // https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define
18 |
19 | isMounted?: boolean;
20 | }
21 |
22 | export interface PropOptions {
23 | /**
24 | * default value for the prop
25 | * @default undefined
26 | */
27 | default?: any;
28 |
29 | /**
30 | * whether the prop is required
31 | * @default false
32 | */
33 | required?: boolean;
34 |
35 | /**
36 | * The prop's attribute name.
37 | */
38 | attr?: string;
39 | }
40 |
41 | export interface JwcElement {
42 | /**
43 | * Whether ths component is connected to the DOM.
44 | */
45 | isConnected: boolean;
46 |
47 | /**
48 | * Whether the component should be updated.
49 | */
50 | shouldUpdate: boolean;
51 |
52 | /**
53 | * The component's custom styles.
54 | */
55 | customStyles?: string;
56 |
57 | /**
58 | * The component host element.
59 | */
60 | host: HTMLElement;
61 |
62 | /**
63 | * The component's reactivity data.
64 | */
65 | $data?: any;
66 |
67 | /**
68 | * The component's options.
69 | * @see CustomElementProps
70 | */
71 | $options?: CustomElementProps;
72 |
73 | /**
74 | * The component's injected dependencies.
75 | */
76 | $deps?: any;
77 |
78 | /**
79 | * The last time vdom was rendered.
80 | */
81 | $lastRender?: VNode;
82 |
83 | /**
84 | * The component's attributes changed callback.
85 | */
86 | attributeChangedCallback?: (
87 | name: string,
88 | oldValue: string,
89 | newValue: string
90 | ) => void;
91 |
92 | /**
93 | * The component's connected callback.
94 | */
95 | connectedCallback?: () => void;
96 |
97 | /**
98 | * The component's connected function.
99 | */
100 | connected?: (shadowRoot: ShadowRoot) => void;
101 |
102 | /**
103 | * The component's disconnected callback.
104 | */
105 | disconnectedCallback?: () => void;
106 |
107 | /**
108 | * The component's disconnected function.
109 | */
110 | disconnected?: () => void;
111 |
112 | /**
113 | * The component's adopted callback.
114 | * @see https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define
115 | */
116 | adoptedCallback?: (oldDocument: Document, newDocument: Document) => void;
117 |
118 | /**
119 | * The component's update function.
120 | */
121 | update?: () => void;
122 |
123 | /**
124 | * The component's render function.
125 | */
126 | render?(...args: any[]): any;
127 | }
128 |
129 | /**
130 | * The JwcElement constructor.
131 | */
132 | export interface JwcElementConstructor {
133 | new (): JwcElement;
134 | }
135 |
--------------------------------------------------------------------------------
/packages/runtime/v-dom/diff.ts:
--------------------------------------------------------------------------------
1 | import { updateDOM } from "../dom/dom";
2 | import { removeAttrs } from "../utils/attrs";
3 | import { patch } from "./patch";
4 | import { VNode } from "./vnode";
5 |
6 | /**
7 | * Diff the old vnode and the new vnode.
8 | *
9 | * 1. Mark the new vnode,
10 | * and mark the child nodes of the new vnode as new.
11 | * 2. Diff the old vnode and the new vnode.
12 | * 3. Update the dom tree.
13 | *
14 | * Note:
15 | * I can't get the type of the old vnode and the new vnode.
16 | * So I use any to replace the type.
17 | *
18 | * @param oldVNode
19 | * @param newVNode
20 | */
21 | export function diff(oldVNode: any, newVNode: any, host?: Node) {
22 | if (!oldVNode) {
23 | newVNode = markNewVNode(newVNode);
24 | }
25 | newVNode = diffRecursive(oldVNode, newVNode);
26 | const updated = updateDOM(oldVNode, newVNode);
27 |
28 | if (host) {
29 | const hostel: Node[] = [];
30 | if (host.childNodes[0].nodeName === "STYLE") {
31 | /**
32 | * If the first child node of the host is a style node,
33 | * then the style node is not a child node of the vnode.
34 | *
35 | * So I need to remove the style node from the host.
36 | */
37 | host.childNodes.forEach((node: any) => {
38 | if (node.nodeName !== "STYLE") hostel.push(node);
39 | });
40 | }
41 | // transform the vnode to the dom tree
42 | if (updated.children instanceof Array) {
43 | for (let index = 0; index < updated.children.length; index++) {
44 | patch(
45 | hostel[index],
46 | updated.children[index],
47 | oldVNode.children[index],
48 | index
49 | );
50 | }
51 | }
52 | }
53 |
54 | return removeAttrs(updated, ["isUpdated", "isNew", "isDeleted"]);
55 | }
56 |
57 | /**
58 | * Mark the new vnode,
59 | * and mark the child nodes of the new vnode as new.
60 | *
61 | * @param node the new vnode
62 | */
63 | function markNewVNode(node: VNode) {
64 | if (node.isNew) {
65 | // For each child node of the node
66 | for (const child of node.children) {
67 | markNewVNode(child);
68 | }
69 | }
70 | node?.isNew === false && (node.isNew = true);
71 | return node;
72 | }
73 |
74 | /**
75 | * Diff the old vnode and the new vnode.
76 | *
77 | * 1. If the old vnode is marked as deleted, then return.
78 | * 2. If the new vnode is marked as new, then return.
79 | * 3. If the **tag name** of the old vnode and the new vnode are not equal,
80 | * then mark the old vnode as deleted, and mark the new vnode as new.
81 | * 4. If the attributes of the old vnode and the new vnode are not equal,
82 | * then mark the new vnode as updated.
83 | * 5. Diff the child nodes of the old vnode and the new vnode.
84 | *
85 | * @param oldVNode
86 | * @param newVNode
87 | */
88 | function diffRecursive(
89 | oldVNode: VNode | undefined,
90 | newVNode: VNode,
91 | host?: VNode
92 | ) {
93 | if (!oldVNode) {
94 | newVNode = markNewVNode(newVNode);
95 | return newVNode;
96 | }
97 | // the type of the vnode may be VNode[] ( x.map(() => (<>>)) )
98 | if (oldVNode instanceof Array && newVNode instanceof Array) {
99 | const maxLength = Math.max(oldVNode.length, newVNode.length);
100 | for (let i = 0; i < maxLength; i++) {
101 | newVNode[i] = diffRecursive(oldVNode[i], newVNode[i], host);
102 | }
103 | return newVNode;
104 | }
105 | // just text node
106 | if (typeof oldVNode === "string" && typeof newVNode === "string") {
107 | if (oldVNode !== newVNode) {
108 | newVNode = newVNode;
109 | host!.isUpdated = true;
110 | }
111 | }
112 |
113 | if (oldVNode.isDeleted) {
114 | return oldVNode;
115 | }
116 |
117 | // if (newVNode.isNew) {
118 | // // if the new vnode is new, then return;
119 | // return newVNode;
120 | // }
121 |
122 | if (oldVNode?.tagName !== newVNode.tagName) {
123 | oldVNode.isDeleted = true;
124 | newVNode.isNew = true;
125 | }
126 |
127 | if (
128 | JSON.stringify(oldVNode.attributes) !==
129 | JSON.stringify(newVNode.attributes)
130 | ) {
131 | /**
132 | * if the attributes of the old vnode and the new vnode are not equal,
133 | * then the new vnode is updated.
134 | */
135 | newVNode.isUpdated = true;
136 | }
137 | // diff the child nodes of the old vnode and the new vnode
138 | if (oldVNode.children && newVNode.children) {
139 | const oldChildren = Object.values(oldVNode.children);
140 | const newChildren = Object.values(newVNode.children);
141 | if (JSON.stringify(oldChildren) !== JSON.stringify(newChildren)) {
142 | const maxLength = Math.max(oldChildren.length, newChildren.length);
143 | for (let i = 0; i < maxLength; i++) {
144 | newVNode.children[i] = diffRecursive(
145 | oldChildren[i],
146 | newChildren[i],
147 | newVNode
148 | );
149 | }
150 | }
151 | }
152 | return newVNode;
153 | }
154 |
--------------------------------------------------------------------------------
/packages/runtime/dom/dom.ts:
--------------------------------------------------------------------------------
1 | import { VNode } from "../v-dom";
2 | import { camelToDash } from "../utils/others";
3 |
4 | /**
5 | * Create a dom node according to the tag name of the vnode.
6 | *
7 | * 1. Create a dom node according to the tag name of the vnode.
8 | * 2. Set the attributes of the dom node.
9 | * 3. Create the child nodes of the dom node.
10 | *
11 | * @param node the vnode
12 | */
13 | export function createElement(node: VNode | VNode[]): Node {
14 | if (Array.isArray(node)) {
15 | const fragment = document.createDocumentFragment();
16 | for (const child of node) {
17 | fragment.appendChild(createElement(child));
18 | }
19 | return fragment;
20 | }
21 | // create a dom node according to the tag name of the vnode
22 | const el =
23 | node.tagName === "Fragment"
24 | ? document.createDocumentFragment()
25 | : document.createElement(node.tagName);
26 |
27 | // set the attributes of the dom node
28 | const attributes = node.attributes;
29 | for (const key in attributes) {
30 | const value = attributes[key];
31 | if (key.startsWith("on")) {
32 | const eventName = key.slice(2).toLowerCase();
33 | el.addEventListener(eventName, value);
34 | } else if (key === "style") {
35 | for (const styleKey of value) {
36 | const styleValue = value[styleKey];
37 | (el as HTMLElement).style[styleKey] = styleValue;
38 | }
39 | } else {
40 | if (!(node.tagName === "Fragment")) {
41 | (el as HTMLElement).setAttribute(camelToDash(key), value);
42 | }
43 | }
44 | }
45 |
46 | // create the child nodes of the dom node
47 | if (node.children) {
48 | for (const child of node.children) {
49 | if (typeof child === "string") {
50 | el.appendChild(document.createTextNode(child));
51 | continue;
52 | }
53 | el.appendChild(createElement(child));
54 | }
55 | }
56 | return el;
57 | }
58 |
59 | /**
60 | * Update the attributes of the dom node.
61 | *
62 | * 1. Get the dom node of the vnode.
63 | * 2. Get the attributes of the vnode.
64 | * 3. Update the attributes of the dom node.
65 | * 4. Update the child nodes of the dom node.
66 | *
67 | * @param node the vnode
68 | */
69 | export function updateElement(oldNode: VNode, newNode: VNode) {
70 | // get the dom node of the vnode
71 | const el = newNode.el! as HTMLElement;
72 |
73 | // get the attributes of the vnode
74 | const attributes = newNode.attributes;
75 |
76 | // update the attributes of the dom node
77 | for (const key in attributes) {
78 | if (key.startsWith("on")) {
79 | if (typeof oldNode?.attributes[key] === "function") {
80 | const eventName = key.slice(2).toLowerCase();
81 | el.removeEventListener(eventName, oldNode.attributes[key]);
82 | el.addEventListener(eventName, attributes[key]);
83 | }
84 | } else {
85 | el.setAttribute(camelToDash(key), attributes[key]);
86 | }
87 | }
88 |
89 | for (const child of newNode.children) {
90 | if (typeof child === "string") {
91 | el.appendChild(document.createTextNode(child));
92 | continue;
93 | }
94 | el.appendChild(createElement(child));
95 | }
96 |
97 | // update the child nodes of the dom node
98 | if (newNode.children) {
99 | for (const child of Object.values(newNode.children)) {
100 | updateDOM(undefined, child);
101 | }
102 | }
103 |
104 | return el;
105 | }
106 |
107 | /**
108 | * Update the dom tree.
109 | *
110 | * 1. If the node is marked as deleted, then remove it from the dom tree.
111 | * 2. If the node is marked as new, then create a new dom node.
112 | * 3. If the node is marked as updated, then
113 | * update the attributes of the dom node.
114 | * 4. Update the child nodes of the dom node.
115 | *
116 | */
117 | export function updateDOM(
118 | oldNode: VNode | undefined,
119 | newNode: VNode | VNode[]
120 | ): VNode {
121 | if (Array.isArray(newNode)) {
122 | for (const child of newNode) {
123 | newNode = updateDOM(undefined, child);
124 | }
125 | return newNode as VNode;
126 | }
127 | // if the node is marked as deleted, then remove it from the dom tree
128 | if (newNode.isDeleted) {
129 | newNode.el!.parentNode!.removeChild(newNode.el!);
130 | return newNode;
131 | }
132 |
133 | // if the node is marked as new, then create a new dom node
134 | if (newNode.isNew) {
135 | newNode.el = createElement(newNode);
136 | return newNode;
137 | }
138 |
139 | // if the node is marked as updated, then update the attributes of the dom node
140 | if (newNode.isUpdated) {
141 | const updated = updateElement(oldNode, newNode);
142 | if (updated) {
143 | newNode.el = updated;
144 | }
145 | return newNode;
146 | }
147 |
148 | // update the child nodes of the dom node
149 | if (newNode.children) {
150 | for (const child of Object.values(newNode.children)) {
151 | updateDOM(undefined, child);
152 | }
153 | }
154 |
155 | return newNode;
156 | }
157 |
--------------------------------------------------------------------------------
/packages/core/src/wc-class.ts:
--------------------------------------------------------------------------------
1 | import { createCSSElement, defineProxy } from "@jwcjs/reactively";
2 | import { createElement, diff, removeAttrs } from "@jwcjs/runtime";
3 | import {
4 | COMPONENT_PROP_METADATA_KEY,
5 | JwcElement,
6 | PropOptions,
7 | WatcherOptions,
8 | } from "@jwcjs/shared";
9 |
10 | /**
11 | * The map of adoptedStyleSheets.
12 | * It is used to avoid duplicate styleSheets.
13 | */
14 | const adoptedStyleSheetsMap = new WeakMap();
15 |
16 | export class JwcComponent extends HTMLElement implements JwcElement {
17 | public override tagName: string;
18 | public rootNode = null;
19 |
20 | public $data = null;
21 | public $options = null;
22 | public $deps = null;
23 |
24 | public $lastRender = null;
25 | public customStyles = null;
26 | public shouldUpdate = false;
27 | public props = {};
28 | public propsList: PropOptions[] = [];
29 | public previousProps = {};
30 | public previousVNode = null;
31 |
32 | public host: HTMLElement;
33 |
34 | public override shadowRoot: ShadowRoot;
35 |
36 | private watchersMap = new Map();
37 |
38 | public getMetaList(key: string): Map {
39 | return Reflect.getMetadata(key, this) || new Map();
40 | }
41 |
42 | private initWatcher() {
43 | const watchers =
44 | this.getMetaList(COMPONENT_PROP_METADATA_KEY) ||
45 | ([] as WatcherOptions[]);
46 | watchers.forEach((watcher: WatcherOptions) => {
47 | const { callbackName } = watcher;
48 | const currentItem = this.watchersMap.get(callbackName);
49 | /**
50 | * if the callbackName is already exist,
51 | * add the watcher to the array.
52 | *
53 | * if not, create a new array and add the watcher to it.
54 | */
55 | if (currentItem) {
56 | this.watchersMap.set(callbackName, [...currentItem, watcher]);
57 | } else {
58 | this.watchersMap.set(callbackName, [watcher]);
59 | }
60 | });
61 | }
62 |
63 | constructor() {
64 | super();
65 | this.host = this;
66 | this.$options = (this.constructor as any).$options;
67 | this.init();
68 | }
69 |
70 | /**
71 | * init the styleSheets.
72 | * Put the styleSheets into the shadowRoot.
73 | */
74 | private initCSS(shadowRoot: ShadowRoot) {
75 | if (adoptedStyleSheetsMap.has(this.constructor)) {
76 | /**
77 | * if the adoptedStyleSheetsMap has the constructor,
78 | * it means that the styleSheets has been created.
79 | */
80 | shadowRoot.adoptedStyleSheets = adoptedStyleSheetsMap.get(
81 | this.constructor
82 | );
83 | } else {
84 | /**
85 | * if the adoptedStyleSheetsMap doesn't have the constructor,
86 | * it means that the styleSheets hasn't been created.
87 | */
88 | const styleSheets = this.$options.css;
89 | if (styleSheets) {
90 | const css = new CSSStyleSheet();
91 | css.replaceSync(styleSheets);
92 | shadowRoot.adoptedStyleSheets = [css];
93 | adoptedStyleSheetsMap.set(this.constructor, [css]);
94 | }
95 | }
96 | return shadowRoot;
97 | }
98 |
99 | private initShadowRoot() {
100 | let shadowRoot: ShadowRoot =
101 | this.shadowRoot || this.attachShadow({ mode: "open" });
102 | shadowRoot = this.initCSS(this.shadowRoot);
103 | if (this.$options.css) {
104 | shadowRoot.appendChild(createCSSElement(this.$options.css));
105 | }
106 | if (this.inlineStyles) {
107 | this.customStyles = createCSSElement(this.inlineStyles);
108 | shadowRoot.appendChild(this.customStyles);
109 | }
110 | return shadowRoot;
111 | }
112 |
113 | private attrsToProps() {
114 | const host =
115 | this.shadowRoot && this.shadowRoot.host
116 | ? this.shadowRoot.host
117 | : this;
118 | const attrs: Record = {};
119 | for (let i = 0; i < host.attributes.length; i++) {
120 | const attr = host.attributes[i];
121 | attrs[attr.name] = attr.value;
122 | }
123 | this.propsList.forEach((prop: PropOptions) => {
124 | const { attr: name, default: defaultValue } = prop;
125 | if (attrs[name]) {
126 | this.previousProps[name] = attrs[name];
127 | this[name] = attrs[name];
128 | } else {
129 | this.previousProps[name] = defaultValue;
130 | this[name] = defaultValue;
131 | }
132 | });
133 | }
134 |
135 | private init() {
136 | this.props = this.getMetaList(COMPONENT_PROP_METADATA_KEY) || [];
137 | this.propsList = Object.values(this.props);
138 | const that = this;
139 | // define the default value of the props.
140 | this.propsList.forEach((prop: PropOptions) => {
141 | const { attr: name, default: defaultValue } = prop;
142 | this.previousProps[name] = defaultValue;
143 | // getAttribute
144 | this[name] = defaultValue;
145 | defineProxy(that, name, prop);
146 | });
147 | this.initWatcher();
148 | }
149 |
150 | get inlineStyles() {
151 | return super.getAttribute("style");
152 | }
153 |
154 | public updateDiff() {
155 | const previous = this.$lastRender;
156 | const current = this.render(this.$data);
157 | if (previous) {
158 | this.$lastRender = null;
159 | this.$lastRender = diff(
160 | removeAttrs(previous),
161 | removeAttrs(current),
162 | this.shadowRoot
163 | );
164 | }
165 | }
166 |
167 | public customEvent(name: string, detail: any) {
168 | const event = new CustomEvent(name, {
169 | detail,
170 | bubbles: true,
171 | composed: true,
172 | });
173 |
174 | this.dispatchEvent(event);
175 | }
176 |
177 | public connectedCallback() {
178 | const shadowRoot = this.initShadowRoot();
179 | this.attrsToProps();
180 | /**
181 | * beforeCreate ->
182 | * created ->
183 | * afterCreate ->
184 | * beforeMount ->
185 | * mounted
186 | */
187 | const propsList = this.props as PropOptions[];
188 | const that = this;
189 | propsList.forEach((prop: PropOptions) => {
190 | const attr = shadowRoot.host.getAttribute(prop.attr);
191 | if (attr) {
192 | that[prop.attr] = attr;
193 | }
194 | });
195 | const rendered = this.render(this.$data);
196 | this.rootNode = createElement(removeAttrs(rendered) as any);
197 | if (this.$options.isMounted) {
198 | this.rootNode && shadowRoot.appendChild(this.rootNode);
199 | }
200 |
201 | this.$lastRender = removeAttrs(rendered);
202 | }
203 |
204 | public disconnectedCallback() {}
205 |
206 | public attributeChangedCallback(
207 | name: string,
208 | oldValue: string,
209 | newValue: string
210 | ) {
211 | if (oldValue !== newValue) {
212 | this.props[name] = newValue;
213 | }
214 | this.updateDiff();
215 | }
216 |
217 | public adoptedCallback() {}
218 |
219 | public render(data: {}): any {}
220 | }
221 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | # [0.1.0-alpha.1](https://github.com/jwcjs/core/compare/0.1.0-alpha.0...0.1.0-alpha.1) (2023-01-29)
4 |
5 |
6 | ### Bug Fixes
7 |
8 | * **core:** missing checking vnode type in creating, fixed [#19](https://github.com/jwcjs/core/issues/19) ([#20](https://github.com/jwcjs/core/issues/20)) ([6e25dc9](https://github.com/jwcjs/core/commit/6e25dc91606c56348d6339d23b09a0df5d086a64))
9 | * root package, init before build packages ([#21](https://github.com/jwcjs/core/issues/21)) ([a90296f](https://github.com/jwcjs/core/commit/a90296f7bb7d725991dfac37e835368eeb27b877))
10 | * style attribute ([#25](https://github.com/jwcjs/core/issues/25)) ([21c27a3](https://github.com/jwcjs/core/commit/21c27a3b514c2a96d64bdd2db65638dabb9bdbd1))
11 |
12 |
13 | ### Performance Improvements
14 |
15 | * **core:** export all packages in root ([98995ee](https://github.com/jwcjs/core/commit/98995ee8b250cb58d60295d4f05f714d4db6d910))
16 |
17 |
18 |
19 | # [0.1.0-alpha.0](https://github.com/jwcjs/core/compare/3aac67f35915f92e2a3a27b16a014dacaa662caa...0.1.0-alpha.0) (2022-12-22)
20 |
21 |
22 | ### Bug Fixes
23 |
24 | * **core:** default value of prop inoperative ([034e51f](https://github.com/jwcjs/core/commit/034e51fae9462f30da7f2ab4f3bfaf2ed9d8a457))
25 | * **core:** missing adoptedCallback ([638f8f5](https://github.com/jwcjs/core/commit/638f8f5fcd93bdcacf4d636bc89d56a3b9ceeb7b))
26 | * **core:** missing attributes updating ([cc6c3b2](https://github.com/jwcjs/core/commit/cc6c3b20538b67646890a159cfafef5afc57cccc))
27 | * css type warning ([c2c0774](https://github.com/jwcjs/core/commit/c2c077439a532f1fca57d06b1856f75f8d52d78b))
28 | * export modules ([8ae586b](https://github.com/jwcjs/core/commit/8ae586bfeead128fe64c475f965f46a952e0b54c))
29 | * **jwc-core:** component decorators doesn't return a function ([b827d9a](https://github.com/jwcjs/core/commit/b827d9a005ec2d7392aa00cfe1a033e2bdfc0e8e))
30 | * **jwc-core:** create dom element at first rendering ([27dcf6c](https://github.com/jwcjs/core/commit/27dcf6c18f6dca14bd2dd9d1798d82325738e68d))
31 | * **jwc-core:** inject styles into host ([f198477](https://github.com/jwcjs/core/commit/f19847797f13b9b95b63fa9c44f4e44e94ed482c))
32 | * **jwc-reactively:** reactive function extra props ([c97f7f5](https://github.com/jwcjs/core/commit/c97f7f524dacd1e31fc3b9cb4ee66a4ad95f805c))
33 | * **jwc-runtime:** diff function return a vnode ([bb18358](https://github.com/jwcjs/core/commit/bb18358d32abc969ec2492e69b25711a5aa0714f))
34 | * **jwc-runtime:** incorrectly passed arguments after using fragment ([0a7caa5](https://github.com/jwcjs/core/commit/0a7caa514c8057710fe1a4ce97122295afc5896a))
35 | * **jwc-runtime:** vnode attrs do not be updated ([fb27f63](https://github.com/jwcjs/core/commit/fb27f63f86cc0320fffe2816c1df4b61b03a99de))
36 | * **jwc-starter-vite-ts:** vite warning of css files ([0d70380](https://github.com/jwcjs/core/commit/0d70380782f198f28d6c3282b166dc67944422ca))
37 | * **reactively:** circular dependency ([a324244](https://github.com/jwcjs/core/commit/a3242443cf1eee82cd569e41024f366ab442ffea))
38 | * **reactively:** circular dependency ([#12](https://github.com/jwcjs/core/issues/12)) ([40075cc](https://github.com/jwcjs/core/commit/40075cc0de9ac11c28da434ad8171dfb7e54c569))
39 | * **runtime:** dom functions in circular dependency ([3b2f54a](https://github.com/jwcjs/core/commit/3b2f54a280f5e0026af59ec01b75c2f16dba8218))
40 | * type defination ([f7ae05e](https://github.com/jwcjs/core/commit/f7ae05efd5a87384884cc7cd0f4f1573d46ceb50))
41 |
42 |
43 | ### Features
44 |
45 | * cli commands ([e22c311](https://github.com/jwcjs/core/commit/e22c311f18795ae59de89868699d322a276dbf7d))
46 | * core bundle ([#7](https://github.com/jwcjs/core/issues/7)) ([e34655d](https://github.com/jwcjs/core/commit/e34655dd60e10ca7af0a2cd96803fbce3042c200))
47 | * event listener ([32c5040](https://github.com/jwcjs/core/commit/32c5040f2aeabf4ca0e1e86427f8ed00b3b63211))
48 | * JSX types support ([#2](https://github.com/jwcjs/core/issues/2)) ([f33f673](https://github.com/jwcjs/core/commit/f33f673a6e448a1767e66dd58139841188d6bc64))
49 | * **jwc-core:** base JwcComponent class ([699acba](https://github.com/jwcjs/core/commit/699acbab580dd6af0e941667bc37fb7bcb9c604a))
50 | * **jwc-core:** element and watcher interfaces ([896b4d1](https://github.com/jwcjs/core/commit/896b4d10bbc3c8afbe026addaf28ebb395fec785))
51 | * **jwc-core:** init shadow root and css style ([c850cf6](https://github.com/jwcjs/core/commit/c850cf656e0d1f0272af8ef20153dc108191a284))
52 | * **jwc-core:** init watcher in wc ([f03f9d7](https://github.com/jwcjs/core/commit/f03f9d76625387ae8aadbbfc723c7ba7c07e3f11))
53 | * **jwc-core:** reactively decorators ([2e1e1f5](https://github.com/jwcjs/core/commit/2e1e1f534341b644a6af247161cfefac88709942))
54 | * **jwc-core:** register custom element ([fada8a7](https://github.com/jwcjs/core/commit/fada8a7464e21153a06039c23177744dffc17930))
55 | * **jwc-core:** transform attrs to props ([f027d79](https://github.com/jwcjs/core/commit/f027d7978718b9696e27040a62643845948de44b))
56 | * **jwc-reactively:** base reactive data handler ([63e74c6](https://github.com/jwcjs/core/commit/63e74c68233987d8d4179432541e36e156c2c4e9))
57 | * **jwc-reactively:** create css element with css text ([e72cb55](https://github.com/jwcjs/core/commit/e72cb555db4f6dc08d6bdbfac21fcb6aa82cb79e))
58 | * **jwc-reactively:** define data and event by proxy ([a39e943](https://github.com/jwcjs/core/commit/a39e943ec9b8476843db8f7cc5f2ba1c00e6dd87))
59 | * **jwc-reactively:** define reactively props datas ([d023082](https://github.com/jwcjs/core/commit/d0230826b997855d494a655306b17c4b88b0eac3))
60 | * **jwc-runtime:** `` support ([#4](https://github.com/jwcjs/core/issues/4)) ([1edd095](https://github.com/jwcjs/core/commit/1edd095a50c9d85ba91b2ac7920985f00680f889))
61 | * **jwc-runtime:** reactively support ([#1](https://github.com/jwcjs/core/issues/1)) ([25680d3](https://github.com/jwcjs/core/commit/25680d30aaaf28667ff1727c52956962eb50198d))
62 | * **jwc-starter-vite-ts:** create a vite starter template ([76be0cc](https://github.com/jwcjs/core/commit/76be0cc8f5200829125dcb0d95d7154bfeed84f3))
63 | * starter vite config ([b41a115](https://github.com/jwcjs/core/commit/b41a115db92b012e5522aafff0d39f09e871c3c4))
64 | * use cli to create project ([9ace584](https://github.com/jwcjs/core/commit/9ace5840ce21dc117ec3bb25354b16292198f434))
65 | * **v-dom:** diff function ([3aac67f](https://github.com/jwcjs/core/commit/3aac67f35915f92e2a3a27b16a014dacaa662caa))
66 | * **v-dom:** h create vnode function ([852dc11](https://github.com/jwcjs/core/commit/852dc118ffb518909ed702b1b3ac6554d5a6d7db))
67 |
68 |
69 | ### Performance Improvements
70 |
71 | * export modules in core ([#3](https://github.com/jwcjs/core/issues/3)) ([9672b8f](https://github.com/jwcjs/core/commit/9672b8f7269ad1ef57dc0072e0a49840b2e44030))
72 | * **jwc-core:** `*.css` file types support ([105b133](https://github.com/jwcjs/core/commit/105b133de959a1d70fd64067a14ac97647f7463c))
73 | * **jwc-starter:** import vite client env ([0e72b92](https://github.com/jwcjs/core/commit/0e72b9262779b3c939c3a1c42fb460772a103660))
74 | * remove useless things ([8af7171](https://github.com/jwcjs/core/commit/8af7171a46b42e82446539b49b59052e6a0dd8f4))
75 |
76 |
77 | ### Reverts
78 |
79 | * remove a exist file ([f3f5620](https://github.com/jwcjs/core/commit/f3f5620ad9b72cedf3f2d9111deed40dd458749e))
80 | * remove a wrong usage ([15d0479](https://github.com/jwcjs/core/commit/15d0479a1db294bc44764cb3ef0ff58ce7c5a09b))
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/packages/shared/src/types/local-jsx.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for Jwc.js
2 | // Definitions by: wibus-wee
3 | // Thanks to: DefinitelyTyped
4 | // ReactJS
5 |
6 | import { h } from "@jwcjs/runtime";
7 | import * as CSS from "csstype";
8 |
9 | type Booleanish = boolean | "true" | "false";
10 | type NativeAnimationEvent = AnimationEvent;
11 | type NativeClipboardEvent = ClipboardEvent;
12 | type NativeCompositionEvent = CompositionEvent;
13 | type NativeDragEvent = DragEvent;
14 | type NativeFocusEvent = FocusEvent;
15 | type NativeKeyboardEvent = KeyboardEvent;
16 | type NativeMouseEvent = MouseEvent;
17 | type NativeTouchEvent = TouchEvent;
18 | type NativePointerEvent = PointerEvent;
19 | type NativeTransitionEvent = TransitionEvent;
20 | type NativeUIEvent = UIEvent;
21 | type NativeWheelEvent = WheelEvent;
22 |
23 | export type CSSProperties = CSS.Properties;
24 |
25 | declare global {
26 | // eslint-disable-next-line @typescript-eslint/no-namespace
27 | namespace JSX {
28 | //
29 | // Event System
30 | // ----------------------------------------------------------------------
31 | // TODO: change any to unknown when moving to TS v3
32 | interface BaseSyntheticEvent {
33 | nativeEvent: E;
34 | currentTarget: C;
35 | target: T;
36 | bubbles: boolean;
37 | cancelable: boolean;
38 | defaultPrevented: boolean;
39 | eventPhase: number;
40 | isTrusted: boolean;
41 | preventDefault(): void;
42 | isDefaultPrevented(): boolean;
43 | stopPropagation(): void;
44 | isPropagationStopped(): boolean;
45 | persist(): void;
46 | timeStamp: number;
47 | type: string;
48 | }
49 |
50 | //
51 | // Browser Interfaces
52 | // https://github.com/nikeee/2048-typescript/blob/master/2048/js/touch.d.ts
53 | // ----------------------------------------------------------------------
54 |
55 | interface AbstractView {
56 | styleMedia: StyleMedia;
57 | document: Document;
58 | }
59 |
60 | interface Touch {
61 | identifier: number;
62 | target: EventTarget;
63 | screenX: number;
64 | screenY: number;
65 | clientX: number;
66 | clientY: number;
67 | pageX: number;
68 | pageY: number;
69 | }
70 |
71 | interface TouchList {
72 | [index: number]: Touch;
73 | length: number;
74 | item(index: number): Touch;
75 | identifiedTouch(identifier: number): Touch;
76 | }
77 |
78 | //
79 | // Error Interfaces
80 | // ----------------------------------------------------------------------
81 | interface ErrorInfo {
82 | /**
83 | * Captures which component contained the exception, and its ancestors.
84 | */
85 | componentStack: string;
86 | }
87 |
88 | /**
89 | * currentTarget - a reference to the element on which the event listener is registered.
90 | *
91 | * target - a reference to the element from which the event was originally dispatched.
92 | * This might be a child element to the element on which the event listener is registered.
93 | * If you thought this should be `EventTarget & T`, see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11508#issuecomment-256045682
94 | */
95 | type SyntheticEvent = BaseSyntheticEvent<
96 | E,
97 | EventTarget & T,
98 | EventTarget
99 | >;
100 | interface ClipboardEvent
101 | extends SyntheticEvent {
102 | clipboardData: DataTransfer;
103 | }
104 |
105 | interface CompositionEvent
106 | extends SyntheticEvent {
107 | data: string;
108 | }
109 |
110 | interface DragEvent
111 | extends MouseEvent {
112 | dataTransfer: DataTransfer;
113 | }
114 |
115 | interface PointerEvent
116 | extends MouseEvent {
117 | pointerId: number;
118 | pressure: number;
119 | tangentialPressure: number;
120 | tiltX: number;
121 | tiltY: number;
122 | twist: number;
123 | width: number;
124 | height: number;
125 | pointerType: "mouse" | "pen" | "touch";
126 | isPrimary: boolean;
127 | }
128 |
129 | interface FocusEvent
130 | extends SyntheticEvent {
131 | relatedTarget: (EventTarget & RelatedTarget) | null;
132 | target: EventTarget & Target;
133 | }
134 |
135 | type FormEvent = SyntheticEvent;
136 |
137 | interface InvalidEvent extends SyntheticEvent {
138 | target: EventTarget & T;
139 | }
140 |
141 | interface ChangeEvent extends SyntheticEvent {
142 | target: EventTarget & T;
143 | }
144 |
145 | export type ModifierKey =
146 | | "Alt"
147 | | "AltGraph"
148 | | "CapsLock"
149 | | "Control"
150 | | "Fn"
151 | | "FnLock"
152 | | "Hyper"
153 | | "Meta"
154 | | "NumLock"
155 | | "ScrollLock"
156 | | "Shift"
157 | | "Super"
158 | | "Symbol"
159 | | "SymbolLock";
160 |
161 | interface KeyboardEvent
162 | extends UIEvent {
163 | altKey: boolean;
164 | /** @deprecated */
165 | charCode: number;
166 | ctrlKey: boolean;
167 | code: string;
168 | /**
169 | * See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method.
170 | */
171 | getModifierState(key: ModifierKey): boolean;
172 | /**
173 | * See the [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#named-key-attribute-values). for possible values
174 | */
175 | key: string;
176 | /** @deprecated */
177 | keyCode: number;
178 | locale: string;
179 | location: number;
180 | metaKey: boolean;
181 | repeat: boolean;
182 | shiftKey: boolean;
183 | /** @deprecated */
184 | which: number;
185 | }
186 |
187 | interface MouseEvent
188 | extends UIEvent {
189 | altKey: boolean;
190 | button: number;
191 | buttons: number;
192 | clientX: number;
193 | clientY: number;
194 | ctrlKey: boolean;
195 | /**
196 | * See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method.
197 | */
198 | getModifierState(key: ModifierKey): boolean;
199 | metaKey: boolean;
200 | movementX: number;
201 | movementY: number;
202 | pageX: number;
203 | pageY: number;
204 | relatedTarget: EventTarget | null;
205 | screenX: number;
206 | screenY: number;
207 | shiftKey: boolean;
208 | }
209 |
210 | interface TouchEvent extends UIEvent {
211 | altKey: boolean;
212 | changedTouches: TouchList;
213 | ctrlKey: boolean;
214 | /**
215 | * See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method.
216 | */
217 | getModifierState(key: ModifierKey): boolean;
218 | metaKey: boolean;
219 | shiftKey: boolean;
220 | targetTouches: TouchList;
221 | touches: TouchList;
222 | }
223 |
224 | interface UIEvent
225 | extends SyntheticEvent {
226 | detail: number;
227 | view: AbstractView;
228 | }
229 |
230 | interface WheelEvent
231 | extends MouseEvent {
232 | deltaMode: number;
233 | deltaX: number;
234 | deltaY: number;
235 | deltaZ: number;
236 | }
237 |
238 | interface AnimationEvent
239 | extends SyntheticEvent {
240 | animationName: string;
241 | elapsedTime: number;
242 | pseudoElement: string;
243 | }
244 |
245 | interface TransitionEvent
246 | extends SyntheticEvent {
247 | elapsedTime: number;
248 | propertyName: string;
249 | pseudoElement: string;
250 | }
251 |
252 | type EventHandler> = {
253 | bivarianceHack(event: E): void;
254 | }["bivarianceHack"];
255 | type JwcEventHandler = EventHandler>;
256 |
257 | type ClipboardEventHandler = EventHandler<
258 | ClipboardEvent
259 | >;
260 | type CompositionEventHandler = EventHandler<
261 | CompositionEvent
262 | >;
263 | type DragEventHandler = EventHandler>;
264 | type FocusEventHandler = EventHandler>;
265 | type FormEventHandler = EventHandler>;
266 | type ChangeEventHandler = EventHandler>;
267 | type KeyboardEventHandler = EventHandler>;
268 | type MouseEventHandler = EventHandler>;
269 | type TouchEventHandler = EventHandler>;
270 | type PointerEventHandler = EventHandler>;
271 | type UIEventHandler = EventHandler>;
272 | type WheelEventHandler = EventHandler>;
273 | type AnimationEventHandler = EventHandler<
274 | AnimationEvent
275 | >;
276 | type TransitionEventHandler = EventHandler<
277 | TransitionEvent
278 | >;
279 |
280 | // All the WAI-ARIA 1.1 role attribute values from https://www.w3.org/TR/wai-aria-1.1/#role_definitions
281 | type AriaRole =
282 | | "alert"
283 | | "alertdialog"
284 | | "application"
285 | | "article"
286 | | "banner"
287 | | "button"
288 | | "cell"
289 | | "checkbox"
290 | | "columnheader"
291 | | "combobox"
292 | | "complementary"
293 | | "contentinfo"
294 | | "definition"
295 | | "dialog"
296 | | "directory"
297 | | "document"
298 | | "feed"
299 | | "figure"
300 | | "form"
301 | | "grid"
302 | | "gridcell"
303 | | "group"
304 | | "heading"
305 | | "img"
306 | | "link"
307 | | "list"
308 | | "listbox"
309 | | "listitem"
310 | | "log"
311 | | "main"
312 | | "marquee"
313 | | "math"
314 | | "menu"
315 | | "menubar"
316 | | "menuitem"
317 | | "menuitemcheckbox"
318 | | "menuitemradio"
319 | | "navigation"
320 | | "none"
321 | | "note"
322 | | "option"
323 | | "presentation"
324 | | "progressbar"
325 | | "radio"
326 | | "radiogroup"
327 | | "region"
328 | | "row"
329 | | "rowgroup"
330 | | "rowheader"
331 | | "scrollbar"
332 | | "search"
333 | | "searchbox"
334 | | "separator"
335 | | "slider"
336 | | "spinbutton"
337 | | "status"
338 | | "switch"
339 | | "tab"
340 | | "table"
341 | | "tablist"
342 | | "tabpanel"
343 | | "term"
344 | | "textbox"
345 | | "timer"
346 | | "toolbar"
347 | | "tooltip"
348 | | "tree"
349 | | "treegrid"
350 | | "treeitem"
351 | | (string & {});
352 |
353 | // All the WAI-ARIA 1.1 attributes from https://www.w3.org/TR/wai-aria-1.1/
354 | interface AriaAttributes {
355 | /** Identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application. */
356 | "aria-activedescendant"?: string | undefined;
357 | /** Indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the aria-relevant attribute. */
358 | "aria-atomic"?: Booleanish | undefined;
359 | /**
360 | * Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for an input and specifies how predictions would be
361 | * presented if they are made.
362 | */
363 | "aria-autocomplete"?:
364 | | "none"
365 | | "inline"
366 | | "list"
367 | | "both"
368 | | undefined;
369 | /** Indicates an element is being modified and that assistive technologies MAY want to wait until the modifications are complete before exposing them to the user. */
370 | "aria-busy"?: Booleanish | undefined;
371 | /**
372 | * Indicates the current "checked" state of checkboxes, radio buttons, and other widgets.
373 | * @see aria-pressed @see aria-selected.
374 | */
375 | "aria-checked"?: boolean | "false" | "mixed" | "true" | undefined;
376 | /**
377 | * Defines the total number of columns in a table, grid, or treegrid.
378 | * @see aria-colindex.
379 | */
380 | "aria-colcount"?: number | undefined;
381 | /**
382 | * Defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid.
383 | * @see aria-colcount @see aria-colspan.
384 | */
385 | "aria-colindex"?: number | undefined;
386 | /**
387 | * Defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid.
388 | * @see aria-colindex @see aria-rowspan.
389 | */
390 | "aria-colspan"?: number | undefined;
391 | /**
392 | * Identifies the element (or elements) whose contents or presence are controlled by the current element.
393 | * @see aria-owns.
394 | */
395 | "aria-controls"?: string | undefined;
396 | /** Indicates the element that represents the current item within a container or set of related elements. */
397 | "aria-current"?:
398 | | boolean
399 | | "false"
400 | | "true"
401 | | "page"
402 | | "step"
403 | | "location"
404 | | "date"
405 | | "time"
406 | | undefined;
407 | /**
408 | * Identifies the element (or elements) that describes the object.
409 | * @see aria-labelledby
410 | */
411 | "aria-describedby"?: string | undefined;
412 | /**
413 | * Identifies the element that provides a detailed, extended description for the object.
414 | * @see aria-describedby.
415 | */
416 | "aria-details"?: string | undefined;
417 | /**
418 | * Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.
419 | * @see aria-hidden @see aria-readonly.
420 | */
421 | "aria-disabled"?: Booleanish | undefined;
422 | /**
423 | * Indicates what functions can be performed when a dragged object is released on the drop target.
424 | * @deprecated in ARIA 1.1
425 | */
426 | "aria-dropeffect"?:
427 | | "none"
428 | | "copy"
429 | | "execute"
430 | | "link"
431 | | "move"
432 | | "popup"
433 | | undefined;
434 | /**
435 | * Identifies the element that provides an error message for the object.
436 | * @see aria-invalid @see aria-describedby.
437 | */
438 | "aria-errormessage"?: string | undefined;
439 | /** Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed. */
440 | "aria-expanded"?: Booleanish | undefined;
441 | /**
442 | * Identifies the next element (or elements) in an alternate reading order of content which, at the user's discretion,
443 | * allows assistive technology to override the general default of reading in document source order.
444 | */
445 | "aria-flowto"?: string | undefined;
446 | /**
447 | * Indicates an element's "grabbed" state in a drag-and-drop operation.
448 | * @deprecated in ARIA 1.1
449 | */
450 | "aria-grabbed"?: Booleanish | undefined;
451 | /** Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element. */
452 | "aria-haspopup"?:
453 | | boolean
454 | | "false"
455 | | "true"
456 | | "menu"
457 | | "listbox"
458 | | "tree"
459 | | "grid"
460 | | "dialog"
461 | | undefined;
462 | /**
463 | * Indicates whether the element is exposed to an accessibility API.
464 | * @see aria-disabled.
465 | */
466 | "aria-hidden"?: Booleanish | undefined;
467 | /**
468 | * Indicates the entered value does not conform to the format expected by the application.
469 | * @see aria-errormessage.
470 | */
471 | "aria-invalid"?:
472 | | boolean
473 | | "false"
474 | | "true"
475 | | "grammar"
476 | | "spelling"
477 | | undefined;
478 | /** Indicates keyboard shortcuts that an author has implemented to activate or give focus to an element. */
479 | "aria-keyshortcuts"?: string | undefined;
480 | /**
481 | * Defines a string value that labels the current element.
482 | * @see aria-labelledby.
483 | */
484 | "aria-label"?: string | undefined;
485 | /**
486 | * Identifies the element (or elements) that labels the current element.
487 | * @see aria-describedby.
488 | */
489 | "aria-labelledby"?: string | undefined;
490 | /** Defines the hierarchical level of an element within a structure. */
491 | "aria-level"?: number | undefined;
492 | /** Indicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region. */
493 | "aria-live"?: "off" | "assertive" | "polite" | undefined;
494 | /** Indicates whether an element is modal when displayed. */
495 | "aria-modal"?: Booleanish | undefined;
496 | /** Indicates whether a text box accepts multiple lines of input or only a single line. */
497 | "aria-multiline"?: Booleanish | undefined;
498 | /** Indicates that the user may select more than one item from the current selectable descendants. */
499 | "aria-multiselectable"?: Booleanish | undefined;
500 | /** Indicates whether the element's orientation is horizontal, vertical, or unknown/ambiguous. */
501 | "aria-orientation"?: "horizontal" | "vertical" | undefined;
502 | /**
503 | * Identifies an element (or elements) in order to define a visual, functional, or contextual parent/child relationship
504 | * between DOM elements where the DOM hierarchy cannot be used to represent the relationship.
505 | * @see aria-controls.
506 | */
507 | "aria-owns"?: string | undefined;
508 | /**
509 | * Defines a short hint (a word or short phrase) intended to aid the user with data entry when the control has no value.
510 | * A hint could be a sample value or a brief description of the expected format.
511 | */
512 | "aria-placeholder"?: string | undefined;
513 | /**
514 | * Defines an element's number or position in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.
515 | * @see aria-setsize.
516 | */
517 | "aria-posinset"?: number | undefined;
518 | /**
519 | * Indicates the current "pressed" state of toggle buttons.
520 | * @see aria-checked @see aria-selected.
521 | */
522 | "aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined;
523 | /**
524 | * Indicates that the element is not editable, but is otherwise operable.
525 | * @see aria-disabled.
526 | */
527 | "aria-readonly"?: Booleanish | undefined;
528 | /**
529 | * Indicates what notifications the user agent will trigger when the accessibility tree within a live region is modified.
530 | * @see aria-atomic.
531 | */
532 | "aria-relevant"?:
533 | | "additions"
534 | | "additions removals"
535 | | "additions text"
536 | | "all"
537 | | "removals"
538 | | "removals additions"
539 | | "removals text"
540 | | "text"
541 | | "text additions"
542 | | "text removals"
543 | | undefined;
544 | /** Indicates that user input is required on the element before a form may be submitted. */
545 | "aria-required"?: Booleanish | undefined;
546 | /** Defines a human-readable, author-localized description for the role of an element. */
547 | "aria-roledescription"?: string | undefined;
548 | /**
549 | * Defines the total number of rows in a table, grid, or treegrid.
550 | * @see aria-rowindex.
551 | */
552 | "aria-rowcount"?: number | undefined;
553 | /**
554 | * Defines an element's row index or position with respect to the total number of rows within a table, grid, or treegrid.
555 | * @see aria-rowcount @see aria-rowspan.
556 | */
557 | "aria-rowindex"?: number | undefined;
558 | /**
559 | * Defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.
560 | * @see aria-rowindex @see aria-colspan.
561 | */
562 | "aria-rowspan"?: number | undefined;
563 | /**
564 | * Indicates the current "selected" state of various widgets.
565 | * @see aria-checked @see aria-pressed.
566 | */
567 | "aria-selected"?: Booleanish | undefined;
568 | /**
569 | * Defines the number of items in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.
570 | * @see aria-posinset.
571 | */
572 | "aria-setsize"?: number | undefined;
573 | /** Indicates if items in a table or grid are sorted in ascending or descending order. */
574 | "aria-sort"?:
575 | | "none"
576 | | "ascending"
577 | | "descending"
578 | | "other"
579 | | undefined;
580 | /** Defines the maximum allowed value for a range widget. */
581 | "aria-valuemax"?: number | undefined;
582 | /** Defines the minimum allowed value for a range widget. */
583 | "aria-valuemin"?: number | undefined;
584 | /**
585 | * Defines the current value for a range widget.
586 | * @see aria-valuetext.
587 | */
588 | "aria-valuenow"?: number | undefined;
589 | /** Defines the human readable text alternative of aria-valuenow for a range widget. */
590 | "aria-valuetext"?: string | undefined;
591 | }
592 |
593 | interface DOMAttributes {
594 | children?: typeof h | string | number | boolean | null | undefined;
595 | dangerouslySetInnerHTML?:
596 | | {
597 | __html: string;
598 | }
599 | | undefined;
600 |
601 | // Clipboard Events
602 | onCopy?: ClipboardEventHandler | undefined;
603 | onCopyCapture?: ClipboardEventHandler | undefined;
604 | onCut?: ClipboardEventHandler | undefined;
605 | onCutCapture?: ClipboardEventHandler | undefined;
606 | onPaste?: ClipboardEventHandler | undefined;
607 | onPasteCapture?: ClipboardEventHandler | undefined;
608 |
609 | // Composition Events
610 | onCompositionEnd?: CompositionEventHandler | undefined;
611 | onCompositionEndCapture?: CompositionEventHandler | undefined;
612 | onCompositionStart?: CompositionEventHandler | undefined;
613 | onCompositionStartCapture?: CompositionEventHandler | undefined;
614 | onCompositionUpdate?: CompositionEventHandler | undefined;
615 | onCompositionUpdateCapture?: CompositionEventHandler | undefined;
616 |
617 | // Focus Events
618 | onFocus?: FocusEventHandler | undefined;
619 | onFocusCapture?: FocusEventHandler | undefined;
620 | onBlur?: FocusEventHandler | undefined;
621 | onBlurCapture?: FocusEventHandler | undefined;
622 |
623 | // Form Events
624 | onChange?: FormEventHandler | undefined;
625 | onChangeCapture?: FormEventHandler | undefined;
626 | onBeforeInput?: FormEventHandler | undefined;
627 | onBeforeInputCapture?: FormEventHandler | undefined;
628 | onInput?: FormEventHandler | undefined;
629 | onInputCapture?: FormEventHandler | undefined;
630 | onReset?: FormEventHandler | undefined;
631 | onResetCapture?: FormEventHandler | undefined;
632 | onSubmit?: FormEventHandler | undefined;
633 | onSubmitCapture?: FormEventHandler | undefined;
634 | onInvalid?: FormEventHandler | undefined;
635 | onInvalidCapture?: FormEventHandler | undefined;
636 |
637 | // Image Events
638 | onLoad?: JwcEventHandler | undefined;
639 | onLoadCapture?: JwcEventHandler | undefined;
640 | onError?: JwcEventHandler | undefined; // also a Media Event
641 | onErrorCapture?: JwcEventHandler | undefined; // also a Media Event
642 |
643 | // Keyboard Events
644 | onKeyDown?: KeyboardEventHandler | undefined;
645 | onKeyDownCapture?: KeyboardEventHandler | undefined;
646 | /** @deprecated */
647 | onKeyPress?: KeyboardEventHandler | undefined;
648 | /** @deprecated */
649 | onKeyPressCapture?: KeyboardEventHandler | undefined;
650 | onKeyUp?: KeyboardEventHandler | undefined;
651 | onKeyUpCapture?: KeyboardEventHandler | undefined;
652 |
653 | // Media Events
654 | onAbort?: JwcEventHandler | undefined;
655 | onAbortCapture?: JwcEventHandler | undefined;
656 | onCanPlay?: JwcEventHandler | undefined;
657 | onCanPlayCapture?: JwcEventHandler | undefined;
658 | onCanPlayThrough?: JwcEventHandler | undefined;
659 | onCanPlayThroughCapture?: JwcEventHandler | undefined;
660 | onDurationChange?: JwcEventHandler | undefined;
661 | onDurationChangeCapture?: JwcEventHandler | undefined;
662 | onEmptied?: JwcEventHandler | undefined;
663 | onEmptiedCapture?: JwcEventHandler | undefined;
664 | onEncrypted?: JwcEventHandler | undefined;
665 | onEncryptedCapture?: JwcEventHandler | undefined;
666 | onEnded?: JwcEventHandler | undefined;
667 | onEndedCapture?: JwcEventHandler | undefined;
668 | onLoadedData?: JwcEventHandler | undefined;
669 | onLoadedDataCapture?: JwcEventHandler | undefined;
670 | onLoadedMetadata?: JwcEventHandler | undefined;
671 | onLoadedMetadataCapture?: JwcEventHandler | undefined;
672 | onLoadStart?: JwcEventHandler | undefined;
673 | onLoadStartCapture?: JwcEventHandler | undefined;
674 | onPause?: JwcEventHandler | undefined;
675 | onPauseCapture?: JwcEventHandler | undefined;
676 | onPlay?: JwcEventHandler | undefined;
677 | onPlayCapture?: JwcEventHandler | undefined;
678 | onPlaying?: JwcEventHandler | undefined;
679 | onPlayingCapture?: JwcEventHandler | undefined;
680 | onProgress?: JwcEventHandler | undefined;
681 | onProgressCapture?: JwcEventHandler | undefined;
682 | onRateChange?: JwcEventHandler | undefined;
683 | onRateChangeCapture?: JwcEventHandler | undefined;
684 | onResize?: JwcEventHandler | undefined;
685 | onResizeCapture?: JwcEventHandler | undefined;
686 | onSeeked?: JwcEventHandler | undefined;
687 | onSeekedCapture?: JwcEventHandler | undefined;
688 | onSeeking?: JwcEventHandler | undefined;
689 | onSeekingCapture?: JwcEventHandler | undefined;
690 | onStalled?: JwcEventHandler | undefined;
691 | onStalledCapture?: JwcEventHandler | undefined;
692 | onSuspend?: JwcEventHandler | undefined;
693 | onSuspendCapture?: JwcEventHandler | undefined;
694 | onTimeUpdate?: JwcEventHandler | undefined;
695 | onTimeUpdateCapture?: JwcEventHandler | undefined;
696 | onVolumeChange?: JwcEventHandler | undefined;
697 | onVolumeChangeCapture?: JwcEventHandler | undefined;
698 | onWaiting?: JwcEventHandler | undefined;
699 | onWaitingCapture?: JwcEventHandler | undefined;
700 |
701 | // MouseEvents
702 | onAuxClick?: MouseEventHandler | undefined;
703 | onAuxClickCapture?: MouseEventHandler | undefined;
704 | onClick?: MouseEventHandler | undefined;
705 | onClickCapture?: MouseEventHandler | undefined;
706 | onContextMenu?: MouseEventHandler | undefined;
707 | onContextMenuCapture?: MouseEventHandler | undefined;
708 | onDoubleClick?: MouseEventHandler | undefined;
709 | onDoubleClickCapture?: MouseEventHandler | undefined;
710 | onDrag?: DragEventHandler | undefined;
711 | onDragCapture?: DragEventHandler | undefined;
712 | onDragEnd?: DragEventHandler | undefined;
713 | onDragEndCapture?: DragEventHandler | undefined;
714 | onDragEnter?: DragEventHandler | undefined;
715 | onDragEnterCapture?: DragEventHandler | undefined;
716 | onDragExit?: DragEventHandler | undefined;
717 | onDragExitCapture?: DragEventHandler | undefined;
718 | onDragLeave?: DragEventHandler | undefined;
719 | onDragLeaveCapture?: DragEventHandler | undefined;
720 | onDragOver?: DragEventHandler | undefined;
721 | onDragOverCapture?: DragEventHandler | undefined;
722 | onDragStart?: DragEventHandler | undefined;
723 | onDragStartCapture?: DragEventHandler | undefined;
724 | onDrop?: DragEventHandler | undefined;
725 | onDropCapture?: DragEventHandler | undefined;
726 | onMouseDown?: MouseEventHandler | undefined;
727 | onMouseDownCapture?: MouseEventHandler | undefined;
728 | onMouseEnter?: MouseEventHandler | undefined;
729 | onMouseLeave?: MouseEventHandler | undefined;
730 | onMouseMove?: MouseEventHandler | undefined;
731 | onMouseMoveCapture?: MouseEventHandler | undefined;
732 | onMouseOut?: MouseEventHandler | undefined;
733 | onMouseOutCapture?: MouseEventHandler | undefined;
734 | onMouseOver?: MouseEventHandler | undefined;
735 | onMouseOverCapture?: MouseEventHandler | undefined;
736 | onMouseUp?: MouseEventHandler | undefined;
737 | onMouseUpCapture?: MouseEventHandler | undefined;
738 |
739 | // Selection Events
740 | onSelect?: JwcEventHandler | undefined;
741 | onSelectCapture?: JwcEventHandler | undefined;
742 |
743 | // Touch Events
744 | onTouchCancel?: TouchEventHandler | undefined;
745 | onTouchCancelCapture?: TouchEventHandler | undefined;
746 | onTouchEnd?: TouchEventHandler | undefined;
747 | onTouchEndCapture?: TouchEventHandler | undefined;
748 | onTouchMove?: TouchEventHandler | undefined;
749 | onTouchMoveCapture?: TouchEventHandler | undefined;
750 | onTouchStart?: TouchEventHandler | undefined;
751 | onTouchStartCapture?: TouchEventHandler | undefined;
752 |
753 | // Pointer Events
754 | onPointerDown?: PointerEventHandler | undefined;
755 | onPointerDownCapture?: PointerEventHandler | undefined;
756 | onPointerMove?: PointerEventHandler | undefined;
757 | onPointerMoveCapture?: PointerEventHandler | undefined;
758 | onPointerUp?: PointerEventHandler | undefined;
759 | onPointerUpCapture?: PointerEventHandler | undefined;
760 | onPointerCancel?: PointerEventHandler | undefined;
761 | onPointerCancelCapture?: PointerEventHandler | undefined;
762 | onPointerEnter?: PointerEventHandler | undefined;
763 | onPointerEnterCapture?: PointerEventHandler | undefined;
764 | onPointerLeave?: PointerEventHandler | undefined;
765 | onPointerLeaveCapture?: PointerEventHandler | undefined;
766 | onPointerOver?: PointerEventHandler | undefined;
767 | onPointerOverCapture?: PointerEventHandler | undefined;
768 | onPointerOut?: PointerEventHandler | undefined;
769 | onPointerOutCapture?: PointerEventHandler | undefined;
770 | onGotPointerCapture?: PointerEventHandler | undefined;
771 | onGotPointerCaptureCapture?: PointerEventHandler | undefined;
772 | onLostPointerCapture?: PointerEventHandler | undefined;
773 | onLostPointerCaptureCapture?: PointerEventHandler | undefined;
774 |
775 | // UI Events
776 | onScroll?: UIEventHandler | undefined;
777 | onScrollCapture?: UIEventHandler | undefined;
778 |
779 | // Wheel Events
780 | onWheel?: WheelEventHandler | undefined;
781 | onWheelCapture?: WheelEventHandler | undefined;
782 |
783 | // Animation Events
784 | onAnimationStart?: AnimationEventHandler | undefined;
785 | onAnimationStartCapture?: AnimationEventHandler | undefined;
786 | onAnimationEnd?: AnimationEventHandler | undefined;
787 | onAnimationEndCapture?: AnimationEventHandler | undefined;
788 | onAnimationIteration?: AnimationEventHandler | undefined;
789 | onAnimationIterationCapture?: AnimationEventHandler | undefined;
790 |
791 | // Transition Events
792 | onTransitionEnd?: TransitionEventHandler | undefined;
793 | onTransitionEndCapture?: TransitionEventHandler | undefined;
794 | }
795 |
796 | interface HTMLAttributes extends AriaAttributes, DOMAttributes {
797 | // React-specific Attributes
798 | defaultChecked?: boolean | undefined;
799 | defaultValue?: string | number | ReadonlyArray | undefined;
800 | suppressContentEditableWarning?: boolean | undefined;
801 | suppressHydrationWarning?: boolean | undefined;
802 |
803 | // Standard HTML Attributes
804 | accessKey?: string | undefined;
805 | class?: string | undefined;
806 | contentEditable?: Booleanish | "inherit" | undefined;
807 | contextMenu?: string | undefined;
808 | dir?: string | undefined;
809 | draggable?: Booleanish | undefined;
810 | hidden?: boolean | undefined;
811 | id?: string | undefined;
812 | lang?: string | undefined;
813 | nonce?: string | undefined;
814 | placeholder?: string | undefined;
815 | slot?: string | undefined;
816 | spellCheck?: Booleanish | undefined;
817 | style?: CSSProperties | undefined;
818 | tabIndex?: number | undefined;
819 | title?: string | undefined;
820 | translate?: "yes" | "no" | undefined;
821 |
822 | // Unknown
823 | radioGroup?: string | undefined; // ,