Package Exports
- link-dom
- link-dom/link-dom.es.min.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (link-dom) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Link Dom
Super concise chained call UI library
Demo | Versions | MessageBoard
Introduction
Link-dom is an extremely concise imperative UI library with chained call features and no third-party dependencies. Its minimum size is only 7.7kb.
Link-dom provides the createStore method for simple state management.
In tool library projects that require UI development, if native js is used to write UI, it will be difficult to manage and maintain, and the code volume will be large. And introducing UI libraries such as Vue and React will greatly increase the package size and even be a bit of overkill. The birth of link-dom is precisely to meet such scenarios of small-scale UI usage. UI is described by JS imperative and componentized UI management is achieved.
Feature
- Imperative UI, supports chained calls and has no third-party dependencies.
- Has friendly type declaration support, covering attributes, styles, Dom tags, etc.
- Has good css-in-js support and supports nested rules similar to less.
- Support reactive css-in-js.
- Supports state management.
Install
npm i link-dom
CDN Usage
<script src="https://cdn.jsdelivr.net/npm/link-dom"></script>
<script>
console.log(window.LinkDom)
</script>
Usage
Dom
Dom is a layer of encapsulation of dom elements, and all operations on the dom are handled inside IUIEle. Chaining and friendly type hints are also implemented in Dom
Generate an Dom element with dom.xxx
import {mount, dom} from 'link-dom';
const hello = dom.div.text('Hello');
mount(hello, 'body');
Dom declaration
export declare class Dom<T extends HTMLElement = HTMLElement> {
__ld_type: LinkDomType;
el: T;
constructor(key: (keyof HTMLElementTagNameMap) | T);
private _ur;
class(): string;
class(val: IReactiveLike<string>): this;
id(): string;
id(val: IReactiveLike<string>): this;
addClass(name: string): this;
removeClass(name: string): this;
hasClass(name: string): boolean;
toggleClass(name: string, force?: boolean): boolean;
replaceClass(n: string, old: string): this;
remove(): this;
text(): string;
text(val: IReactiveLike<string | number>): this;
private __mounted?;
mounted(v: (el: Dom) => void): this;
attr(name: {
[prop in IAttrKey]?: any;
} | Record<string, any>): this;
attr(name: IAttrKey): string;
attr(name: string): string;
attr(name: IAttrKey, value: any): this;
attr(name: string, value: any): this;
removeAttr(key: string): this;
private __xr_funcs;
func(k: string): (...args: any[]) => any;
func(k: string, v: (...args: any[]) => any): this;
data(name: Record<string, any>): this;
data(name: string): any | null;
data(name: string, value: any): this;
style(name: IStyle | Record<string, any>): this;
style(name: IStyleKey | string): string;
style<T extends IStyleKey>(name: T, value: IStyle[T]): this;
value(): string;
value(val: IReactiveLike<string | number>): this;
html(): string;
html(val: IReactiveLike<string | number>): this;
outerHtml(): string;
outerHtml(val: IReactiveLike<string | number>): this;
child<T extends HTMLElement = HTMLElement>(i: number): Dom<T> | null;
next<T extends HTMLElement = HTMLElement>(): Dom<T> | null;
prev<T extends HTMLElement = HTMLElement>(): Dom<T> | null;
firstChild<T extends HTMLElement = HTMLElement>(): Dom<T> | null;
lastChild<T extends HTMLElement = HTMLElement>(): Dom<T> | null;
brothers(): Dom<HTMLElement>[];
children(): Dom<HTMLElement>[];
click(value: IEventObject<this>): this;
on(name: Partial<Record<IEventKey, IEventObject<this>>>): this;
on(name: IEventKey, value?: IEventObject<this>): this;
append(...doms: IChild[]): this;
ref(v: Dom): this;
hide(): this;
display(display?: IReactiveLike<IStyle["display"]>): this;
show(visible: IReactiveLike<boolean>, display?: IStyle["display"]): this;
query<T extends HTMLElement = HTMLElement>(selector: string, one: true): Dom<T>;
query<T extends HTMLElement = HTMLElement>(selector: string, one?: false): Dom<T>[];
src(): string;
src(v: IReactiveLike<string>): this;
parent<T extends HTMLElement = HTMLElement>(i?: number): Dom<T> | null;
empty(): this;
name(): string;
name(v: IReactiveLike<string>): this;
findName(name: string): Dom<HTMLElement>;
find(v: string): Dom<HTMLElement>;
type(name: "text" | "number" | "password" | "checkbox" | "radio" | "color" | "range" | "submit" | "reset" | "input" | "date" | "email" | "tel"): this;
bind(v: IReactiveLike<string | number | boolean>): this;
}
Transform Selector of HTMLElement into Dom
const body = dom('body');
// or
const body = dom(document.body);
style
import {style} from 'link-dom';
style({
'.parent': {
fontSize: '12px',
'*': {color: '#000'},
'.child': {
color: '#444',
'&.active': {color: '#f44'}
},
}
})
reactive style
import {style} from 'link-dom';
const color = ref('#000')
style({
'.parent': {
fontSize: '12px',
'*': {color},
'.child': {
color: ()=>color.value,
'&.active': {color: '#f44'}
},
}
})
events
import {mount, dom} from 'link-dom';
function Main(){
return dom.div.text('Hello World!')
.on('click', (e, self)=>{
console.log('Hello', self)
});
}
mount(Main(), 'body');
Use a decorator
import {mount, dom} from 'link-dom';
function Main(){
return dom.div.text('Hello World!')
.on('click', {
stop: true,
listener: ()=>{
console.log('Hello')
}
});
}
mount(Main(), 'body');
Support 'prevent' | 'stop' | 'capture' | 'once' | 'self'
decorators.
mounted
import {mount, dom} from 'link-dom';
mount(dom.div.mounted(el=>{
console.log('mounted', el);
}), 'body');
collectRef
import {collectRef, mount, dom} from 'link-dom';
function Main(){
const refs = collectRef('hello');
return dom.div.ref(refs.hello)
.click(()=>{console.log(refs.hello.text())})
.text('Hello World!');
}
mount(Main(), 'body');
State management
A simple state management and related APIs to use
import {createStore, mount, computed} from 'link-dom';
function Counter () {
const store = createStore({
count: 0,
});
const increase = () => {
store.count += 1;
};
const unsub = store.$sub('count', (v, pv) => {
console.log(`Subscribe Count Change value=,`, v, `; prevValue=`, pv);
});
const countAdd1 = computed(()=>store.count + 1);
return dom.div.append(
dom.input.type('number').bind(store.count),
dom.span.text(() => `count=${store.count}; count+1=${countAdd1.value}`),
dom.span.text('ShowText').show(()=>store.count % 2 === 1),
dom.button.text('addCount').click(increase),
dom.button.text('UnSubscribe').click(unsub)
);
}
mount(Counter(), 'body');
createStore
Create a state store
import {createStore} from 'link-dom';
const store = createStore({
count: 0,
});
ref
Create a state store
import {ref} from 'link-dom';
const count = ref(0);
console.log(count.value);
computed
import {computed} from 'link-dom';
const store = createStore({
count: 0,
});
const countAdd1 = computed(()=>{
return store.count + 1;
})
const countAdd2 = computed(()=>{
return countAdd1.value + 1;
})
const countSetDemo = computed(()=>{
return store.count + 1;
}, (v)=>{
store.count = v - 1;
});
countAdd1.sub((v, old)=>{
console.log('sub:', v, old);
});
Dom.bind
Used for bidirectional binding of input type elements, the data inside the bind method does not need to be wrapped using react
const input = dom.input.bind(store.count)
bind set Computed
const countSetDemo = computed(()=>{
return store.count + 1;
}, (v)=>{
store.count = v - 1;
});
const input = dom.input.bind(countSetDemo)
Spacial Api
dom.text('hello'); // create textNode
dom.text.text('hello');
dom.comment('hello'); // create Comment
dom.comment.text('hello');
dom.frag.append(/**/); // create DocumentFragment
dom.svg.html(/**/); // create SVGElement
dom.fromHTML('<a>1</a>'); // from html string
dom.query('.test'); // query elements
dom.find('.test'); // find first element
Store.$sub
Subscribe to reactive data changes, which returns an unsubscribe
const unsub = store.$sub('count', (value, prevValue)=>{
console.log(value, prevValue);
});
// Call unsub can unsubscribe
Store.$unsub
Cancel Subscribe
let handler = (value, prevValue)=>{
console.log(value, prevValue);
}
store.$sub('count', handler);
store.$unsub('count', handler);
store.$unsub('count'); // 不传入第二个参数可以取消指定状态的所有订阅
watch
import {watch, createStore, ref, computed} from 'link-dom';
const store = createStore({
count: 0,
});
const count = ref(0);
const countAdd1 = computed(()=>{
return store.count + count + 1;
});
watch(store.count, (v, old)=>{console.log('watch store', v, old)});
watch(count, (v, old)=>{console.log('watch ref', v, old)});
watch(countAdd1, (v, old)=>{console.log('watch computed', v, old)});
watch(()=>countAdd1.value, (v, old)=>{console.log('watch countAdd1', v, old)});
watch(()=>store.count + 1, (v, old)=>{console.log('watch count+1', v, old)});
store.count ++;
count.value ++;
unwatch
import {watch, ref} from 'link-dom';
const count = ref(0);
const unwatch = watch(count, (v, pref)=>{
console.log('watch', ref)
});
unwatch();
Samples
List
import {dom, collectRef, UIEle, mount} from 'link-dom';
function List () {
const refs = collectRef('list');
const list = ['1', '2', '3'];
const SingleChild = (children: UIEle|string|number) => {
return dom.span.append(children);
};
let index = 0;
return dom.div.ref(refs.list).append(
dom.button.text('add').click(() => {
index ++;
list.push(`${index}`);
refs.list.append(SingleChild(index));
}),
list.map(item => SingleChild(item))
);
}
mount(List(), 'body');
Counter
Minimal version
import {dom, createStore, mount} from 'link-dom';
function Counter () {
const store = createStore({ count: 0 });
return dom.button.text(()=>`count is ${store.count}`)
.click(() => store.count++);
}
mount(Counter(), 'body');
With Subscribe
import {dom, createStore, react, mount} from 'link-dom';
function Counter () {
const store = createStore({
count: 0,
});
const increase = () => {
store.count += 1;
};
const unsub = store.$sub('count', (v, pv) => {
console.log(`Subscribe Count Change value=,`, v, `; prevValue=`, pv);
});
const countAdd1 = computed(()=>store.count + 1);
return dom.div.append(
dom.input.type('number').bind(store.count),
dom.span.text(() => `count=${store.count}; count+1=${countAdd1.value}`),
dom.span.text('ShowText').show(()=>store.count % 2 === 1),
dom.button.text('addCount').click(increase),
dom.button.text('UnSubscribe').click(unsub)
);
}
mount(Counter(), 'body');