/
githubmirror
/
preact
Обзор
Документация
Войти
/
githubmirror
/
preact
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
test/ts/preact.tsx
459 строк
10 KB
Jovi De Croock
convert to oxfmt
14 июл 2026, 21:48
14 июл 2026, 21:48
041216f
Код
Авторство
О чём код?
import { createElement, render, Component, ComponentProps, FunctionalComponent, AnyComponent, h, createRef, SignalLike, ComponentChildren, TargetedEvent, TargetedInputEvent, TargetedToggleEvent, TargetedSubmitEvent, AllHTMLAttributes, HTMLAttributes, InputHTMLAttributes } from '../../'; function createSignal<T>(value: T): SignalLike<T> { return { value, peek() { return value; }, subscribe() { return () => {}; } }; } interface DummyProps { initialInput: string; } interface DummyState { input: string; } class DummyComponent extends Component<DummyProps, DummyState> { constructor(props: DummyProps) { super(props); this.state = { input: `x${this.props}x` }; } private setRef = (el: AnyComponent<any>) => { console.log(el); }; render({ initialInput }: DummyProps, { input }: DummyState) { return ( <div> <DummerComponent initialInput={initialInput} input={input} /> {/* Can specify all Preact attributes on a typed FunctionalComponent */} <ComponentWithChildren initialInput={initialInput} input={input} key="1" ref={this.setRef} /> </div> ); } } interface DummerComponentProps extends DummyProps, DummyState {} function DummerComponent({ input, initialInput }: DummerComponentProps) { return ( <div> Input: {input}, initial: {initialInput} </div> ); } render(createElement('div', { title: 'test', key: '1' }), document); render( createElement(DummyComponent, { initialInput: 'The input', key: '1' }), document ); render( createElement(DummerComponent, { initialInput: 'The input', input: 'New input', key: '1' }), document ); render(h('div', { title: 'test', key: '1' }), document); render(h(DummyComponent, { initialInput: 'The input', key: '1' }), document); render( h(DummerComponent, { initialInput: 'The input', input: 'New input', key: '1' }), document ); // Mounting into different types of Nodes render(h('div', {}), document.createElement('div')); render(h('div', {}), document); render(h('div', {}), document.createElement('div').shadowRoot!); render(h('div', {}), document.createDocumentFragment()); // From https://gist.github.com/developit/f4c67a2ede71dc2fab7f357f39cff28c, modified to be TypeScript compliant function createRootFragment(parent: Element, replaceNode: Element | Element[]) { const replaceNodes: Element[] = ([] as Element[]).concat(replaceNode); const s = replaceNodes[replaceNodes.length - 1].nextSibling; function insert(c: Node, r: Node | null) { return parent.insertBefore(c, r || s); } return ((parent as any).__k = { nodeType: 1, parentNode: parent, firstChild: replaceNodes[0], childNodes: replaceNodes, contains: (c: Node) => parent.contains(c), insertBefore: insert, appendChild: (c: Node) => insert(c, null), removeChild: function (c: Node) { return parent.removeChild(c); } }); } render( h('div', {}), createRootFragment( document.createElement('div'), document.createElement('div') ) ); // Accessing children const ComponentWithChildren: FunctionalComponent<DummerComponentProps> = ({ input, initialInput, children }) => { return ( <div> <span>{initialInput}</span> <span>{input}</span> <span>{children}</span> </div> ); }; const UseOfComponentWithChildren = () => { return ( <ComponentWithChildren initialInput="initial" input="input"> <span>child 1</span> <span>child 2</span> </ComponentWithChildren> ); }; const DummyChildren: FunctionalComponent = ({ children }) => { return children; }; function ReturnChildren(props: { children: preact.ComponentChildren }) { return props.children; } function TestUndefinedChildren() { return ( <ReturnChildren> <ReturnChildren>Hello</ReturnChildren> </ReturnChildren> ); } // using ref and or jsx class ComponentUsingRef extends Component<any, any> { private array: string[]; private refs: (Element | null)[] = []; constructor() { super(); this.array = ['1', '2']; } render() { this.refs = []; return ( <div jsx> {this.array.map(el => ( <span ref={this.setRef}>{el}</span> ))} {/* Can specify Preact attributes on a component */} <DummyComponent initialInput="1" key="1" ref={this.setRef} /> </div> ); } private setRef = (el: Element | null) => { this.refs.push(el); }; } // using lifecycles class ComponentWithLifecycle extends Component<DummyProps, DummyState> { render() { return <div>Hi</div>; } componentWillMount() { console.log('componentWillMount'); } componentDidMount() { console.log('componentDidMount'); } componentWillUnmount() { console.log('componentWillUnmount'); } componentWillReceiveProps(nextProps: DummyProps, nextCtx: any) { const { initialInput } = nextProps; console.log('componentWillReceiveProps', initialInput, nextCtx); } shouldComponentUpdate( nextProps: DummyProps, nextState: DummyState, nextContext: any ) { return false; } componentWillUpdate( nextProps: DummyProps, nextState: DummyState, nextContext: any ) { console.log('componentWillUpdate', nextProps, nextState, nextContext); } componentDidUpdate( previousProps: DummyProps, previousState: DummyState, previousContext: any ) { console.log( 'componentDidUpdate', previousProps, previousState, previousContext ); } } // Default props: JSX.LibraryManagedAttributes class DefaultProps extends Component<{ text: string; bool: boolean }> { static defaultProps = { text: 'hello' }; render() { return <div>{this.props.text}</div>; } } const d1 = <DefaultProps bool={false} text="foo" />; const d2 = <DefaultProps bool={false} />; class DefaultPropsWithUnion extends Component< { default: boolean } & ( | { type: 'string'; str: string; } | { type: 'number'; num: number; } ) > { static defaultProps = { default: true }; render() { return <div />; } } const d3 = <DefaultPropsWithUnion type="string" str={'foo'} />; const d4 = <DefaultPropsWithUnion type="number" num={0xf00} />; const d5 = <DefaultPropsWithUnion type="string" str={'foo'} default={false} />; const d6 = <DefaultPropsWithUnion type="number" num={0xf00} default={false} />; class DefaultUnion extends Component< | { type: 'number'; num: number; } | { type: 'string'; str: string; } > { static defaultProps = { type: 'number', num: 1 }; render() { return <div />; } } const d7 = <DefaultUnion />; const d8 = <DefaultUnion num={1} />; const d9 = <DefaultUnion type="number" />; const d10 = <DefaultUnion type="string" str="foo" />; class ComponentWithDefaultProps extends Component<{ value: string }> { static defaultProps = { value: '' }; render() { return <div>{this.props.value}</div>; } } const withDefaultProps = <ComponentWithDefaultProps />; interface PartialState { foo: string; bar: number; } class ComponentWithPartialSetState extends Component<{}, PartialState> { render({}, { foo, bar }: PartialState) { return ( <button onClick={() => this.handleClick('foo')}> {foo}-{bar} </button> ); } handleClick = (value: keyof PartialState) => { this.setState({ [value]: 'updated' }); }; } const withPartialSetState = <ComponentWithPartialSetState />; let functionalProps: ComponentProps<typeof DummerComponent> = { initialInput: '', input: '' }; let classProps: ComponentProps<typeof DummyComponent> = { initialInput: '' }; let elementProps: ComponentProps<'button'> = { type: 'button' }; // Typing of style property const acceptsNumberAsLength = <div style={{ marginTop: 20 }} />; const acceptsStringAsLength = <div style={{ marginTop: '20px' }} />; const ReturnNull: FunctionalComponent = () => null; // Should accept arbitrary properties outside of JSX.HTMLAttributes h('option', { x: 'foo' }); createElement('option', { value: 'foo' }); // Refs should work on elements const ref = createRef<HTMLDivElement>(); createElement('div', { ref: ref }, 'hi'); h('div', { ref: ref }, 'hi'); // Refs should work on functions const functionRef = createRef(); const RefComponentTest = () => <p>hi</p>; createElement(RefComponentTest, { ref: functionRef }, 'hi'); h(RefComponentTest, { ref: functionRef }, 'hi'); // Should accept onInput const onInput = (e: TargetedInputEvent<HTMLInputElement>) => {}; <input onInput={onInput} />; <input onInput={e => e.currentTarget.value} />; createElement('input', { onInput: onInput }); h('input', { onInput: onInput }); // Should accept onBeforeInput const onBeforeInput = (e: TargetedInputEvent<HTMLInputElement>) => {}; <input onBeforeInput={e => e.currentTarget.value} />; createElement('input', { onBeforeInput: onBeforeInput }); h('input', { onBeforeInput: onBeforeInput }); // Should accept onSubmit const onSubmit = (e: TargetedSubmitEvent<HTMLFormElement>) => {}; <form onSubmit={e => e.currentTarget.elements} />; createElement('form', { onSubmit: onSubmit }); h('form', { onSubmit: onSubmit }); // Should accept onToggle const onToggle = (e: TargetedToggleEvent<HTMLDetailsElement>) => {}; <dialog onToggle={e => ({ newState: e.newState, oldState: e.oldState })} />; createElement('dialog', { onToggle: onToggle }); h('dialog', { onToggle: onToggle }); // Should default to correct event target element for the attribute interface h<InputHTMLAttributes>('input', { onClick: e => e.currentTarget.capture }); createElement<InputHTMLAttributes>('input', { onClick: e => e.currentTarget.capture }); const inputAttributesWithSignalType: InputHTMLAttributes<HTMLInputElement> = { type: createSignal('text' as const) }; createElement<InputHTMLAttributes>('input', { type: createSignal('email' as const) }); <input onClick={e => e.currentTarget.capture} />; function Checkbox({ onChange }: HTMLAttributes<HTMLInputElement>) { function handleChange(this: void, event: TargetedEvent<HTMLInputElement>) { onChange?.call(this, event); } return <input onChange={handleChange} />; } // `AllHTMLAttributes` should support all interfaces used within `JSX.IntrinsicElements` const allHTMLAttributes: AllHTMLAttributes<HTMLMarqueeElement> = { // Global HTMLAttributes class: 'foo', // Per-element attributes autoCapitalize: 'off', dateTime: '2021-01-01', href: createSignal('https://example.com'), itemID: 'foo', maxlength: createSignal(10), playsInline: true, // ClassAttributes ref: createRef<HTMLMarqueeElement>(), // DOMAttributes onClick: (e: TargetedEvent<HTMLMarqueeElement>) => {}, // AriaAttributes 'aria-colcount': 1 };