jsx-dom-runtime
Advanced tools
Comparing version 0.24.0 to 0.25.0
import { declarePreset } from '@babel/helper-plugin-utils'; | ||
const svgTags = new Set([ | ||
const tags$1 = new Set([ | ||
'animate', 'animateMotion', 'animateTransform', 'circle', 'clipPath', | ||
@@ -16,10 +16,8 @@ 'defs', 'desc', 'ellipse', 'feBlend', 'feColorMatrix', 'feComponentTransfer', | ||
const maybeSvg = new Set(['a', 'script', 'style', 'title']); | ||
const maybe = new Set(['a', 'script', 'style', 'title']); | ||
const isSvgElement = (path) => { | ||
return svgTags.has(path.node.name.name) || | ||
(maybeSvg.has(path.node.name.name) && svgTags.has(path.parentPath.parent?.openingElement?.name?.name)); | ||
}; | ||
const isSvgTag = (tag) => tags$1.has(tag); | ||
const maybeSvg = (tag) => maybe.has(tag); | ||
const htmlTags = new Set([ | ||
const tags = new Set([ | ||
'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', | ||
@@ -42,3 +40,5 @@ 'base', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', | ||
const DOMEvents = new Set([ | ||
const isHtmlTag = (tag) => tags.has(tag); | ||
const events = new Set([ | ||
'oncopy', 'oncut', 'onpaste', | ||
@@ -84,18 +84,50 @@ // Composition Events | ||
const isDOMEvent = (name) => events.has(name); | ||
const jsxPlugin = (babel) => { | ||
const { types: t } = babel; | ||
const createNsAttribute = (val) => { | ||
return t.jSXAttribute( | ||
t.jSXIdentifier('__ns'), | ||
t.JSXExpressionContainer(t.numericLiteral(val)) | ||
); | ||
const isSvgElement = (path) => { | ||
return isSvgTag(path.node.name.name) || | ||
maybeSvg(path.node.name.name) && | ||
path.parentPath.parent.openingElement?.attributes.some( | ||
(a) => a.name.name === '__ns' && a.value.expression?.value === 1 | ||
); | ||
}; | ||
const xlinkHref = (attrs) => { | ||
return attrs.findIndex((attr) => { | ||
return t.isJSXNamespacedName(attr.name) && | ||
attr.name.namespace.name === 'xlink' && | ||
attr.name.name.name === 'href'; | ||
}); | ||
}; | ||
return { | ||
visitor: { | ||
JSXOpeningElement(path) { | ||
if (isSvgElement(path)) { | ||
path.node.attributes.push(createNsAttribute(1)); | ||
if (!isSvgElement(path)) { | ||
return; | ||
} | ||
const attrs = path.node.attributes; | ||
const index = xlinkHref(attrs); | ||
if (index > -1) { | ||
const attr = attrs.splice(index, 1).pop(); | ||
attrs.push( | ||
t.jSXAttribute( | ||
t.jSXIdentifier('href'), | ||
t.stringLiteral(attr.value.value), | ||
), | ||
); | ||
} | ||
attrs.push( | ||
t.jSXAttribute( | ||
t.jSXIdentifier('__ns'), | ||
t.JSXExpressionContainer(t.numericLiteral(1)), | ||
), | ||
); | ||
}, | ||
@@ -110,26 +142,22 @@ JSXAttribute(path) { | ||
if ( | ||
attr.name === 'className' && | ||
(htmlTags.has(tag) || svgTags.has(tag)) | ||
) { | ||
attr.name = 'class'; | ||
if (attr.name === 'className') { | ||
if (isHtmlTag(tag) || isSvgTag(tag)) { | ||
attr.name = 'class'; | ||
} | ||
return; | ||
} | ||
if (htmlTags.has(tag)) { | ||
if ( | ||
attr.name === 'htmlFor' && | ||
(tag === 'label' || tag === 'output') | ||
) { | ||
if (attr.name === 'htmlFor') { | ||
if (tag === 'label' || tag === 'output') { | ||
attr.name = 'for'; | ||
return; | ||
} | ||
return; | ||
} | ||
if (isHtmlTag(tag)) { | ||
const attrName = attr.name.toLowerCase(); | ||
if (DOMEvents.has(attrName)) { | ||
if (isDOMEvent(attrName)) { | ||
attr.name = attrName; | ||
return; | ||
} | ||
return; | ||
@@ -136,0 +164,0 @@ } |
@@ -137,3 +137,3 @@ /// <reference lib="dom" /> | ||
onwaiting?: TEventHandler<T> | ||
// MouseEvents | ||
// Mouse Events | ||
onauxclick?: MouseEventHandler<T> | ||
@@ -146,2 +146,3 @@ onclick?: MouseEventHandler<T> | ||
ondragenter?: DragEventHandler<T> | ||
/** @deprecated Not supported */ | ||
ondragexit?: DragEventHandler<T> | ||
@@ -393,5 +394,77 @@ ondragleave?: DragEventHandler<T> | ||
// All the WAI-ARIA 1.1 role attribute values from https://www.w3.org/TR/wai-aria-1.1/#role_definitions | ||
type AriaRole = | ||
| 'alert' | ||
| 'alertdialog' | ||
| 'application' | ||
| 'article' | ||
| 'banner' | ||
| 'button' | ||
| 'cell' | ||
| 'checkbox' | ||
| 'columnheader' | ||
| 'combobox' | ||
| 'complementary' | ||
| 'contentinfo' | ||
| 'definition' | ||
| 'dialog' | ||
| 'directory' | ||
| 'document' | ||
| 'feed' | ||
| 'figure' | ||
| 'form' | ||
| 'grid' | ||
| 'gridcell' | ||
| 'group' | ||
| 'heading' | ||
| 'img' | ||
| 'link' | ||
| 'list' | ||
| 'listbox' | ||
| 'listitem' | ||
| 'log' | ||
| 'main' | ||
| 'marquee' | ||
| 'math' | ||
| 'menu' | ||
| 'menubar' | ||
| 'menuitem' | ||
| 'menuitemcheckbox' | ||
| 'menuitemradio' | ||
| 'navigation' | ||
| 'none' | ||
| 'note' | ||
| 'option' | ||
| 'presentation' | ||
| 'progressbar' | ||
| 'radio' | ||
| 'radiogroup' | ||
| 'region' | ||
| 'row' | ||
| 'rowgroup' | ||
| 'rowheader' | ||
| 'scrollbar' | ||
| 'search' | ||
| 'searchbox' | ||
| 'separator' | ||
| 'slider' | ||
| 'spinbutton' | ||
| 'status' | ||
| 'switch' | ||
| 'tab' | ||
| 'table' | ||
| 'tablist' | ||
| 'tabpanel' | ||
| 'term' | ||
| 'textbox' | ||
| 'timer' | ||
| 'toolbar' | ||
| 'tooltip' | ||
| 'tree' | ||
| 'treegrid' | ||
| 'treeitem' | ||
| (string & {}); | ||
export interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> { | ||
innerHTML?: string | ||
textContent?: string | ||
accessKey?: string | ||
@@ -419,3 +492,3 @@ accesskey?: string | ||
// WAI-ARIA | ||
role?: string | ||
role?: AriaRole | ||
// RDFa Attributes | ||
@@ -1005,2 +1078,9 @@ about?: string | ||
type Element = HTMLElement | ||
type ElementType<P = any> = | ||
| { | ||
[K in keyof IntrinsicElements]: P extends IntrinsicElements[K] | ||
? K | ||
: never; | ||
}[keyof IntrinsicElements] | ||
| HTMLElement; | ||
@@ -1007,0 +1087,0 @@ interface ElementAttributesProperty { props: {}; } |
@@ -24,3 +24,3 @@ let appendChildren = (node, children) => { | ||
let internalKeys = new Set(['ref', 'children', '__ns']); | ||
let properties = new Set(['innerHTML', 'textContent', 'value']); | ||
let properties = new Set(['innerHTML', 'value', 'muted']); | ||
let jsx = (node, props) => { | ||
@@ -27,0 +27,0 @@ if (typeof node === 'function') { |
{ | ||
"name": "jsx-dom-runtime", | ||
"version": "0.24.0", | ||
"version": "0.25.0", | ||
"description": "A tiny in 500 bytes library to JSX syntax templates for DOM", | ||
@@ -48,3 +48,2 @@ "type": "module", | ||
"devDependencies": { | ||
"@babel/preset-env": "^7.20.2", | ||
"@babel/preset-typescript": "^7.18.6", | ||
@@ -54,3 +53,2 @@ "@evilmartians/lefthook": "^1.2.9", | ||
"@rollup/plugin-babel": "^6.0.3", | ||
"@rollup/plugin-node-resolve": "^15.0.1", | ||
"@size-limit/preset-small-lib": "^8.2.4", | ||
@@ -57,0 +55,0 @@ "@testing-library/dom": "^8.20.0", |
@@ -31,6 +31,6 @@ # jsx-dom-runtime | ||
```js | ||
import { createRef } from 'jsx-dom-runtime'; | ||
import { useRef } from 'jsx-dom-runtime'; | ||
const App = () => { | ||
const List = createRef(); | ||
const List = useRef(); | ||
@@ -71,9 +71,11 @@ const addItem = () => { | ||
Adding a reference to a DOM Element | ||
```js | ||
import { createRef } from 'jsx-dom-runtime'; | ||
import { useRef } from 'jsx-dom-runtime'; | ||
let i = 0; | ||
const ref = createRef(); | ||
const clickHandler = () => { | ||
const ref = useRef(); | ||
const click = () => { | ||
ref.current.textContent = ++i; | ||
@@ -83,4 +85,4 @@ }; | ||
<document.body> | ||
<p ref={ref}>{i}</p> | ||
<button type="button" onclick={clickHandler}> | ||
<p ref={ref}>0</p> | ||
<button type="button" onclick={click}> | ||
+ 1 | ||
@@ -94,13 +96,50 @@ </button> | ||
```js | ||
const autofocus = (node) => { | ||
setTimeout(() => node.focus(), 100); | ||
const focus = (node) => { | ||
node.addEventListener('focusin', () => { | ||
node.style.backgroundColor = 'pink'; | ||
}); | ||
node.addEventListener('focusout', () => { | ||
node.style.backgroundColor = ''; | ||
}); | ||
}; | ||
<document.body> | ||
<input type="text" ref={autofocus} /> | ||
<input type="text" ref={focus} /> | ||
</document.body>; | ||
``` | ||
### Extend | ||
## Text | ||
```js | ||
import { useText } from 'jsx-dom-runtime'; | ||
const [text, setText] = useText('The initial text'); | ||
<document.body> | ||
<p>{text}</p> | ||
<button type="button" onclick={() => setText('Clicked!')}> | ||
Click me | ||
</button> | ||
</document.body>; | ||
``` | ||
### Template | ||
Get template from string. | ||
```js | ||
import { Template } from 'jsx-dom-runtime'; | ||
<document.body> | ||
<Template> | ||
{`<svg width="24" height="24" aria-hidden="true"> | ||
<path d="M12 12V6h-1v6H5v1h6v6h1v-6h6v-1z"/> | ||
</svg>`} | ||
</Template> | ||
</document.body> | ||
``` | ||
## Extend | ||
Add custom attributes in `JSX.Element`. | ||
@@ -129,3 +168,3 @@ | ||
x-dataset={{ testid: 'test', hook: 'text' }} | ||
x-autofocus={100} | ||
x-autofocus={1000} | ||
/> | ||
@@ -141,20 +180,25 @@ </document.body>; | ||
### Template | ||
Add support of the properties to DOM Element object. | ||
Get template from string | ||
```js | ||
import { Template } from 'jsx-dom-runtime'; | ||
import { properties } from 'jsx-dom-runtime'; | ||
properties.add('innerText'); | ||
properties.add('textContent'); | ||
<document.body> | ||
<Template> | ||
{`<svg width="24" height="24" aria-hidden="true"> | ||
<path d="M12 12V6h-1v6H5v1h6v6h1v-6h6v-1z"/> | ||
</svg>`} | ||
</Template> | ||
</document.body> | ||
<span innerText="Hello" />{', '} | ||
<span textContent="World" /> | ||
</document.body>; | ||
``` | ||
Result | ||
```html | ||
<span>Hello</span>, <span>World</span> | ||
``` | ||
## License | ||
[MIT](./LICENSE) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
60700
17
1704
199