{"id":23366727,"url":"https://github.com/d8corp/innet-dom","last_synced_at":"2025-04-07T23:16:02.251Z","repository":{"id":56755421,"uuid":"473759604","full_name":"d8corp/innet-dom","owner":"d8corp","description":"Tools to build Web Site","archived":false,"fork":false,"pushed_at":"2023-06-25T10:56:06.000Z","size":2716,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-14T11:03:53.221Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/d8corp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-24T20:14:46.000Z","updated_at":"2022-04-26T20:27:44.000Z","dependencies_parsed_at":"2023-02-19T16:40:17.907Z","dependency_job_id":null,"html_url":"https://github.com/d8corp/innet-dom","commit_stats":null,"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d8corp%2Finnet-dom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d8corp%2Finnet-dom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d8corp%2Finnet-dom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d8corp%2Finnet-dom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/d8corp","download_url":"https://codeload.github.com/d8corp/innet-dom/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247744329,"owners_count":20988783,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-21T14:17:26.849Z","updated_at":"2025-04-07T23:16:02.228Z","avatar_url":"https://github.com/d8corp.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003ca href=\"https://www.npmjs.com/package/innet\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/d8corp/innet/main/logo.svg\" align=\"left\" width=\"90\" height=\"90\" alt=\"InnetJs logo by Mikhail Lysikov\"\u003e\n\u003c/a\u003e\n\n# \u0026nbsp; @innet/dom\n\n\u0026nbsp;\n\n[![NPM](https://img.shields.io/npm/v/@innet/dom.svg)](https://www.npmjs.com/package/@innet/dom)\n[![downloads](https://img.shields.io/npm/dm/@innet/dom.svg)](https://www.npmtrends.com/@innet/dom)\n[![changelog](https://img.shields.io/badge/Changelog-⋮-brightgreen)](https://changelogs.xyz/@innet/dom)\n[![license](https://img.shields.io/npm/l/@innet/dom)](https://github.com/d8corp/innet-dom/blob/main/LICENSE)\n\n## Abstract\nThis is an `innet` tool, that helps to create frontend-side application.\n\nHere you can find JSX components, state-management, portals, context, slots, routing and more.\n\nBased on [innet](https://www.npmjs.com/package/innet).\n\n[![stars](https://img.shields.io/github/stars/d8corp/innet-dom?style=social)](https://github.com/d8corp/innet-dom/stargazers)\n[![watchers](https://img.shields.io/github/watchers/d8corp/innet-dom?style=social)](https://github.com/d8corp/innet-dom/watchers)\n\n## Install\nUse [innetjs](https://www.npmjs.com/package/innetjs) to start `innet-dom` app development.\n\n```shell\nnpx innetjs init my-app -t fe\n```\n*change my-app to work folder name*\n\nGo into `my-app` and check `README.md`\n\n## Handler\n\nUse `dom` handler to start an application.\n\nClear `src` folder and create `index.ts` inside.\n```typescript\nimport innet from 'innet'\nimport dom from '@innet/dom'\n\nimport app from './app'\n\ninnet(app, dom)\n```\n\n## JSX\nYou can use xml-like syntax to create and append elements into the DOM.\nMore information about JSX [here](https://www.typescriptlang.org/docs/handbook/jsx.html).\n\nCreate `app.tsx` in `src` folder.\n```typescript jsx\nexport default (\n  \u003ch1\u003e\n    Hello World!\n  \u003c/h1\u003e\n)\n```\n\nEverything, that you provide as the first argument of `innet` function with the `dom` handler,\nwill fall into the `body` DOM-element.\n\n## portal\n\nIf you want to put your content into another element (not `body`), use portal element.\n\nFor example, you can change `index.html` from `public` folder.\n```html\n\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead ... \u003e\n\u003cbody\u003e\n  \u003cdiv id=\"app\"\u003e\u003c/div\u003e\n  \u003c!-- add this ^ --\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nAnd change `app.tsx`\n\n```typescript jsx\nconst app = document.getElementById('app')\n\nexport default (\n  \u003cportal parent={app}\u003e\n    \u003ch1\u003e\n      Hello World!\n    \u003c/h1\u003e\n  \u003c/portal\u003e\n)\n```\n\nYou can use `portal` everywhere inside the app.\n\nChange `app.tsx`\n```typescript jsx\nconst app = document.getElementById('app')\nconst myElement = document.createElement('div')\n\nexport default (\n  \u003cportal parent={app}\u003e\n    \u003ch1\u003e\n      Hello World!\n    \u003c/h1\u003e\n    \u003cportal parent={myElement}\u003e\n      This is content of myElement\n    \u003c/portal\u003e\n  \u003c/portal\u003e\n)\n```\n\n`myElement` should contain `This is content of myElement` and `app` should contain the next code.\n```html\n\u003ch1\u003e\n  Hello World!\n\u003c/h1\u003e\n```\n\n## State Management\n\nUsually, state management is available only inside a component.\n\nWith `innet` you can fully exclude component approach, but state management still to be available.\n\nThe state management based on [watch-state](https://github.com/d8corp/watch-state)\n\nTo bind state and content, use `State`, `Cache` or a function as the content.\n\nTurn back `index.html` and change `app.tsx`\n```typescript jsx\nimport { State } from 'watch-state'\n\nconst count = new State(0)\n\nconst increase = () =\u003e {\n  count.value++\n}\n\nexport default (\n  \u003c\u003e\n    \u003ch1\u003e\n      Count: {count}\n    \u003c/h1\u003e\n    \u003cbutton onclick={increase}\u003e\n      Click Me\n    \u003c/button\u003e\n  \u003c/\u003e\n)\n```\n\nTo bind a state and a prop use `State`, `Cache` or a function as a value of the prop.\n\nChange `app.tsx`\n```typescript jsx\nimport { State } from 'watch-state'\n\nconst darkMode = new State(false)\n\nconst handleChange = (e: Event) =\u003e {\n  darkMode.value = e.target.checked\n}\n\nexport default (\n  \u003cdiv class={() =\u003e darkMode.value ? 'dark' : 'light'}\u003e\n    \u003ch1\u003e\n      Hello World!\n    \u003c/h1\u003e\n    \u003clabel\u003e\n      \u003cinput\n        type=\"checkbox\"\n        onchange={handleChange}\n      /\u003e\n      Dark Mode\n    \u003c/label\u003e\n  \u003c/div\u003e\n)\n```\n\n## Components\n\nComponent is a function.\nYou can use it as JSX element.\n\nCreate `Content.tsx`\n```typescript jsx\nexport const Content = () =\u003e (\n  \u003ch1\u003e\n    Hello World!\n  \u003c/h1\u003e\n)\n```\n\nChange `app.tsx`\n```typescript jsx\nimport { Content } from './Content'\n\nexport default (\n  \u003cContent /\u003e\n)\n```\n\n#### props\nAny component gets an argument `props`.  \nIf props have not provided the argument equals `undefined` else you get an object that contains the props.\n\nChange `Content.tsx`\n```typescript jsx\nexport function Content ({ color }) {\n  return (\n    \u003ch1 style={{ color }}\u003e\n      Hello World!\n    \u003c/h1\u003e\n  )\n}\n```\n\nThen you should use the `color` prop outside.\n\nChange `app.tsx`\n```typescript jsx\nimport { Content } from './Content'\n\nexport default (\n  \u003cContent color='red' /\u003e\n)\n```\n\n### Hooks\nYou can use hooks inside a component. Sync hooks should be used before `await`,\nasync hooks should be used as the first `await`.\n\n```typescript jsx\nexport async function Content (props1) {\n  const sync1 = useSyncHook1()\n  const sync2 = useSyncHook2()\n  \n  const [\n    async1,\n    async2,\n  ] = await Promise.all([\n    useAsyncHook1(),\n    useAsyncHook2(),\n  ])\n  \n  // other\n}\n```\n\n#### useProps\nYou can get props with `useProps` hook.\n\n```typescript jsx\nimport { useProps } from '@innet/jsx'\n\nexport function Content (props1) {\n  const props2 = useProps()\n\n  return (\n    \u003ch1\u003e\n      {props1 === props2 ? 'same' : 'different'}\n    \u003c/h1\u003e\n  )\n}\n```\n\n#### useChildren\nTo get children elements you can take `useChildren`.\n\nChange `Content.tsx`\n```typescript jsx\nimport { useChildren } from '@innet/jsx'\n\nexport function Content ({ color }) {\n  const children = useChildren()\n\n  return (\n    \u003ch1 style={{ color }}\u003e\n      {children}\n    \u003c/h1\u003e\n  )\n}\n```\n\nThen you can use the children outside.\n\nChange `app.tsx`\n```typescript jsx\nimport { Content } from './Content'\n\nexport default (\n  \u003cContent color='red'\u003e\n    Hello World!\n  \u003c/Content\u003e\n)\n```\n\n### Return\n\nA component awaits a return:\n- `string`, `number` - render as text node\n  ```typescript jsx\n  const Test1 = () =\u003e 123\n  const Test2 = () =\u003e '123'\n  ```\n- `null`, `undefined`, `boolean`, `symbol` - ignore\n  ```typescript jsx\n  const Test1 = () =\u003e null\n  const Test2 = () =\u003e {}\n  const Test3 = () =\u003e true\n  const Test4 = () =\u003e Symbol()\n  ```\n- DOM Element - put in the DOM\n  ```typescript jsx\n  const Test = () =\u003e document.createElement('div')\n  ```\n- JSX Fragment, `array` - render content\n  ```typescript jsx\n  const Test1 = () =\u003e \u003c\u003econtent\u003c/\u003e\n  const Test2 = () =\u003e ['content']\n  ```\n- JSX Element - put in the DOM\n  ```typescript jsx\n  const Test1 = () =\u003e \u003cdiv\u003econtent\u003c/div\u003e\n  const Test2 = () =\u003e \u003cbr /\u003e\n  ```\n- JSX Plugin - run plugin\n  ```typescript jsx\n  const Test1 = () =\u003e \u003cportal parent={app}\u003econtent\u003c/portal\u003e\n  const Test2 = () =\u003e \u003cslot\u003econtent\u003c/slot\u003e\n  ```\n- function - observable children\n  ```typescript jsx\n  const state = new State()\n  const Test1 = () =\u003e () =\u003e state.value\n  const Test2 = () =\u003e state\n  const Test3 = () =\u003e \u003c\u003e{() =\u003e state.value}\u003c/\u003e\n  ```\n\n### Life Cycle\nEach component renders only once!\n\nThere are 3 steps of life cycle:\n- **render** (DOM elements are not created)\n- **mounted** (DOM elements are created)\n- **destroy** (elements will be removed from the DOM)\n\nBecause of a component renders only once you can have effects right inside the component function.\n```typescript jsx\nimport { State } from 'watch-state'\n\nfunction Content () {\n  const state = new State()\n\n  fetch('...')\n    .then(e =\u003e e.json())\n    .then(data =\u003e {\n      state.value = data.text\n    })\n\n  return (\n    \u003cdiv\u003e\n      {state}\n    \u003c/div\u003e\n  )\n}\n```\n\n### Async Component\n\nInnet supports async components, you can simplify previous code.\n```typescript jsx\nasync function Content () {\n  const { text } = await fetch('...').then(e =\u003e e.json())\n\n  return \u003cdiv\u003e{text}\u003c/div\u003e\n}\n```\n\n[innetjs](https://www.npmjs.com/package/innetjs) helps to make code splitting.\n```typescript jsx\nasync function Content () {\n  const { Test } = await import('./Test')\n\n  return (\n    \u003cdiv\u003e\n      \u003cTest /\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\n`Test.tsx`\n```typescript jsx\nexport const Test = () =\u003e (\n  \u003cdiv\u003e\n    Test success!\n  \u003c/div\u003e\n)\n```\n\nWhile it's loading nothing can be shown.\nIf you want to show something, use `Generic Async Component`.\n\n### Generic Async Component\n\nJust add a star and use `yield` instead of `return`\n```typescript jsx\nasync function * Content () {\n  yield 'Loading...'\n\n  const { text } = await fetch('...').then(e =\u003e e.json())\n\n  yield \u003cdiv\u003e{text}\u003c/div\u003e\n}\n```\n\n### Generic Component\n\nIt can be useful when you want to do something after a content deployed.\n\n```typescript jsx\nfunction * Content () {\n  yield (\n    \u003cdiv id='test'\u003e\n      Hello World!\n    \u003c/div\u003e\n  )\n\n  colsole.log(document.getElementById('test'))\n}\n```\n\nYou can use `queueMicrotask` instead of a generic component, but there are a small difference:\n\n`queueMicrotask` runs after whole content is available and generic component runs right after the content of the component rendered.\n```typescript jsx\nfunction * A () {\n  queueMicrotask(() =\u003e {\n    console.log(\n      'queueMicrotask A',\n      document.getElementById('a'),\n      document.getElementById('b'),\n    )\n  })\n\n  yield \u003cspan id='a' /\u003e\n\n  console.log(\n    'generic A',\n    document.getElementById('a'),\n    document.getElementById('b'),\n  )\n}\n\nfunction * B () {\n  queueMicrotask(() =\u003e {\n    console.log(\n      'queueMicrotask B',\n      document.getElementById('a'),\n      document.getElementById('b'),\n    )\n  })\n\n  yield \u003cspan id='b' /\u003e\n\n  console.log(\n    'generic B',\n    document.getElementById('a'),\n    document.getElementById('b'),\n  )\n}\n\nfunction Content () {\n  return (\n    \u003c\u003e\n      \u003cA /\u003e\n      \u003cB /\u003e\n    \u003c/\u003e\n  )\n}\n```\n\nYou get the next output:\n\n```\ngeneric A \u003cspan id=\"a\"\u003e\u003c/span\u003e null\ngeneric B \u003cspan id=\"a\"\u003e\u003c/span\u003e \u003cspan id=\"b\"\u003e\u003c/span\u003e\nqueueMicrotask A \u003cspan id=\"a\"\u003e\u003c/span\u003e \u003cspan id=\"b\"\u003e\u003c/span\u003e\nqueueMicrotask B \u003cspan id=\"a\"\u003e\u003c/span\u003e \u003cspan id=\"b\"\u003e\u003c/span\u003e\n```\n\n## Ref\n\n`Ref` helps to get an HTML element.\n```typescript jsx\nimport { Ref } from '@innet/dom'\n\nfunction * Content () {\n  const wrapper = new Ref\u003cHTMLDivElement\u003e()\n  \n  yield (\n    \u003cdiv ref={wrapper}\u003e\n      Hello World!\n    \u003c/div\u003e\n  )\n\n  colsole.log(wrapper.value)\n}\n```\n\n## onDestroy\n\nYou can subscribe on destroy of a component by `onDestroy` from `watch-state`\n\nChange `Content.tsx`\n\n```typescript jsx\nimport { State, onDestroy } from 'watch-state'\n\nexport function Content() {\n  const count = new State(0)\n  // create a state\n\n  const timer = setInterval(() =\u003e {\n    count.value++\n  }, 1000)\n  // increase the state each second\n\n  onDestroy(() =\u003e clearInterval(timer))\n  // stop timer on destroy\n\n  return () =\u003e count.value\n  // return observable value\n}\n```\n\nAnd change `app.tsx`\n```typescript jsx\nimport { State } from 'watch-state'\nimport { Content } from './Content'\n\nconst show = new State(true)\n\nconst handleChange = (e: Event) =\u003e {\n  show.value = e.target.checked\n}\n\nexport default (\n  \u003c\u003e\n    \u003cshow when={show}\u003e\n      \u003cContent /\u003e\n    \u003c/show\u003e\n    \u003cinput\n      type=\"checkbox\"\n      checked\n      onchange={handleChange}\n    /\u003e\n  \u003c/\u003e\n)\n```\n\n## Context\n\nYou can pass a value from a parent element through any children to the place you need.\n\nChange `Content.tsx`\n```typescript jsx\nimport { Context, useContext } from '@innet/dom'\n\nexport const color = new Context('blue')\n\nexport function Content () {\n  const currentColor = useContext(color)\n\n  return (\n    \u003ch1 style={{ color: currentColor }}\u003e\n      {children}\n    \u003c/h1\u003e\n  )\n}\n```\n\nAnd change `app.tsx`\n```typescript jsx\nimport { Content, color } from './Content'\n\nexport default (\n  \u003c\u003e\n    \u003cContent\u003e\n      Without context\n    \u003c/Content\u003e\n    \u003ccontext for={color} set='red'\u003e\n      \u003cContent\u003e\n        With context\n      \u003c/Content\u003e\n    \u003c/context\u003e\n  \u003c/\u003e\n)\n```\n\n## show\n\nYou can use `show` element to show/hide content by state.\n\n```typescript jsx\nimport { State } from 'watch-state'\n\nconst show = new State(true)\n\nexport default (\n  \u003cshow when={show}\u003e\n    \u003cbutton\n      onclick={() =\u003e {\n        show.value = false\n      }}\u003e\n      Click Me\n    \u003c/button\u003e\n  \u003c/show\u003e\n)\n```\n\n\u003e `when` can be: `State` | `Cache` | `() =\u003e any` | `any`\n\n## hide\n\nYou can use `hide` element to show/hide content by state.\n\n```typescript jsx\nimport { State } from 'watch-state'\n\nconst isHidden = new State(false)\n\nexport default (\n  \u003chide when={isHidden}\u003e\n    \u003cbutton\n      onclick={() =\u003e {\n        isHidden.value = true\n      }}\u003e\n      Click Me\n    \u003c/button\u003e\n  \u003c/hide\u003e\n)\n```\n\n\u003e `when` can be: `State` | `Cache` | `() =\u003e any` | `any`\n\n## switch\n\nYou can use `switch` element to show a content by string state.\n\n```typescript jsx\nimport { State } from 'watch-state'\n\nconst str = new State('')\n\nconst case1 = () =\u003e {\n  str.value = 'case1'\n}\n\nconst case2 = () =\u003e {\n  str.value = 'case2'\n}\n\nexport default (\n  \u003cswitch of={str}\u003e\n    \u003cslot name='case1'\u003e\n      Case 1\n      \u003cbutton\n        onclick={case2}\u003e\n        Next\n      \u003c/button\u003e\n    \u003c/slot\u003e\n    \u003cslot name='case2'\u003e\n      Case 2\n    \u003c/slot\u003e\n    Default content\n    \u003cbutton\n      onclick={case1}\u003e\n      Next\n    \u003c/button\u003e\n  \u003c/switch\u003e\n)\n```\n\n\u003e `of` can be: `State\u003cstring | number\u003e` | `Cache\u003cstring | number\u003e` | `() =\u003e (string | number)` | `string | number`\n\n## map\n\nYou can use `map` method of an array to put view on data.\n```typescript jsx\nconst names = ['Mike', 'Alex', 'Dan']\n\nexport default (\n  \u003cul\u003e\n    {names.map(name =\u003e (\n      \u003cli\u003e\n        {name}\n      \u003c/li\u003e\n    ))}\n  \u003c/ul\u003e\n)\n```\n\nIt's ok for static data, but if you use a state, it's better to use `map` element.\n```typescript jsx\nimport { State } from 'watch-state'\nimport { useMapValue, useMapIndex } from '@innet/dom'\n\nconst names = new State(['Mike', 'Alex', 'Dan'])\n\nfunction User () {\n  const name = useMapValue()\n  const index = useMapIndex()\n  \n  return (\n    \u003cli\u003e\n      #{index}:\n      {name}\n    \u003c/li\u003e\n  )\n}\n\nexport default (\n  \u003cul\u003e\n    \u003cmap of={names}\u003e\n      \u003cUser /\u003e\n    \u003c/map\u003e\n  \u003c/ul\u003e\n)\n```\n\nUse `key` property to improve `DOM` changes when you use an array of objects with some uniq field, like id.\n\n```typescript jsx\nimport { State } from 'watch-state'\n\nconst names = new State([\n  { id: 1, text: 'test1' },\n  { id: 2, text: 'test2' },\n  { id: 3, text: 'test3' },\n])\n\nfunction User () {\n  const name = useMapValue()\n  const index = useMapIndex()\n\n  return (\n    \u003cli\u003e\n      #{index}:\n      {name}\n    \u003c/li\u003e\n  )\n}\n\nexport default (\n  \u003cul\u003e\n    \u003cmap of={names} key='id'\u003e\n      \u003cUser /\u003e\n    \u003c/map\u003e\n  \u003c/ul\u003e\n)\n```\n\n## slots\n\nYou can use slots to provide a couple of named child elements.\n```typescript jsx\nimport { useChildren } from '@innet/jsx'\n\nexport const Content = () =\u003e (\n  \u003cslots from={useChildren()}\u003e\n    \u003cdiv class='header'\u003e\n      \u003cslot name='header'\u003e\n        default header\n      \u003c/slot\u003e\n    \u003c/div\u003e\n    \u003cdiv class='content'\u003e\n      \u003cslot\u003e\n        default content\n      \u003c/slot\u003e\n    \u003c/div\u003e\n    \u003cdiv class='footer'\u003e\n      \u003cslot name='footer'\u003e\n        default footer\n      \u003c/slot\u003e\n    \u003c/div\u003e\n  \u003c/slots\u003e\n)\n```\n\n```typescript jsx\nexport default (\n  \u003cContent\u003e\n    \u003cslot\u003eCustom content\u003c/slot\u003e\n    \u003cslot name='header'\u003e\n      Custom header\n    \u003c/slot\u003e\n  \u003c/Content\u003e\n)\n```\n\nYou get `Custom header`, `Custom content` and `default footer`\n\n## useSlots\n\n`useSlots` is a way to get slots.\n```typescript jsx\nimport { useSlots } from '@innet/dom'\n\nexport const Content = () =\u003e {\n  const {\n    '': content,\n    header,\n    footer\n  } = useSlots()\n\n  return (\n    \u003c\u003e\n      \u003cshow when={header}\u003e\n        \u003cdiv class='header'\u003e\n          {header}\n        \u003c/div\u003e\n      \u003c/show\u003e\n      \u003cdiv class='content'\u003e\n        {content}\n      \u003c/div\u003e\n      \u003cshow when={footer}\u003e\n        \u003cdiv class='footer'\u003e\n          {footer}\n        \u003c/div\u003e\n      \u003c/show\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n\u003e Any slots without name or with name equals empty string and any content outside slots collect into empty string slot.\n\n```typescript jsx\nexport default (\n  \u003cContent\u003e\n    \u003cslot name='header'\u003e\n      Custom header\n    \u003c/slot\u003e\n    Custom content\n  \u003c/Content\u003e\n)\n```\n\nYou can use a couple of slots with the same name.\n```typescript jsx\nexport default (\n  \u003cContent\u003e\n    \u003cslot name='header'\u003e\n      Custom header1 \u003cbr /\u003e\n    \u003c/slot\u003e\n    \u003cslot name='header'\u003e\n      Custom header2\n    \u003c/slot\u003e\n    Custom content\n  \u003c/Content\u003e\n)\n```\n\n## router\nYou can render content by url.\n\n```typescript jsx\nexport const Content = () =\u003e (\n  \u003crouter\u003e\n    \u003cslot name='/'\u003e\n      Home page\n    \u003c/slot\u003e\n    \u003cslot name='settings'\u003e\n      Settings page\n    \u003c/slot\u003e\n    Not Found\n  \u003c/router\u003e\n)\n```\n\nThere are strong matching by default, so you can see\n\n`/` - Home page  \n`/settings` - Settings page  \n`/settings/test` - Not Found  \n`/any-other` - Not Found\n\nIf you want to show `Settings page` on `/settings/test`, use `ish` prop on router element\n```typescript jsx\nexport const Content = () =\u003e (\n  \u003crouter ish\u003e\n    \u003cslot name='/'\u003e\n      Home page\n    \u003c/slot\u003e\n    \u003cslot name='settings'\u003e\n      Settings page\n    \u003c/slot\u003e\n    Not Found\n  \u003c/router\u003e\n)\n```\n\nWhen you use a router, that is inside a slot of another router, the route checks the next peace of url path.\n```typescript jsx\nexport const Content = () =\u003e (\n  \u003crouter ish\u003e\n    \u003cslot name='/'\u003e\n      Home page\n    \u003c/slot\u003e\n    \u003cslot name='settings'\u003e\n      \u003crouter\u003e\n        \u003cslot name='main'\u003e\n          Main Settings\n        \u003c/slot\u003e\n        \u003cslot name='user'\u003e\n          User Settings\n        \u003c/slot\u003e\n        Settings\n      \u003c/router\u003e\n    \u003c/slot\u003e\n    Not Found\n  \u003c/router\u003e\n)\n```\n\n`/` - Home page  \n`/settings` - Settings  \n`/settings/main` - Main Settings  \n`/settings/user` - User Settings  \n`/settings/any-other` - Settings  \n`/any-other` - Not Found\n\nYou can use `search` prop to make router binds on query search params\n```typescript jsx\nexport const Content = () =\u003e (\n  \u003crouter search='modal'\u003e\n    \u003cslot name='login'\u003e\n      Login\n    \u003c/slot\u003e\n    \u003cslot name='logout'\u003e\n      Logout\n    \u003c/slot\u003e\n  \u003c/router\u003e\n)\n```\n\n`?modal=login` - Login  \n`/settings?modal=logout` - Logout  \n`/settings?user=1\u0026modal=logout` - Logout  \n`/any-other?any-params\u0026modal=any-other` - render nothing\n\n## useRoute\nYou can handle dynamic routes by `useRoute`.\n```typescript jsx\nconst Test = () =\u003e {\n  const route = useRoute()\n\n  return () =\u003e route.value\n}\n\nexport const Content = () =\u003e (\n  \u003crouter ish\u003e\n    \u003cslot name='/'\u003e\n      Home page: \u003cTest /\u003e\n    \u003c/slot\u003e\n    \u003cslot name='settings'\u003e\n      Settings: \u003cTest /\u003e\n    \u003c/slot\u003e\n    Other: \u003cTest /\u003e\n  \u003c/router\u003e\n)\n```\n\n`/` - Home page: /  \n`/settings` - Settings: /  \n`/settings/test` - Settings: test    \n`/any-other` - Other: any-other\n\n## a\nThe tag `a` has a couple of features.\n\n\u003e `rel=\"noopener noreferrer nofollow\"` and `target=\"_blank\"` are default for external links.\n\n### href\nIf `href` starts from `/`, `?` or `#` then the Link will use [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API).\n\n```typescript jsx\nexport const Content = () =\u003e (\n  \u003cdiv\u003e\n    \u003ca href=\"/\"\u003ehome\u003c/a\u003e\n    \u003ca href=\"/test\"\u003etest\u003c/a\u003e\n    \u003ca href=\"/home\"\u003eunknown\u003c/a\u003e\n    \u003ca href=\"?modal=test\"\u003emodal\u003c/a\u003e\n    \u003cdiv\u003e\n      \u003crouter\u003e\n        \u003cslot name='/'\u003e\n          Home Page\n        \u003c/slot\u003e\n        \u003cslot name='test'\u003e\n          Test Page\n        \u003c/slot\u003e\n        404\n      \u003c/router\u003e\n      \u003crouter search='modal'\u003e\n        \u003cslot name='test'\u003e\n          Test Modal\n        \u003c/slot\u003e\n      \u003c/router\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n)\n```\n\n### replace\nBy default, it pushes to history, but you may use `replace` to replace current history state.\n\n```typescript jsx\nexport const Content = () =\u003e (\n  \u003ca replace href=\"/\"\u003e\n    home\n  \u003c/a\u003e\n)\n```\n\n### class\nYou can add root or active link class\n\n```typescript jsx\nconst classes = {\n  root: 'link',\n  active: 'active',\n}\n\nexport const Content = () =\u003e (\n  \u003cdiv\u003e\n    \u003ca\n      href=\"/\"\n      class='only-root'\u003e\n      home\n    \u003c/a\u003e\n    \u003ca\n      href=\"/test\"\n      class={classes}\u003e\n      test\n    \u003c/a\u003e\n  \u003c/div\u003e\n)\n```\n\nYou can use all features from [html-classes](https://www.npmjs.com/package/html-classes) for the `class` prop.\n\n```typescript jsx\nconst classes = {\n  root: ['link1', 'link2', () =\u003e 'dynamic-class'],\n  active: { active: true },\n}\n\nexport const Content = () =\u003e (\n  \u003cdiv\u003e\n    \u003ca\n      href=\"/\"\n      class={() =\u003e 'dynamic-root'}\u003e\n      home\n    \u003c/a\u003e\n    \u003ca\n      href=\"/test\"\n      class={classes}\u003e\n      test\n    \u003c/a\u003e\n  \u003c/div\u003e\n)\n```\n\n### exact\nBy default, active class appends if URL starts with `href` prop value, but use `exact` to compare exactly.\n\n```typescript jsx\nconst classes = { root: 'link', active: 'active' }\n\nexport const Content = () =\u003e (\n  \u003cdiv\u003e\n    \u003ca\n      href=\"/\"\n      exact\n      classes={classes}\u003e\n      home\n    \u003c/a\u003e\n    \u003ca\n      href=\"/test\"\n      classes={classes}\u003e\n      test\n    \u003c/a\u003e\n  \u003c/div\u003e\n)\n```\n\n### scroll\nYou can use smooth scroll\n```css\nbody, html {\n  scroll-behavior: smooth;\n}\n```\nThe property of `scroll` says should we scroll on click and how.\n\n\u003e by default equals `before`\n\n```typescript jsx\nexport const Content = () =\u003e (\n  \u003cdiv\u003e\n    \u003ca href=\"/\" scroll='before'\u003e\n      home\n    \u003c/a\u003e\n    \u003ca href=\"/test\" scroll='after'\u003e\n      test\n    \u003c/a\u003e\n    \u003ca href=\"?modal\" scroll='none'\u003e\n      test\n    \u003c/a\u003e\n  \u003c/div\u003e\n)\n```\n\n### scrollTo\nIf you want to scroll the page to custom position (by default it's up of the page) use `scrollTo`\n\n```typescript jsx\nexport const Content = () =\u003e (\n  \u003cdiv\u003e\n    \u003ca href=\"/\" scrollTo={100}\u003e\n      home\n    \u003c/a\u003e\n    \u003ca href=\"/test\" scrollTo='#root'\u003e\n      test\n    \u003c/a\u003e\n  \u003c/div\u003e\n)\n```\n\nUse a string to scroll under an element relates to the CSS selector you provide or use `-1` to stop scrolling.\n\n## delay\nYou can show and hide elements with delay.\n\n```typescript jsx\nexport function Content () {\n  return (\n    \u003cdelay show={1000}\u003e\n      Works\n      \u003cdelay show={1000}\u003e\n        fine!\n      \u003c/delay\u003e\n    \u003c/delay\u003e\n  )\n}\n```\n\n### useHidden\nYou can react on removing of elements\n\nChange `Content.tsx`\n```typescript jsx\nimport { useHidden } from '@innet/dom'\n\nexport function Content () {\n  const hidden = useHidden()\n\n  return () =\u003e hidden.value ? 'hidden' : 'shown'\n}\n```\n\nAnd change `app.tsx`\n```typescript jsx\nimport { State } from 'watch-state'\n\nconst show = new State(true)\n\nconst handleClick = () =\u003e {\n  show.value = false\n}\n\nexport default () =\u003e show.value \u0026\u0026 (\n  \u003cdelay hide={1000}\u003e\n    \u003cContent /\u003e\n    \u003cbutton\n      onclick={handleClick}\u003e\n      Hide\n    \u003c/button\u003e\n  \u003c/delay\u003e\n)\n```\n\n### ref\nYou can use `ref` to get the hidden state.\n\nChange `Content.tsx`\n```typescript jsx\nexport function Content () {\n  const hidden = new Ref()\n\n  return (\n    \u003cdelay ref={hidden} hide={1000}\u003e\n      {() =\u003e hidden.value.value ? 'hidden' : 'shown'}\n    \u003c/delay\u003e\n  )\n}\n```\n\nAnd change `app.tsx`\n```typescript jsx\nimport { State } from 'watch-state'\n\nconst show = new State(true)\n\nconst handleClick = () =\u003e {\n  show.value = false\n}\n\nexport default () =\u003e show.value \u0026\u0026 (\n  \u003c\u003e\n    \u003cContent /\u003e\n    \u003cbutton\n      onclick={handleClick}\u003e\n      Hide\n    \u003c/button\u003e\n  \u003c/\u003e\n)\n```\n\n## useParent\n\nYou can get parent HTML element inside a component\n\n```typescript jsx\nimport { getParent } from '@innet/dom'\n\nexport function Content () {\n  console.log(useParent())\n}\n```\n\n## style\n\nYou can style components with `style` function.\nThe function returns `useStyle` hook.\nUse this hook inside a component to get [html-classes](https://www.npmjs.com/package/html-classes) features on `class` prop.\n\n```typescript jsx\nimport { style, Style } from '@innet/dom'\n\nimport styles from './Content.scss'\n// or you can use an object like\n// { root: '...', header: '...', content: '...' }\n\nconst useContentStyles = style(styles)\n\nexport interface ContentProps extends Style {}\n\nexport function Content (props: ContentProps) {\n  const styles = useContentStyles()\n\n  return (\n    \u003cdiv class={() =\u003e styles.root}\u003e\n      \u003cheader class={() =\u003e styles.header}\u003e\n        header\n      \u003c/header\u003e\n      \u003cmain class={() =\u003e styles.content}\u003e\n        content\n      \u003c/main\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\nThen you can use `class` prop to define classes.\n\n```typescript jsx\nimport { State } from 'watch-state'\n\nconst show = new State(true)\n\nconst handleClick = () =\u003e {\n  show.value = !show.value\n}\n\nexport default (\n  \u003c\u003e\n    \u003cContent\n      class={{\n        root: 'root',\n        header: ['header', 'another-class'],\n        content: [\n          'content',\n          () =\u003e show.value \u0026\u0026 'show'\n        ],\n      }}\n    /\u003e\n    \u003cbutton\n      onclick={handleClick}\u003e\n      Hide\n    \u003c/button\u003e\n  \u003c/\u003e\n)\n```\n\n## Issues\nIf you find a bug or have a suggestion, please file an issue on [GitHub](https://github.com/d8corp/innet-dom/issues).\n\n[![issues](https://img.shields.io/github/issues-raw/d8corp/innet-dom)](https://github.com/d8corp/innet-dom/issues)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd8corp%2Finnet-dom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fd8corp%2Finnet-dom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd8corp%2Finnet-dom/lists"}