{"id":27923331,"url":"https://github.com/lexriver/dome","last_synced_at":"2025-05-06T22:32:31.138Z","repository":{"id":57126868,"uuid":"320024691","full_name":"LexRiver/dome","owner":"LexRiver","description":null,"archived":false,"fork":false,"pushed_at":"2024-09-25T17:28:26.000Z","size":284,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-22T10:52:11.728Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LexRiver.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-12-09T17:06:00.000Z","updated_at":"2024-09-25T17:28:39.000Z","dependencies_parsed_at":"2025-05-06T22:32:18.175Z","dependency_job_id":null,"html_url":"https://github.com/LexRiver/dome","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LexRiver%2Fdome","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LexRiver%2Fdome/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LexRiver%2Fdome/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LexRiver%2Fdome/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LexRiver","download_url":"https://codeload.github.com/LexRiver/dome/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252779581,"owners_count":21802978,"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":"2025-05-06T22:32:07.828Z","updated_at":"2025-05-06T22:32:31.058Z","avatar_url":"https://github.com/LexRiver.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DOME\r\n\r\nReact-like library for manipulating DOM.\r\nThere is no virtual DOM in this library, so syntax like `\u003cdiv\u003etext\u003c/div\u003e` directly creates DOM element.\r\n\r\n# Install\r\n\r\n```\r\nnpm install @lexriver/dome\r\n```\r\n\r\n# Import\r\n\r\n```tsx\r\nimport { React, DomeComponent, DomeRouter, DomeManipulator, AnimatedText, AnimatedTable, AnimatedArray} from '@lexriver/dome'\r\n\r\n// optional: import observable data type\r\nimport { ObservableVariable, ObservableArray, ObservableMap, ObservableLocalStorageVariable, createObservable, checkIfObservable } from '@lexriver/dome' \r\n// more details: https://www.npmjs.com/package/@lexriver/observable\r\n\r\n// optional: import TypeEvent\r\nimport { TypeEvent } from '@lexriver/dome' \r\n// more details: https://www.npmjs.com/package/@lexriver/type-event\r\n\r\n// optional: import data types checker\r\nimport {DataTypes} from '@lexriver/dome'\r\n// more details: https://www.npmjs.com/package/@lexriver/data-types\r\n\r\n// optional: import Async methods\r\nimport {Async} from '@lexriver/dome'\r\n// more details: https://www.npmjs.com/package/@lexriver/async\r\n\r\n```\r\n\r\n## Creating component\r\n\r\n```tsx\r\ninterface Attrs{\r\n    counter:number\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        return \u003cdiv\u003ecounter={this.attrs.counter}\u003c/div\u003e\r\n    }\r\n    \r\n}\r\n```\r\n\r\nor functional component\r\n\r\n```tsx\r\nconst MyComponent = (attrs, children) =\u003e \u003cdiv\u003ecounter={attrs.counter}\u003c/div\u003e\r\n```\r\n\r\n## Mount component to DOM\r\n\r\n```typescript\r\ndocument.body.appendChild(\u003cMyComponent counter={100} /\u003e)\r\n```\r\n\r\n## Example of component with dynamic content load\r\n\r\n```tsx\r\ninterface Attrs{\r\n    text:string\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    protected refContainer!:HTMLDivElement // for reference to main div of component\r\n    render(){\r\n        return \u003cdiv ref={ref =\u003e this.refContainer = ref}\u003eloading...\u003c/div\u003e\r\n    }\r\n    async afterRender(){\r\n        this.scheduleUpdate() // update component right after the first render\r\n    }\r\n\r\n    async updateAsync(){\r\n        let remoteText = await fetch(...) // get remoteText from server\r\n        DomeManipulator.replaceAllChildrenAsync(this.refContainer, \u003c\u003etext={remoteText}\u003c/\u003e) // replace 'loading...' with text\r\n    }\r\n}\r\n```\r\n\r\nSpecial property `this.rootElement` can be used for reference to main element.\r\n\r\n```tsx\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        // div is rootElement for this component\r\n        return \u003cdiv\u003eloading...\u003c/div\u003e \r\n    }\r\n    async afterRender(){\r\n        this.scheduleUpdate()\r\n    }\r\n\r\n    async updateAsync(){\r\n        let remoteText = await fetch(...) // get remoteText from server\r\n        DomeManipulator.replaceAllChildrenAsync(this.rootElement, \u003c\u003etext={remoteText}\u003c/\u003e) // replace 'loading...' with text\r\n    }\r\n}\r\n```\r\n\r\nAttributes (properties) for component are not read only, so we can change them, but `this.scheduleUpdate()` should be called to re-render the component.\r\nKeep in mind that if parent component will be re-rendered then child component will be re-rendered also with attributes used in parent component.\r\n\r\n```tsx\r\ninterface Attrs{\r\n    text?:string\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        if(!this.attrs.text){\r\n            return \u003cdiv\u003eloading...\u003c/div\u003e\r\n        }\r\n        return \u003cdiv\u003etext={this.attrs.text}\u003c/div\u003e\r\n    }\r\n    async afterRender(){\r\n        this.attrs.text = await fetch(...)\r\n        this.scheduleUpdate() // scheduleUpdate executes `updateAsync()` method if it is defined else `render()` method\r\n    }\r\n}\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\nThere is no such thing as State of component.\r\nIt's recommended to use `Observable` attributes or the global state for application as a colleciton of observable variables.\r\n\r\n## Example of Observable attributes\r\n\r\nEvery time the observable attribute changes then the `updateAsync()` or `render()` method will be executed.\r\n\r\n```tsx\r\ninterface Attrs{\r\n    textO:ObservableVariable\u003cstring\u003e\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        return \u003cdiv\u003etext={this.attrs.textO.get()}\u003c/div\u003e\r\n    }\r\n    async afterRender(){\r\n        this.attrs.textO.set(await fetch(...)) // when fetch completes the component will be updated\r\n    }\r\n}\r\n\r\nlet myObservableStringO = new ObservableVariable\u003cstring\u003e('default value')\r\nsetTimeout(() =\u003e {\r\n    myObservableStrginO.set('another value')\r\n}, 3000)\r\n\r\ndocument.body.appendChild(\u003cMyComponent textO={myObservableStringO} /\u003e)\r\n```\r\n\r\nFor more details on `Observable` please visit: https://github.com/LexRiver/observable\r\n\r\n\r\n\u003cbr/\u003e\r\n\r\n## Example of auto-unsubscribe component from some update events on component unmount from DOM\r\n\r\n```tsx\r\nexport module GlobalState{\r\n    export const someStringO = new ObservableVariable\u003cstring\u003e('default text')\r\n    export const someEvent = new TypeEvent\u003c(counter:number)=\u003evoid\u003e()\r\n}\r\n\r\ninterface Attrs{\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        return \u003cdiv\u003etext={GlobalState.someStringO.get()}\u003c/div\u003e\r\n    }\r\n    async afterRender(){\r\n        GlobalState.someStringO.eventOnChange.subscribe((newValue) =\u003e {\r\n            if(DomeManipulator.isInDom(this.rootElement) == false) return {unsubscribe:true} // remove this listener when component unmounts from DOM\r\n            this.scheduleUpdate() // update component when someStringO changes\r\n        })\r\n        GlobalState.someEvent.subscribe((newValue:number) =\u003e {\r\n            if(DomeManipulator.isInDom(this.rootElement) == false) return {unsubscribe:true} // remove this listener when component unmounts from DOM\r\n            console.log('event in component')\r\n            this.scheduleUpdate() // update component when someEvent triggers\r\n        })\r\n    }\r\n}\r\n\r\ndocument.body.appendChild(\u003cMyComponent /\u003e)\r\n\r\nsetTimeout(() =\u003e {\r\n    GlobalState.someStringO.set('another text') // change the value forces the component to update\r\n}, 3000)\r\n\r\nsetTimeout(() =\u003e {\r\n    GlobalState.someEvent.triggerAsync(100) // trigger the event forces the component to update\r\n}, 5000)\r\n\r\nsetTimeout(() =\u003e {\r\n    DomeManipulator.removeAllChildrenAsync(document.body) // remove component from DOM forces to unsubscribe from events, so the next event will not be triggered\r\n}, 7000)\r\n\r\nsetTimeout(() =\u003e {\r\n    GlobalState.someEvent.triggerAsync(100) // trigger the event, but no listeners\r\n}, 10000)\r\n\r\n```\r\n\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\r\nThere are also features for animation, dynamically change css classes and router, please keep reading.\r\n\r\n# Setup your environment\r\n\r\n```\r\nnpm install --save-dev @babel/cli @babel/core @babel/preset-env @types/node awesome-typescript-loader babel-loader copy-webpack-plugin css-loader express file-loader html-webpack-plugin image-webpack-loader mini-css-extract-plugin node-sass rimraf sass sass-loader serve-handler style-loader terser-webpack-plugin tslib tslint typescript webpack webpack-cli webpack-dev-middleware webpack-dev-server\r\n```\r\n\r\n```\r\nnpm install @lexriver/dome regenerator-runtime\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\nadd file `webpack.config.common.js` to your root\r\n```javascript\r\n// webpack.config.common.js\r\nconst { resolve } = require('path');\r\n\r\nmodule.exports = {\r\n    compileJs: {\r\n        test: /\\.js$/,\r\n        //use: ['babel-loader', 'source-map-loader'],s\r\n        use: [\r\n            {\r\n                loader: 'babel-loader',\r\n                options: {\r\n                    rootMode: \"upward\",\r\n                }\r\n            }\r\n        ],\r\n    }\r\n    ,\r\n    compileTs: {\r\n        test: /\\.tsx?$/,\r\n        use: [\r\n            {loader: 'babel-loader'}, \r\n            {loader: 'awesome-typescript-loader'}\r\n        ],\r\n    }\r\n    ,\r\n    compileFonts: {\r\n        test: /\\.(woff(2)?|ttf|eot|otf|svg)(\\?v=\\d+\\.\\d+\\.\\d+)?$/,\r\n        use: [\r\n            {\r\n                loader: 'file-loader',\r\n                options: {\r\n                    name: '[name].[hash].[ext]',\r\n                    outputPath: 'fonts/'\r\n                }\r\n            }\r\n        ]\r\n    }\r\n    ,\r\n    compileImages: {\r\n        test: /\\.(jpe?g|png|gif|svg)$/i,\r\n        loaders: [\r\n            {loader: 'file-loader',\r\n            options: {\r\n                hash: 'sha512',\r\n                digest:'hex',\r\n                name:'img/[name].[hash].[ext]'\r\n            }\r\n        },\r\n            {\r\n                loader: 'image-webpack-loader',\r\n                options: {\r\n                    disable: true,\r\n                    mozjpeg: {\r\n                        progressive: true,\r\n                        quality: 90\r\n                    },\r\n                    // optipng.enabled: false will disable optipng\r\n                    optipng: {\r\n                        enabled: false,\r\n                    },\r\n                    pngquant: {\r\n                        quality: [0.80, 0.90], //Instructs pngquant to use the least amount of colors required to meet or exceed the max quality. If conversion results in quality below the min quality the image won't be saved.\r\n                                            // Min and max are numbers in range 0 (worst) to 1 (perfect), similar to JPEG.\r\n                        speed: 4\r\n                    },\r\n                    gifsicle: {\r\n                        interlaced: false,\r\n                    },\r\n                    // the webp option will enable WEBP\r\n                    // webp: {\r\n                    //     quality: 75\r\n                    // }\r\n                }\r\n            }\r\n            //'file-loader?hash=sha512\u0026digest=hex\u0026name=img/[name].[hash].[ext]',\r\n            //'image-webpack-loader?bypassOnDebug\u0026optipng.optimizationLevel=7\u0026gifsicle.interlaced=false',\r\n        ],\r\n    }\r\n}\r\n\r\n```\r\n\u003cbr/\u003e\r\nadd file `webpack.config.dev.js` to your root\r\n\r\n```javascript\r\n// webpack.config.dev.js\r\n// development config\r\n\r\nconst webpack = require('webpack');\r\nconst { resolve } = require('path');\r\nconst { CheckerPlugin } = require('awesome-typescript-loader');\r\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\r\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin')\r\n//const HappyPack = require('happypack')\r\n//const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')\r\nconst {compileJs, compileFonts, compileTs, compileImages} = require('./webpack.config.common')\r\nconst CopyPlugin = require('copy-webpack-plugin')\r\n\r\n/*\r\n\r\nnode-sass -- provides binding for Node.js to LibSass, a Sass compiler.\r\nsass-loader -- is a loader for Webpack for compiling SCSS/Sass files.\r\nstyle-loader -- injects our styles into our DOM.\r\ncss-loader -- interprets @import and @url() and resolves them.\r\nmini-css-extract-plugin -- extracts our CSS out of the JavaScript bundle into a separate file, essential for production builds.\r\n\r\n*/\r\n\r\n\r\nmodule.exports = {\r\n    mode: 'development',\r\n    entry: [\r\n        'webpack-dev-server/client?http://localhost:8181',// bundle the client for webpack-dev-server and connect to the provided endpoint\r\n        'webpack/hot/only-dev-server', // bundle the client for hot reloading, only- means to only hot reload for successful updates\r\n        'regenerator-runtime/runtime',\r\n        './src/website/App.tsx' // the entry point of our app\r\n    ],\r\n    output: {\r\n        path: resolve(__dirname, './webpack-out'), //The output directory as an absolute path.\r\n        publicPath: '/', //https://webpack.js.org/configuration/output/#outputpublicpath\r\n        filename: '[name].[hash].bundle.js' //This option determines the name of each output bundle. The bundle is written to the directory specified by the output.path option.\r\n    },    \r\n    devServer: {\r\n        hot: true, // enable HMR on the server\r\n        port: 8181,\r\n        historyApiFallback:true\r\n    },\r\n\r\n    resolve: {\r\n        extensions: ['.ts', '.tsx', '.js', '.jsx'],\r\n    },\r\n    //context: resolve(__dirname, './src/website'),\r\n    module: {\r\n        rules: [\r\n            compileJs,\r\n            compileTs,\r\n            compileFonts,\r\n            compileImages,\r\n            {\r\n                test: /\\.css$/,\r\n                use: [\r\n                    { loader:'style-loader'}, \r\n                    { loader: 'css-loader', options: { importLoaders: 1 } }\r\n                ],\r\n            },\r\n            {\r\n                test: /\\.m\\.s(a|c)ss$/,\r\n                use: [\r\n                    {loader: 'style-loader'},\r\n                    {loader: 'css-loader', options: {modules: {localIdentName: '[path][name]--[local]'}, sourceMap:true}},\r\n                    {loader: 'sass-loader', options: {sourceMap: true}}\r\n                ]\r\n            },\r\n            {\r\n                test: /\\.(scss|sass)$/,\r\n                exclude: /\\.m\\.s(a|c)ss$/,\r\n                use: [\r\n                    {loader: 'style-loader'},\r\n                    {loader: 'css-loader', options: { importLoaders: 1,  } },\r\n                    {loader: 'sass-loader', options: {sourceMap: true } },\r\n                ],\r\n            },\r\n        ],\r\n    },\r\n    plugins: [\r\n        new CheckerPlugin(),\r\n        //new HtmlWebpackPlugin({ template: 'index.html.ejs', }),\r\n        new HtmlWebpackPlugin({ template: './webpack-src/index.html', }),\r\n        //new HtmlWebpackPlugin(),\r\n        new webpack.HotModuleReplacementPlugin(), // enable HMR globally\r\n        new webpack.NamedModulesPlugin(), // prints more readable module names in the browser console on HMR updates\r\n        new MiniCssExtractPlugin({\r\n            filename: '[name].css',\r\n            chunkFilename: '[id].css'\r\n        }),\r\n\r\n        //new HardSourceWebpackPlugin(), // https://github.com/mzgoddard/hard-source-webpack-plugin\r\n        new CopyPlugin([\r\n            {from: './webpack-src/site.webmanifest', flatten:true},\r\n            {from: './webpack-src/*.png', flatten: true},\r\n            {from: './webpack-src/favicon.ico'}\r\n            //{from: './webpack-src/*.webmanifest'}\r\n        ])\r\n\r\n    ],\r\n}\r\n```\r\n\u003cbr/\u003e\r\n\r\nadd file `webpack.config.prod.js` to your root\r\n\r\n```javascript\r\n// webpack.config.prod.js\r\n// production config\r\nconst { resolve } = require('path');\r\nconst { CheckerPlugin } = require('awesome-typescript-loader');\r\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\r\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin')\r\nconst {compileJs, compileFonts, compileTs, compileImages} = require('./webpack.config.common')\r\n//const MinifyPlugin = require(\"babel-minify-webpack-plugin\");\r\nconst TerserPlugin = require('terser-webpack-plugin');\r\nconst CopyPlugin = require('copy-webpack-plugin')\r\n\r\n\r\nmodule.exports = {\r\n    mode: 'production',\r\n    entry: [\r\n        //'@babel/polyfill',\r\n        //'babel-polyfill',\r\n        //'core-js/stable',\r\n        'regenerator-runtime/runtime',\r\n        './src/website/App.tsx',\r\n    ],\r\n    output: {\r\n        filename: 'js/bundle.[hash].min.js',\r\n        chunkFilename: 'js/bundle.[name].[hash].min.js',\r\n        path: resolve(__dirname, './webpack-out/'),\r\n        //path: './webpack-out/',\r\n    },\r\n    devtool: 'source-map', // generates source-maps https://webpack.js.org/configuration/devtool/\r\n    resolve: {\r\n        extensions: ['.ts', '.tsx', '.js', '.jsx'],\r\n    },\r\n    //context: resolve(__dirname, './src/website'),\r\n    optimization: {\r\n        minimize: true,\r\n    },    \r\n    module: {\r\n        rules: [\r\n            {\r\n                test: /\\.js$/,\r\n                use: [\r\n                    {\r\n                        loader: 'babel-loader',\r\n                        options: {\r\n                            rootMode: \"upward\",\r\n                        }\r\n                    }\r\n                ],\r\n            }\r\n            ,\r\n            {\r\n                test: /\\.tsx?$/,\r\n                use: [\r\n                    {loader: 'babel-loader'}, \r\n                    {loader: 'awesome-typescript-loader'}\r\n                ],\r\n            }\r\n            ,\r\n            compileFonts,\r\n            compileImages,\r\n            {\r\n                test: /\\.css$/,\r\n                use: [\r\n                    { loader:'style-loader'}, \r\n                    { loader: 'css-loader' }\r\n                ],\r\n            },\r\n            {\r\n                test: /\\.m\\.s(a|c)ss$/,\r\n                use: [\r\n                    {loader: MiniCssExtractPlugin.loader, options: {sourceMap: false}},\r\n                    {loader: 'css-loader', options: {modules: {localIdentName:'[hash:base64:7]'}, sourceMap:false}},\r\n                    {loader: 'sass-loader', options: {sourceMap: false}}\r\n                ]\r\n            },\r\n            {\r\n                test: /\\.(scss|sass)$/,\r\n                exclude: /\\.m\\.s(a|c)ss$/,\r\n                use: [\r\n                    {loader: MiniCssExtractPlugin.loader, options: {sourceMap: false}},\r\n                    {loader: 'css-loader', options: {sourceMap: false} },\r\n                    {loader: 'sass-loader', options: {sourceMap: false } },\r\n                ],\r\n            },\r\n        ],\r\n    },\r\n    plugins: [\r\n        new CheckerPlugin(),\r\n        //new HtmlWebpackPlugin({ template: 'index.html.ejs', }),\r\n        //new HtmlWebpackPlugin({ template: '../../webpack-src/index.html', }),\r\n        new HtmlWebpackPlugin({template: './webpack-src/index.html'}),\r\n\r\n        new MiniCssExtractPlugin({\r\n            filename: 'css/[contenthash].css',\r\n            chunkFilename: 'css/[id].[contenthash].css',\r\n            //filename: 'css/[name].[hash].css',\r\n            //chunkFilename: 'css/[id].[hash].css'\r\n        }),\r\n        new CopyPlugin([\r\n            //{from: resolve(__dirname, './webpack-src/*.png'), to: resolve(__dirname, './webpack-out/')},\r\n            //{from: resolve(__dirname, './webpack-src/*.ico'), to: resolve(__dirname, './webpack-out/')},\r\n            {from: './webpack-src/site.webmanifest', flatten:true},\r\n            {from: './webpack-src/*.png', flatten: true},\r\n            {from: './webpack-src/favicon.ico'},\r\n            //{from: './webpack-src/*.webmanifest'}\r\n        ])\r\n\r\n    ],\r\n}\r\n\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\nYour `tsconfig.json` should be like this:\r\n\r\n```javascript\r\n    \"compilerOptions\": {\r\n      /* Basic Options */\r\n      \"target\": \"esnext\",\r\n      \"moduleResolution\": \"node\",\r\n      \"module\": \"esnext\",\r\n      \"jsx\": \"react\",\r\n      \"declaration\": true,                   /* Generates corresponding '.d.ts' file. */\r\n      // \"declarationMap\": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */\r\n      \"sourceMap\": true,                     /* Generates corresponding '.map' file. */\r\n      // \"outFile\": \"./\",                       /* Concatenate and emit output to single file. */\r\n      \"outDir\": \"./out\",                        /* Redirect output structure to the directory. */\r\n      //\"rootDir\": \"./\",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */\r\n      //\"rootDir\": \"./src\",\r\n      // \"composite\": true,                     /* Enable project compilation */\r\n      // \"removeComments\": true,                /* Do not emit comments to output. */\r\n      // \"noEmit\": true,                        /* Do not emit outputs. */\r\n      // \"importHelpers\": true,                 /* Import emit helpers from 'tslib'. */\r\n      // \"downlevelIteration\": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */\r\n      // \"isolatedModules\": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */\r\n  \r\n      /* Strict Type-Checking Options */\r\n      //\"strict\": false,\r\n      \"strict\": true,                           /* Enable all strict type-checking options. */\r\n      \"noImplicitAny\": false,                 /* Raise error on expressions and declarations with an implied 'any' type. */\r\n      // \"strictNullChecks\": true,              /* Enable strict null checks. */\r\n      // \"strictFunctionTypes\": true,           /* Enable strict checking of function types. */\r\n      // \"strictPropertyInitialization\": true,  /* Enable strict checking of property initialization in classes. */\r\n      // \"noImplicitThis\": true,                /* Raise error on 'this' expressions with an implied 'any' type. */\r\n      // \"alwaysStrict\": true,                  /* Parse in strict mode and emit \"use strict\" for each source file. */\r\n  \r\n      /* Additional Checks */\r\n      // \"noUnusedLocals\": true,                /* Report errors on unused locals. */\r\n      // \"noUnusedParameters\": true,            /* Report errors on unused parameters. */\r\n      // \"noImplicitReturns\": true,             /* Report error when not all code paths in function return a value. */\r\n      // \"noFallthroughCasesInSwitch\": true,    /* Report errors for fallthrough cases in switch statement. */\r\n  \r\n      /* Module Resolution Options */\r\n      // \"moduleResolution\": \"node\",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */\r\n      // \"baseUrl\": \"./\",                       /* Base directory to resolve non-absolute module names. */\r\n      // \"paths\": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */\r\n      // \"rootDirs\": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */\r\n      // \"typeRoots\": [],                       /* List of folders to include type definitions from. */\r\n      // \"types\": [],                           /* Type declaration files to be included in compilation. */\r\n      // \"allowSyntheticDefaultImports\": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */\r\n      \"esModuleInterop\": true,                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */\r\n      // \"preserveSymlinks\": true,              /* Do not resolve the real path of symlinks. */\r\n  \r\n      /* Source Map Options */\r\n      // \"sourceRoot\": \"\",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */\r\n      // \"mapRoot\": \"\",                         /* Specify the location where debugger should locate map files instead of generated locations. */\r\n      // \"inlineSourceMap\": true,               /* Emit a single file with source maps instead of having a separate file. */\r\n      // \"inlineSources\": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */\r\n  \r\n      /* Experimental Options */\r\n      \"experimentalDecorators\": true,        /* Enables experimental support for ES7 decorators. */\r\n      \"emitDecoratorMetadata\": true,         /* Enables experimental support for emitting type metadata for decorators. */\r\n    }\r\n  }\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\nadd `babel.config.js` to your root:\r\n```javascript\r\n// use babel.config.js to apply to imported packages also\r\nconsole.log('=== loading babel.config.js')\r\nmodule.exports = {\r\n    \"presets\": [\r\n        [\r\n            \"@babel/env\",{\r\n                //\"modules\" : false, //By setting modules to false, we are telling babel not to compile our module code. This will lead to babel preserving our existing es2015 import/export statements.\r\n                \"targets\": {\r\n                    \"browsers\": [\r\n                        \"cover 99.5%\" \r\n                    ]\r\n                }\r\n            }\r\n        ],\r\n  \r\n    ],\r\n    \"plugins\": [\r\n    ]\r\n} \r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\nadd `typings.d.ts` to your root:\r\n```javascript\r\ndeclare module \"*.module.css\";\r\ndeclare module \"*.module.scss\";\r\ndeclare module \"*.m.scss\";\r\ndeclare module \"*.png\"\r\ndeclare module \"*.jpg\"\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n\r\ncreate file `./src/website/App.tsx`\r\n```typescript\r\nimport { React, DomeRouter, DomeManipulator } from '@lexriver/dome'\r\nimport css from './App.m.scss'\r\n\r\nconst App = () =\u003e (\r\n    \u003cdiv id=\"app\" class={css.app}\u003e\r\n        hello world\r\n    \u003c/div\u003e\r\n)\r\ndocument.body.appendChild(\u003cApp /\u003e)\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\ncerate file `./src/website/App.m.scss`\r\n```css\r\n.app {\r\n    border: solid 1px green;\r\n}\r\n```\r\n\u003cbr/\u003e\r\n\r\nadd these scripts to your package.json:\r\n```json\r\n    \"scripts\": {\r\n        \"build\": \"npm run clean-webpack-out \u0026\u0026 webpack -p --config=webpack.config.prod.js\",\r\n        \"clean-webpack-out\": \"rimraf webpack-out/*\",\r\n        \"lint\": \"tslint './src/**/*.ts*' --format stylish --project . --force\",\r\n        \"start\": \"npm run start-dev\",\r\n        \"start-dev\": \"webpack-dev-server --config=webpack.config.dev.js\"\r\n    }\r\n\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\nSo when you run\r\n```\r\nnpm run start\r\n```\r\nthe development process will be started on localhost:8181\r\n\r\nand when your run\r\n```\r\nnpm run build\r\n```\r\nthe production website will be generated in ./webpack-out\r\n\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\r\n\r\n# API\r\n\r\n# DomeComponent\r\n\r\nCreate a custom component\r\n\r\n```tsx\r\ninterface Attrs{ // to add custom attributes to your component\r\n    id?:number\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        return \u003cdiv\u003eid={this.attrs.id}, children={this.children}\u003c/div\u003e\r\n    }\r\n}\r\n```\r\n\r\nThen use it like this \r\n```tsx\r\n    \u003cMyComponent id={100}\u003eText inside\u003c/MyComponent\u003e\r\n```\r\n\r\nThere are also internal attributes for any component:\r\n*    `ref?:(ref)=\u003evoid` - to take a reference to this component\r\n*    `onShowAnimation?:Animation` - animation for show element\r\n*    `onHideAnimation?:Animation` - animation for hide element\r\n\r\nAnd `Animation` type is\r\n\r\n```typescript\r\nexport interface Animation{\r\n    cssClassName:string\r\n    timeMs:number // time in milliseconds before removing cssClassName from element\r\n}\r\n```\r\n\r\nUse these attributes like so:\r\n```tsx\r\n    \u003cMyComponent id={100} ref={ref =\u003e myRef = ref} onShowAnimation={{cssClassName:css.animationShow, timeMs:300}} onHideAnimation={myHideAnimationObject} /\u003e\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## Style html elements\r\n\r\nInline styles:\r\n\r\n```tsx\r\n\u003cdiv style={{backgroundColor:'green', border: 'solid 1px red'}}\u003e\u003c/div\u003e\r\n```\r\n\r\nCss classes could be set by using `class` attribute or by aliases `className` and `cssClasses`\r\n\r\n```tsx\r\n\u003cdiv class='class1 class2'\u003e\u003c/div\u003e\r\n\u003cdiv className='class1 class2'\u003e\u003c/div\u003e //same\r\n\u003cdiv cssClasses='class1 class2'\u003e\u003c/div\u003e //same\r\n```\r\n\r\nInstead of string [`CssClass`](###cssClass-type) type can be used, for example:\r\n```tsx\r\n\u003cdiv class={['class1', 'class2']} /\u003e\r\n\u003cdiv class={{'class1':true, 'class2':myObservableBooleanO}}\r\n```\r\n\r\nPlease see `DomeManipulator.setCssClasses(..)` for more details.\r\n\r\n\u003cbr/\u003e\r\n\r\n## Events for html elements\r\n\r\nTo create an event use standart event names but in camel case:\r\n\r\n```tsx\r\n\u003cbutton onClick={(e) =\u003e {e.preventDefault(); console.log('click')}}\u003eclick me\u003c/button\u003e\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## Set inner html\r\n\r\nTo set inner html for element:\r\n```tsx\r\n\u003cdiv innerHtml={`\u003cstrong\u003ehi\u003c/strong\u003e`} /\u003e\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## Properties for DomeComponent\r\n\r\n## `rootElement:Element|HTMLElement`\r\n\r\nThis is a reference to root element like `\u003cdiv\u003e\u003c/div\u003e` that was used in first render.\r\nDo not use it with fragment `\u003c\u003e\u003c/\u003e` as a root element.\r\n\r\n## `attrs`\r\n\r\nContains all attributes for component including internal attributes (see above)\r\n\r\n## `children`\r\n\r\nContains all children inside component\r\n\r\n\u003cbr/\u003e\r\n\r\n## Methods for custom component\r\n\r\n## init()\r\n\r\nThis method is for overwrite. It will be executed before first render.\r\n\r\n```tsx\r\ninterface Attrs{ // to add custom attributes to component\r\n    id?:number\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        return \u003cdiv\u003eid={this.attrs.id}, children={this.children}\u003c/div\u003e\r\n    }\r\n\r\n    init(){\r\n        console.log('init!')\r\n    }\r\n}\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## render()\r\n\r\nThis method must be overwritten. It will be executed when component first rendered and also when updated if `updateAsync()` was not overwritten.\r\n\r\nThis method must return DOM element or elements.\r\n\r\nTo return a few elements fragment syntax `\u003c\u003e\u003c/\u003e` can be used.\r\n\r\n```tsx\r\ninterface Attrs{\r\n    id?:number\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        return \u003c\u003e\r\n            \u003cdiv\u003eid={this.attrs.id}\u003c/div\u003e\r\n            \u003cdiv\u003echildren={this.children}\u003c/div\u003e\r\n            \u003c/\u003e\r\n    }\r\n}\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## afterRender()\r\n\r\nThis method will be executed after first render.\r\n\r\n```tsx\r\ninterface Attrs{\r\n    id?:number\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        return \u003cdiv\u003eid={this.attrs.id}\u003c/div\u003e\r\n    }\r\n    afterRender(){\r\n        console.log('after render') \r\n    }\r\n}\r\n\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## updateAsync()\r\n\r\nThis method can be overwritten. By default this method will call `render()` method to update the component. And after that `afterUpdate()` will be executed.\r\n\r\nUse method `scheduleupdate()` to force component to re-render.\r\n\r\n```tsx\r\ninterface Attrs{\r\n    id?:number\r\n}\r\n\r\nexport class MyComponent extends DomeComponent\u003cAttrs\u003e{\r\n    render(){\r\n        return \u003cdiv\u003eid={this.attrs.id}\u003c/div\u003e\r\n    }\r\n    afterRender(){\r\n        setTimeout(() =\u003e {\r\n            this.attrs.id = 200\r\n            this.scheduleUpdate()\r\n        }, 3000)\r\n    }\r\n    async updateAsync(){\r\n        DomeManipulator.replaceAllChildrenAsync(this.rootElement, \u003c\u003eafter update: id={this.attrs.id}\u003c/\u003e) \r\n    }\r\n    afterUpdate(){\r\n        console.log('component updated!')\r\n    }\r\n}\r\n\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## scheduleUpdate()\r\n\r\nUse this method to force update the component. This method is not for overwrite.\r\nIt should be used inside component:\r\n\r\n```tsx\r\n    this.scheduleUpdate()\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## afterUpdate()\r\n\r\nThis method will be executed after component update, but not after first render.\r\nOverwrite this method to take effect.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\r\n# Animation\r\n\r\nTo add an animation for render or update component use attributes `onShowAnimation` and `onHideAnimation`.\r\n\r\nThese attributes uses an `Animation` type:\r\n\r\n```typescript\r\nexport interface Animation{\r\n    cssClassName:string // the name of css class to be applied to DOM element\r\n    timeMs:number // amount of milliseconds to wait before removing cssClassName from element\r\n}\r\n```\r\n\u003cbr/\u003e\r\n\r\n```css\r\n/* style.m.scss */\r\n@keyframes zoomIn {\r\n    from {\r\n        opacity: 0;\r\n        transform: scale3d(0.3, 0.3, 0.3);\r\n    }\r\n\r\n    50% {\r\n        opacity: 1;\r\n    }\r\n\r\n    80% {\r\n        transform: scale3d(1.05,1.05,1.05);\r\n    }\r\n}\r\n\r\n.zoomIn {\r\n    animation: zoomIn 400ms linear forwards;\r\n}\r\n```\r\n\u003cbr/\u003e\r\n\r\nAnd use it in `.tsx` file\r\n\r\n```tsx\r\nimport css from './style.m.scss'\r\n\r\n\u003cMyComponent onShowAnimation={{cssClassName:css.zoomIn, timeMs:400}} /\u003e\r\n```\r\n\r\n\r\nTo apply animation for list of items please see below.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\r\n# DomeManipulator\r\n\r\nDomeManipulator is a module for manipulating DOM.\r\n\r\n## hideElementAsync\r\n\r\n```typescript\r\nDomeManipulator.hideElementAsync(element: Element, animation?:Animation)\r\n```\r\n\r\nUse this method to temporarily hide the element\r\n\r\n```typescript\r\nawait DomeManipulator.hideElementAsync(myRef, myAnimationHide)\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## unhideElementAsync\r\n\r\n```typescript\r\nDomeManipulator.unhideElementAsync(element: Element, animation?:Animation)\r\n```\r\n\r\nUse this method to unhide element that was hidden by `.hideElementAsync(..)`\r\n\r\n```typescript\r\nawait DomeManipulator.unhideElementAsync(myRef, myAnimationShow)\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n\r\n## insertAsFirstChildAsync\r\n\r\n```typescript\r\nDomeManipulator.insertAsFirstChildAsync(elementToInsert: Element, parentElement: Element | DocumentFragment, animation?:Animation)\r\n```\r\n\r\nInsert element as a first child for container.\r\n\r\n\u003cbr/\u003e\r\n\r\n## insertBeforeAsync\r\n\r\n```typescript\r\nDomeManipulator.insertBeforeAsync(elementToInsert: Element, refElement: Element | null, parentElement: Element | DocumentFragment, animation?:Animation)\r\n```\r\n\r\nInsert element before another element.\r\n\r\n\u003cbr/\u003e\r\n\r\n\r\n## insertAfterAsync\r\n\r\n```typescript\r\nDomeManipulator.insertAfterAsync(elementToInsert: Element, refElement: Element | null | undefined, parentElement: Element | DocumentFragment, animation?:Animation)\r\n```\r\n\r\nInsert element after another element.\r\n\r\n\u003cbr/\u003e\r\n\r\n## insertByIndexAsync\r\n\r\n```typescript\r\nDomeManipulator.insertByIndexAsync(elementToInsert: Element, index: number, parentElement: Element | DocumentFragment, animation?:Animation)\r\n```\r\n\r\nInsert element after element with exact index in parent.\r\n\r\n\u003cbr/\u003e\r\n\r\n## replaceAsync\r\n\r\n```typescript\r\nDomeManipulator.replaceAsync(oldElement: Element, newElement: Element, animationHide?:Animation, animationShow?:Animation)\r\n```\r\n\r\nReplace one element with another element.\r\n\r\n\u003cbr/\u003e\r\n\r\n## removeElementAsync\r\n\r\n```typescript\r\nDomeManipulator.removeElementAsync(element: Element, animation?:Animation)\r\n```\r\n\r\nRemove element from DOM.\r\n\r\n\u003cbr/\u003e\r\n\r\n## forEachChildrenOf\r\n\r\n```typescript\r\nDomeManipulator.forEachChildrenOf(element:Element, action:(child:ChildNode)=\u003evoid)\r\n```\r\n\r\nDo some action for each child nodes of element.\r\n\r\n\u003cbr/\u003e\r\n\r\n## removeAllChildrenAsync\r\n\r\n```typescript\r\nDomeManipulator.removeAllChildrenAsync(element: Element, animation?:Animation)\r\n```\r\n\r\nRemove all children for element.\r\n\r\n\u003cbr/\u003e\r\n\r\n## appendChildAsync\r\n\r\n```typescript\r\nDomeManipulator.appendChildAsync(containerElement:Element, child:Element, animation?:Animation)\r\n```\r\n\r\nAppend child to container element.\r\n\r\n\u003cbr/\u003e\r\n\r\n## appendChildrenAsync\r\n\r\n```typescript\r\nDomeManipulator.appendChildrenAsync(containerElement:Element, children:Element | Element[] | DocumentFragment | Text | string | null | undefined, animation?:Animation)\r\n```\r\n\r\nAppend one or few children to container element.\r\n\r\n\u003cbr/\u003e\r\n\r\n## replaceAllChildrenAsync\r\n\r\n```typescript\r\n    replaceAllChildrenAsync(\r\n        containerElement: Element, \r\n        childrenToInsert: Element | Element[] | DocumentFragment | Text | string | null | undefined, \r\n        animationForHide?:Animation, \r\n        animationForShow?:Animation\r\n    )\r\n``` \r\n\r\nReplace all children for container element.\r\n\r\n\u003cbr/\u003e\r\n\r\n## isInDom\r\n\r\n```typescript\r\nDomeManipulator.isInDom(el: Element | undefined)\r\n```\r\n\r\nCheck if element is in DOM. Uses `document.body.contains(el)` internally, so it could be not so fast.\r\n\r\n\u003cbr/\u003e\r\n\r\n## isOnScreen\r\n\r\n```typescript\r\nDomeManipulator.isOnScreen(el: ELement | undefined)\r\n```\r\n\r\nCheck if element is on screen now.\r\n\r\n\u003cbr/\u003e\r\n\r\n## addCssClassAsync\r\n\r\n```typescript\r\nDomeManipulator.addCssClassAsync(element: Element, cssClassName: string, removeAfterMs?:number)\r\n```\r\n\r\nAdd css class to element and remove it after `removeAfterMs` milliseconds if provided.\r\n\r\n\u003cbr/\u003e\r\n\r\n## addCssClassesAsync\r\n\r\n```typescript\r\nDomeManipulator.addCssClassesAsync(element: Element, cssClassNames: string[], removeAfterMs?:number)\r\n```\r\n\r\nAdd few css classes to element and remove them after `removeAfterMs` milliseconds if provided.\r\n\r\n\u003cbr/\u003e\r\n\r\n## removeCssClass\r\n\r\n```typescript\r\nDomeManipulator.removeCssClass(element: Element, cssClassName: string)\r\n```\r\n\r\nRemove css class from element.\r\n\r\n\u003cbr/\u003e\r\n\r\n## removeCssClasses\r\n\r\n```typescript\r\nDomeManipulator.removeCssClasses(element: Element, cssClassNames: string[])\r\n```\r\n\r\nRemove few css classes from element.\r\n\r\n\u003cbr/\u003e \r\n\r\n## removeCssClasses\r\n\r\n```typescript\r\nDomeManipulator.setCssClasses(element: Element, value: CssClass)\r\n```\r\n\r\nReplace css classes for element.\r\n\r\n## `CssClass` type\r\n\r\n```typescript\r\nexport type CssClass = {[key:string]:boolean|ObservableVariable\u003cboolean\u003e} | string[] | string\r\n```\r\n\r\nWhere value is an array of strings:\r\n\r\n```typescript\r\n['class1', 'class2]\r\n```\r\n\r\nor object {'className':isVisible}\r\n\r\n```typescript\r\n{'class1':true, 'class2':false}\r\n```\r\n\r\nor object with [`ObservableVariable\u003cboolean\u003e`](https://github.com/LexRiver/observable) as values \r\n\r\n```typescript\r\n{'class1':myBooleanO, 'class2':false}\r\n```\r\n\r\nor string with space separated css class list\r\n\r\n```typescript\r\n'class1 class2'\r\n```\r\n\r\nexample\r\n\r\n```typescript\r\nDomeManipulator.setCssClasses(myDiv, {'class1':true, 'class2':myObsVariableO})\r\nDomeManipulator.setCssClasses(myDiv, 'class1 class2')\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## setAttribute\r\n\r\n```typescript\r\nDomeManipulator.setAttribute(element: Element, name: string, value: any)\r\n```\r\n\r\nChange attribute for html element\r\n\r\n```typescript\r\nDomeManipulator.setAttribute(myDiv, 'data-id', 100)\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## hasFocus\r\n\r\n```typescript\r\nDomeManipulator.hasFocus(el: Element):boolean\r\n```\r\n\r\nCheck if element has focus\r\n\r\n```typescript\r\nDomeManipulator.hasFocus(myDiv) // boolean\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## scrollIntoView\r\n\r\n```typescript\r\nDomeManipulator.scrollIntoView(element: Element, paddingFromTop:number = 100)\r\n```\r\n\r\nSmooth scroll element into view with some padding from top of the screen\r\n\r\n```typescript\r\nDomeManipulator.scrollIntoView(myDiv)\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## scrollToTop\r\n\r\n```typescript\r\nDomeManipulator.scrollToTop()\r\n```\r\n\r\nSmooth scroll to top\r\n\r\n```typescript\r\nDomeManipulator.scrollToTop()\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## getCurrentScrollPosition\r\n\r\n```typescript\r\nDomeManipulator.getCurrentScrollPosition()\r\n```\r\n\r\nReturn current Y-coordinate of scroll, pixels from top.\r\n\r\n\u003cbr/\u003e\r\n\r\n## async scrollToAsync\r\n```typescript\r\nawait DomeManipulator.scrollToAsync(p:{\r\n        pxFromTop?:number, \r\n        pxFromLeft?:number,\r\n        smooth?:boolean,\r\n        msStep?:number, \r\n        maxMsToWait?:number\r\n    })\r\n```\r\n\r\nWait for `document.body.clientHeight` or `document.body.clientWidth` to be enough to scroll to `pxFromTop` or `pxFromLeft` and then scroll to that position.\r\n\r\nParameters\r\n\r\n* `pxFromTop?:number` : amount of pixels from top to scroll to\r\n* `pxFromLeft?:number` : amount of pixels from left to scroll to\r\n* `smooth?:boolean` : true for smooth scroll. Default value is \u003cstrong\u003efalse\u003c/strong\u003e.\r\n* `msStep?:number` : check if scroll is possible at each milliseconds step. Default value is \u003cstrong\u003e50\u003c/strong\u003e\r\n* `maxMsToWait?:number` : max amount of milliseconds to wait for to be able to scroll. Default value is \u003cstrong\u003e5000\u003c/strong\u003e\r\n\r\n```typescript\r\nDomeManipulator.scrollToY({\r\n    pxFromTop: 200,\r\n    smooth: true,\r\n    msStep: 100,\r\n    maxMsToWait: 20*1000\r\n})\r\n```\r\n\r\n\r\n\r\n\r\n\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\r\n# DomeRouter\r\n\r\nUse DomeRouter for navigation in single page app.\r\n\r\n```typescript\r\nDomeRouter.onRoute('/about', true, async (params, url) =\u003e {\r\n    const { PageAbout } = await import('./pages/PageAbout') // dynamic import component\r\n    DomeManipulator.replaceAllChildrenAsync(divContainer, \u003cPageAbout /\u003e, animationHide, animationShow) // replace current page with new page\r\n})\r\n```\r\n\r\nto navigate to another route without page reload use `DomeRouter.navigate('/url')`.\r\nHere is an example of `Link` component:\r\n\r\n```tsx\r\ninterface Attrs{\r\n    url:string|Promise\u003cstring\u003e\r\n    class?:string|{[key:string]:boolean}|{[key:string]:ObservableVariable\u003cboolean\u003e}\r\n    style?:{[key:string]:string|number}\r\n    newTab?:boolean\r\n}\r\n\r\nexport class Link extends DomeComponent\u003cAttrs\u003e{\r\n    refA!:HTMLAnchorElement\r\n    \r\n    render(){\r\n        return \u003ca ref={ref =\u003e this.refA = ref} {...this.attrs.style?{style:this.attrs.style}:null}\u003e{this.children}\u003c/a\u003e\r\n    }\r\n\r\n    async afterRender(){\r\n        if(this.attrs.class){\r\n            DomeManipulator.setCssClasses(this.refA, this.attrs.class)\r\n            //this.refA.classList.add(this.attrs.class)\r\n        }\r\n        if(this.attrs.newTab){\r\n            this.refA.setAttribute('target', '_blank')\r\n            this.refA.setAttribute('rel', 'noopener noreferrer')\r\n        }\r\n        const url = await this.attrs.url\r\n        this.refA.setAttribute('href', url)\r\n        if(!this.attrs.newTab){\r\n            this.refA.onclick = (e) =\u003e {\r\n                e.preventDefault()\r\n                DomeRouter.navigate(url)\r\n            }\r\n        }\r\n\r\n    }\r\n}\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## maxHistoryUrlsCount\r\n\r\n```typescript\r\nDomeRouter.maxHistoryUrlsCount\r\n```\r\n\r\nCount of urls to keep to go back in history. The default value is 10.\r\n\r\n```typescript\r\nDomeRouter.maxHistoryUrlsCount = 100\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## navigate\r\n\r\n```typescript\r\nDomeRouter.navigate(url:string)\r\n```\r\n\r\nNavigate to specific url\r\n\r\n```typescript\r\nDomeRouter.navigate('/login')\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## changeUrl\r\n\r\n```typescript\r\nDomeRouter.changeUrl(url:string)\r\n```\r\n\r\nJust change the url without navigating.\r\n\r\n```typescript\r\nDomeRouter.changeUrl('/fake-page')\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## getCurrentUrl\r\n\r\n```typescript\r\nDomeRouter.getCurrentUrl():string\r\n```\r\n\r\nGet current url pathname, like `/login`\r\n\r\n```typescript\r\nconsole.log('currentUrl=', DomeRouter.getCurrentUrl())\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## getPreviousPageUrl\r\n\r\n```typescript\r\nDomeRouter.getPreviousPageUrl(previousPageIndex:number=0):string|undefined\r\n```\r\n\r\nGet url of previous page by index where 0 is previous page, 1 is previous page minus 1, etc...\r\n\r\n```typescript\r\nconsole.log('previous url = ', DomeRouter.getPreviousPageUrl())\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## reloadCurrentPage\r\n\r\n```typescript\r\nDomeRouter.reloadCurrentPage(addToHistory:boolean = false)\r\n```\r\n\r\nReload current page\r\n\r\n\u003cbr/\u003e\r\n\r\n## resolveUrl\r\n\r\n```typescript\r\nDomeRouter.resolveUrl(url:string = window.location.pathname, addToHistory:boolean = true)\r\n```\r\n\r\nExecute action defined for `url` in `onRoute` method\r\n\r\n```typescript\r\nDomeRouter.resolveUrl('/about')\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## onRoute\r\n\r\n```typescript\r\nDomeRouter.onRoute(route:string, exactMatch:boolean, action:RouteAction)\r\n```\r\n\r\nAdd reaction on route change.\r\nParameters:\r\n* `route:string` must starts with '/', for example '/login' or '/'. To add a parameter add `:` before parameter name: `/product/:id`\r\n* `exactMatch:boolean` if true then will be triggered only if whole route matches the pattern\r\n* `action:RouteAction` is a method to execute if route matches\r\n\r\nWhere `RouteAction` type is:\r\n```typescript\r\nexport type RouteAction = (\r\n    params:{[key:string]:string}, // parameters from url\r\n    url:string, \r\n    scrollToPreviousPositionAsync:()=\u003ePromise\u003cvoid\u003e // this function can be called to scroll to previous page position\r\n    ) =\u003e void|Promise\u003cvoid\u003e\r\n```\r\n\r\nexample:\r\n```typescript\r\nDomeRouter.onRoute('/login', true, (params, url) =\u003e changePage(\u003cPageLogin /\u003e))\r\nDomeRouter.onRoute('/product/:productId', true, (params, url) =\u003e {\r\n    // for example for '/product/456' the output will be\r\n    // 'productId=', '456', string\r\n    console.log('productId=', params.productId, typeof params.productId) \r\n})\r\n```\r\n\r\nA type of parameter can be added, for example :\r\n```typescript\r\nDomeRouter.onRoute('/product/:productId\u003cnumber\u003e', true, (params, url) =\u003e {\r\n    // for example for '/product/456' the output will be\r\n    // 'productId=', 456, number\r\n    console.log('productId=', params.productId, typeof params.productId) \r\n})\r\n```\r\n\r\nA possible types are:\r\n* `int` - uses `parseInt(p)` internally\r\n* `float` - uses `parseFloat(p)` internally\r\n* `number` - uses `Number(p)` intrenally\r\nBut there is no validation for paramters, so the result of specified function will be returned.\r\n\r\n```typescript\r\nDomeRouter.onRoute('/product/:productId\u003cint\u003e', true, (params, url) =\u003e {\r\n    // for example for '/product/456' the output will be\r\n    // 'productId=', 456, number\r\n    console.log('productId=', params.productId, typeof params.productId) \r\n})\r\n```\r\n\r\n\u003cbr/\u003e\r\n\r\n## onNotFound\r\n\r\n```typescript\r\nDomeRouter.onNotFound(action:()=\u003evoid)\r\n```\r\n\r\nThis method will be executed if path doesn't match any of routes assigned by '.onRoute' methods\r\n\r\n```typescript\r\nDomeRouter.onNotFound(() =\u003e changePage(\u003cPage404 /\u003e))\r\n```\r\n\r\n\r\n\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\r\n# AnimatedText\r\n\r\n`AnimatedText` is a component for displaying text and when text is changing update the component with animation.\r\n\r\n```tsx\r\nimport { React, DomeComponent, AnimatedText } from '@lexriver/dome'\r\n\r\nlet myTextO = new ObservableVariable\u003cstring\u003e('default text')\r\n\r\n// later in render() method:\r\n\r\n\u003cAnimatedText \r\n    textO={myTextO} \r\n    onHideAnimation={{cssClassName:'hideAnimation', timeMs:300}} \r\n    onShowAnimation={{cssClassName:'showAnimation', timeMs:300}} \r\n/\u003e\r\n\r\n// then\r\nsetTimeout(() =\u003e {\r\n    myTextO.set('another text')\r\n}, 3000)\r\n```\r\n\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\r\n# Animated Array\r\n\r\n`AnimatedArray` class can be used to display list of items, that could be dynamically changed / added / removed with animation.\r\n\r\n```tsx\r\ninterface Attrs{\r\n}\r\n\r\nexport class AnimatedListTest extends DomeComponent\u003cAttrs\u003e{\r\n    currentArray:number[] = [100,101,102,103,104,105, 106, 107, 108, 109]\r\n    animatedArray = new AnimatedArray\u003cnumber\u003e({\r\n        animationHide:{cssClassName:cssAnimation.fadeOut1000, timeMs:1000},\r\n        animationShow:{cssClassName:cssAnimation.fadeIn1000, timeMs:1000},\r\n        array: [], // initial array\r\n        getKey: (x:number) =\u003e 'key'+x, // each element must have an unique key\r\n        getHtmlElement: (x:number) =\u003e { // render element\r\n            return \u003cdiv\u003ethe number is {x}\u003c/div\u003e\r\n        },\r\n        emptyList:\u003cdiv\u003eLoading\u003c/div\u003e\r\n    })\r\n\r\n    render(){\r\n        return \u003cdiv\u003e\u003c/div\u003e // container to assign this.rootElement\r\n    }\r\n\r\n    afterRender(){\r\n        let x = 200\r\n        \r\n        setInterval(() =\u003e { // dynamically update list every 2 seconds\r\n             this.currentArray.push(x++)\r\n             this.currentArray.splice(2,1)\r\n             this.animatedArray.update((this.currentArray), this.rootElement)\r\n        }, 2000)\r\n                \r\n        this.animatedArray.update(this.currentArray, this.rootElement)\r\n    }\r\n}\r\n```\r\n\r\n```scss\r\n// Animation.m.scss\r\n\r\n@keyframes fadeIn {\r\n    from {\r\n        opacity: 0;\r\n    }\r\n\r\n    to {\r\n        opacity: 1;\r\n    }\r\n}\r\n\r\n@keyframes fadeOut {\r\n    from {\r\n        opacity: 1;\r\n    }\r\n\r\n    to {\r\n        opacity: 0;\r\n    }\r\n}\r\n\r\n\r\n.fadeIn1000{\r\n    animation: fadeIn 1000ms linear forwards;\r\n}\r\n.fadeOut1000{\r\n    animation: fadeOut 1000ms linear forwards;\r\n}\r\n```\r\n\r\n\r\n\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\u003cbr/\u003e\r\n\r\n# AnimatedTable\r\n\r\nComponent `AnimatedTable` can be used to display html table where items could be dynamically changed.\r\n\r\n```tsx\r\ninterface Attrs{\r\n\r\n}\r\n\r\nexport class Pages extends DomeComponent\u003cAttrs\u003e{\r\n    refList?:HTMLDivElement\r\n    pagesO = new ObservableVariable\u003cJsonPage[]\u003e([])\r\n    isLoadingO = new ObservableVariable\u003cboolean\u003e(true)\r\n\r\n    render(){\r\n        return \u003cdiv ref={ref =\u003e this.refList = ref}\u003e\r\n\r\n                    \u003cAnimatedTable\u003cJsonPage\u003e \r\n                        animationShowRow={{cssClassName:cssAnimation.fadeIn300, timeMs:300}}\r\n                        animationHideRow={{cssClassName:cssAnimation.fadeOut300, timeMs:300}}\r\n                        animationHideTable={{cssClassName:cssAnimation.fadeOut300, timeMs:300}}\r\n                        animationShowTable={{cssClassName:cssAnimation.fadeIn300, timeMs:300}}\r\n                        animationHideEmptyList={{cssClassName:cssAnimation.fadeOut300, timeMs:300}}\r\n                        animationShowEmptyList={{cssClassName:cssAnimation.fadeIn300, timeMs:300}}\r\n                        //animationHideLoading={{cssClassName:cssAnimation.fadeOut300, timeMs:300}}\r\n                        //animationShowLoading={{cssClassName:cssAnimation.fadeIn300, timeMs:300}}\r\n                        renderTableHead={() =\u003e this.renderTableHead()}\r\n                        tableBody={\u003ctbody\u003e\u003c/tbody\u003e}\r\n                        tableElement={\u003ctable\u003e\u003c/table\u003e}\r\n                        getKey={(page:JsonPage) =\u003e 'key'+page.id}\r\n                        renderTableRow={(page:JsonPage) =\u003e this.renderTableRow(page)}\r\n                        itemsO={this.pagesO}\r\n                        isLoadingO={this.isLoadingO}\r\n                        renderEmptyList={()=\u003e\u003cspan\u003e\u003c/span\u003e}\r\n                        renderLoading={()=\u003e\u003cdiv\u003eloading...\u003c/div\u003e}\r\n                    /\u003e\r\n\r\n                \u003c/div\u003e\r\n    }\r\n\r\n    afterRender(){\r\n        // console.log('afterRender()')\r\n        this.updatePagesAsync()\r\n    }\r\n\r\n    async updatePagesAsync(){\r\n        const pages:JsonPage[] = await Server.getAllPagesAsync()\r\n        //if(!this.refList) return\r\n\r\n        //DomeManipulator.replaceAllChildrenAsync(this.refList, this.renderCustomPages(pages))\r\n        this.pagesO.set(pages)\r\n        this.isLoadingO.set(false)\r\n    }\r\n\r\n    renderTableHead() {\r\n        return \u003cthead\u003e\r\n            \u003ctr\u003e\r\n                \u003cth\u003e\u003c/th\u003e\r\n                \u003cth\u003e\u003cstrong\u003eName\u003c/strong\u003e\u003c/th\u003e\r\n                \u003cth\u003e\u003cstrong\u003eURL\u003c/strong\u003e\u003c/th\u003e\r\n            \u003c/tr\u003e\r\n        \u003c/thead\u003e\r\n    }\r\n\r\n    renderTableRow(page: JsonPage) {\r\n        return \u003ctr\u003e\r\n            \u003ctd\u003e\r\n                \u003cdiv\u003e\r\n                    \r\n                    \u003cbutton onClick={() =\u003e {\r\n                        DomeRouter.navigate( /* get page url to edit (page.id) */)\r\n                    }} /\u003e\r\n                    \u003cbutton onClick={async () =\u003e {\r\n                        try {\r\n                            // delete page code...\r\n                            // update\r\n                            this.updatePagesAsync()\r\n                \r\n                        } catch(x){\r\n                            console.error(x)\r\n                        }\r\n\r\n                    }} /\u003e\r\n                \u003c/div\u003e\r\n            \u003c/td\u003e\r\n            \u003ctd\u003e\r\n                \u003cdiv\u003e{page.name}\u003c/div\u003e\r\n                \u003cdiv\u003e{page.title}\u003c/div\u003e\r\n            \u003c/td\u003e\r\n            \u003ctd\u003e\r\n                \u003cLink url={`${page.url}`} newTab={true}\u003e{page.url}\u003c/Link\u003e\r\n            \u003c/td\u003e\r\n        \u003c/tr\u003e\r\n    }\r\n\r\n}\r\n\r\n```\r\n\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flexriver%2Fdome","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flexriver%2Fdome","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flexriver%2Fdome/lists"}