1.2.3 • Published 5 years ago

css3transform v1.2.3

Weekly downloads
286
License
MIT
Repository
github
Last release
5 years ago

English | 简体中文

Quick Preview

Pass data through the component tree without having to pass props down manually at every level by store, auto update the view on demand.

import { define, render } from 'omi'

class Store {
  data = {
    count: 1
  }
  sub = () => {
    this.data.count--
  }
  add = () => {
    this.data.count++
  }
}

define('my-counter', _ => (
  <div>
    <button onClick={_.store.sub}>-</button>
    <span>{_.store.data.count}</span>
    <button onClick={_.store.add}>+</button>
  </div>
), {
    use: ['count'], 
    //or using useSelf, useSelf will update self only, exclude children components
    //useSelf: ['count'], 
    css: `span { color: red; }`,
    installed() {
      console.log('installed')
    }
  })

render(<my-counter />, 'body', new Store)
  • <my-counter></my-counter> can be used in any framework or no framework, such as document.createElement('my-counter')

You can also use useSelf, useSelf only updates itself. When using useSelf, the corresponding attributes are accessed through usingSelf in JSX.

You can also implement computed props through compute, such as:

define('my-counter', _ => (
  <div>
    <button onClick={_.store.sub}>-</button>
    <span>{_.store.data.count}</span>
    <button onClick={_.store.add}>+</button>
    <div>Double: {_.computed.doubleCount}</div>
  </div>
), {
    use: ['count'],
    compute: {
      doubleCount() {
        return this.count * 2
      }
    }
  })

Path is also supported:

class Store {
  data = {
    list: [
      { name: { first: 'dnt', last: 'zhang' } }
    ]
  }
}

...
...

define('my-counter', _ => (
  ...
  ...
), {
    use: [
      'list[0].name', //Direct string path dep, accessible through this.using[0] 
    ],
    compute: {
      fullName() {
        return this.list[0].name.first + this.list[0].name.last
      }
    }
  })

npm.io

Multi-store injection

import { define, render } from 'omi'

define('my-app', _ => {
  const store = _.store.storeA
  const { data, add, sub } = store
  return (
    <p>
      Clicked: {data.count} times
      <button onClick={add}>+</button>
      <button onClick={sub}>-</button>

      <div>
        {_.store.storeB.data.msg}
        <button  onClick={_.store.storeB.changeMsg}>
          change storeB's msg
        </button>
      </div>
    </p>
  )
}, {
  useSelf: {
    storeA: ['count', 'adding'],
    storeB: ['msg']
  }
})

const storeA = new class Store {
  data = {
    count: 0,
    adding: false
  }
  sub = () => {
    this.data.count--
  }
  add = () => {
    this.data.count++
  }
}

const storeB = new class Store {
  data = {
    msg: 'abc'
  }
  changeMsg = () => {
    this.data.msg = 'bcd'
  }
}

render( <my-app /> , 'body', {
  storeA,
  storeB
})

How to Multi-store injection with compute and computed? Very simple:

define('my-app', _ => {
  const store = _.store.storeA
  const { data, add, sub } = store
  return (
    <p>
      Clicked: {data.count} times
      <button onClick={add}>+</button>
      <button onClick={sub}>-</button>

      <div>
        {_.store.storeB.data.msg}
        <button onClick={_.store.storeB.changeMsg}>
          change storeB's msg
        </button>
      </div>

      <div>{_.computed.dobuleCount}</div>
      <div>{_.computed.reverseMsg}</div>
    </p>
  )
}, {
    useSelf: {
      storeA: ['count', 'adding'],
      storeB: ['msg']
    },
    compute: {
      dobuleCount() {
        return this.storeA.data.count * 2
      },
      reverseMsg() {
        return this.storeB.data.msg.split('').reverse().join('')
      }
    }
  })

API and Hooks

define('my-component', _ => (
  ...
  ...
), {
    use: ['count', 'path.a', 'path[1].b'],
    useSelf: ['path.c', 'path[1].d'],
    compute: {
      doubleCount() {
        return this.count * 2
      }
    },
    css: 'h1 { color: red; }',
    propTypes: {

    },
    defaultProps: {

    },

    //life cycle
    install() { }, 
    installed() { }, 
    uninstall() { }, 
    receiveProps() { },
    beforeUpdate() { }, 
    updated() { }, 
    beforeRender() { }, 
    rendered() { }, 

    //custom methods
    myMethodA() { },
    myMethodB() { }

  })

Ecosystem of Omi

Base

ProjectDescription
omi-docs and codepen and webcomponents.devOmi official documents
omixnpm.io小程序全局状态管理框架,数据触手可及,状态无处遁形
omimnpm.ioCross frameworks and themes components.(DOCS & REPL && JOIN US!)
omi-kbonenpm.io 使用 omi + kbone 多端开发(小程序和Web)的贪吃蛇游戏。
omioOmi for old browsers with same api(IE8+)
omisnpm.io Omis + React
omivnpm.io Omiv + Vue
omi-ssrServer-side rendering(support omio only)
omi-routerOmi official router in 1KB js
omi-cliProject scaffolding. → Base Templates and → Other Templates
omi-devtoolsBrowser DevTools extension
omiuSimple Omi UI
omilWebpack loader for Omi.js components.(DOCS)
omi-snippetsA beautify VSCode extension for .omi or .eno file, Install now!
obaa or JSONPatcherProxyObserve or Proxy any object's any change

Snake MVP

ProjectDescription
omi-snakenpm.io & → Touch the demoThe Snake-Eating Game Based on MVP Architecture Written by Omi
omi-kbone-snakenpm.io omi-kbone 写的 MVP 架构的跨端贪吃蛇游戏,支持小程序和 H5
Preact-snake & → Touch the demoThe Snake-Eating Game Based on MVP Architecture Written by Preact + Preact-CSS + Omis
[P]react-snake & → Touch the demoThe Snake-Eating Game Based on MVP Architecture Written by React/Preact
vue-snakeThe Snake-Eating Game Based on MVP Architecture Written by Vue + Omiv
omix-snakenpm.ioThe Snake-Eating Game Based on MVP Architecture Written by Omix

Mini Program(小程序)

ProjectDescription
omixnpm.io小程序全局状态管理框架,数据触手可及,状态无处遁形
react-kbonenpm.io 直接使用 React 开发小程序或 Web,基于 kbone
preact-kbonenpm.io 直接使用 Preact 开发小程序或 Web,基于 kbone
omi-cloud小程序•云开发
omip直接使用 Omi 开发小程序或 H5 SPA
mps原生小程序增强框架(JSX + Less 输出 WXML + WXSS),也支持 QQ 轻应用
cax小程序 Canvas 和 SVG 渲染引擎
omi-mp通过微信小程序开发和生成 Web 单页应用(H5 SPA)
westore小程序状态管理
comi小程序代码高亮和 markdown 渲染组件
wx-touch-event基于 AlloyFinger 改造的小程序手势解决方案

Other

ProjectDescription
omi-pianonpm.ioBuild piano with Omi and Omi Snippets, Enjoy now!
omi-chartSimple HTML5 Charts using chart-x tag.
md2siteStatic Site Generator with markdown powered by Omio.
omi-30-secondsUseful Omi snippets that you can understand in 30 seconds.
omi-canvasPerfect fusion of web components, jsx and canvas.
omi-swiperOmi + Swiper
omi-vscodeVSCode extension for omi, Install now!
omi-exOmi.js extension(TypeScript)
omi-transformOmi / css3transform integration. Made css3 transform super easy in your Omi project.
omi-fingerSupport touch and gesture events in your Omi project.
omi-touchSmooth scrolling, rotation, pull to refresh and any motion for the web.
omi-nativeRender web components to native
omi-i18nInternationalization solution for omi.js using i18next ecosystem
omi-pageTiny client-side router by page
omieBuild cross platform desktop apps with Omi.js and Electron.js
omi-cvCreate a front-end engineer curriculum vitae, Get Started!
SooHas same API as omi but is great alternative if you want to create custom elements without JSX, virtual DOM and store
CEEFork from custom-elements-everywhere

Why Omi?

  • Tiny size and High performance
  • Cross frameworks(react, preact, vue, angular), components of omi are pure custom elements
  • One framework. Mobile & desktop & mini program
  • Stateless View Architecture Design
  • Be friendly to custom elements, you can pass false attributes to elements through string '0' or string 'flase', you can pass object attributes to elements through : prefix and Omi.$
  • Easy two way binding by extend api
  • Supports TypeScript
  • Reactive data-binding
  • Native tap event support
  • Having Cross-frameworks UI components - omim
  • Excellent compatibility(IE8+) with omio
  • Enhanced CSS, rpx unit support base on 750 screen width
  • Compliance with browser trend and API design
  • Merge Web Components, JSX into one framework
  • Web Components can also be a data-driven view, UI = fn(data).
  • JSX is the best development experience (code intelligent completion and tip) UI Expression with least grammatical noise and it's turing complete(template engine is not, es template string is but grammatical noise is too loud)
  • Look at Facebook React vs Web Components,Omi combines their advantages and gives developers the freedom to choose the way they like
  • Shadow DOM merges with Virtual DOM, Omi uses both virtual DOM and real Shadow DOM to make view updates more accurate and faster
  • Scoped CSS's best solution is Shadow DOM, the community churning out frameworks and libraries for Scoped CSS (using JS or JSON writing styles such as Radium, jsxstyle, react-style; binding to webpack using generated unique className filename-classname-hash, such as CSS Modules, Vue), are hack technologies; and Shadow DOM Style is the perfect solution.
  • The original Path Updating store system. Proxy-based automatic accurate update, low power consumption, high degree of freedom, excellent performance, easy integration of requestIdleCallback,It will automatically update UI partially when data is changed

Compare TodoApp by Omi and React, Omi and React rendering DOM structure:

OmiReactOmio
OmiReactOmio

Omi uses Shadow DOM based style isolation and semantic structure.

Useful Resources

Title NameOther languageRelated
Web Components bookmarks
Snake-Eating Game Making with Web Components of Omi and MVP Architecture
Constructable Stylesheets: seamless reusable styles
Web Components specifications
Web Components in a Nutshell
Using Web Components with React in 2019
Using Web Components in React
Styling We Components Using A Shared Style Sheet
Developer Tools support for Web Components in Firefox 63
Develop W3C Web Components with WebAssembly
60FPS Animation In Omi简体中文 한국어
Render Web Components To Native简体中文 한국어
Shadow Dom In Depth简体中文
Part Theme Explainer求翻译
Web Components MDN简体中文
Web Components Google
Web Components Org
Web Components: the Right Way
Proxy MDN简体中文 한국어
CSS Variables简体中文 한국어
CSS Shadow Parts
Platform HTML5
Using requestIdleCallback简体中文A polyfill
The Power Of Web Components简体中文
ShadowRoot简体中文

Overview of the Readme

Add Omi in One Minute

This page demonstrates using Omi with no build tooling, directly run in the browser.

<!DOCTYPE html>
<html>

<head>
  <title>Omi demo without transpiler</title>
</head>

<body>
  <script src="https://tencent.github.io/omi/packages/omi/dist/omi.js"></script>
  <script>
    const { define, render, html } = Omi

    class Store {
      data = {
        count: 1
      }
      sub = () => {
        this.data.count--
      }
      add = () => {
        this.data.count++
      }
    }

    define('my-counter', _ => html`
      <div>
        <button onClick=${_.store.sub}>-</button>
        <span>${_.store.data.count}</span>
        <button onClick=${_.store.add}>+</button>
      </div>
    `, {
      use: ['count'],
      //or using useSelf, useSelf will update self only, exclude children components
      //useSelf: ['count'], 
      css: `span { color: red; }`,
      installed() {
        console.log('installed')
      }
    })

    render(html`<my-counter />`, 'body', new Store)
  </script>
</body>

</html>

Omi Store provides a way to pass data through the component tree without having to pass props down manually at every level, injected from the root component and shared across all subcomponents. It's very simple to use:

You can also use my-counter tag directly in HTML:

<body>
  <my-counter></my-counter>
</body>

Getting Started

Install

$ npm i omi-cli -g    # install cli
$ omi init my-app     # init project
$ cd my-app           
$ npm start           # develop
$ npm run build       # release

npx omi-cli init my-app is also supported(npm v5.2.0+).

Directory description:

├─ config
├─ public
├─ scripts
├─ src
│  ├─ assets
│  ├─ elements    //Store all custom elements
│  ├─ store       //Store all this store of pages
│  ├─ admin.js    //Entry js of compiler,will build to admin.html
│  └─ index.js    //Entry js of compiler,will build to index.html

Scripts

"scripts": {
    "start": "node scripts/start.js",
    "build": "PUBLIC_URL=. node scripts/build.js",
    "build-windows": "set PUBLIC_URL=.&& node scripts/build.js",
    "fix": "eslint src --fix"
}

You can set up the PUBLIC_URL, such as:

...
"build": "PUBLIC_URL=https://fe.wxpay.oa.com/dv node scripts/build.js",
"build-windows": "set PUBLIC_URL=https://fe.wxpay.oa.com/dv&& node scripts/build.js",
...

Switch omi, omio and reomi

Add or remove the alias config in package.json to switch omi and omio:

"alias": {
  "omi": "omio"
}

Project Template

Template TypeCommandDescribe
Base Template(v3.3.0+)omi init my-appBasic omi or omio(IE8+) project template.
Base Template with snapshootomi init-snap my-appBasic omi or omio(IE8+) project template with snapshoot prerendering.
TypeScript Template(omi-cli v3.3.0+)omi init-ts my-appBasic template with typescript.
Mobile Templateomi init-weui my-appMobile web app template with weui and omi-router.
Kbone Templateomi init-kbone my-appDeveloping mini program or web using omi.

Hello Element

Define a custom element by extending WeElement base class:

import { define, WeElement } from 'omi'

define('hello-element', class extends WeElement {
  onClick = evt => {
    // trigger CustomEvent
    this.fire('abc', { name: 'dntzhang', age: 12 })
    evt.stopPropagation()
  }

  //If you need to use <hello-element></hello-element> directly in html, you must declare propTypes
  static propTypes = {
    msg: String
  }

  static css = `
      div {
        color: red;
        cursor: pointer;
      }`

  render(props) {
    return (
      <div onClick={this.onClick}>
        Hello {props.msg}
        <div>Click Me!</div>
      </div>
    )
  }
})

Using hello-element:

import { define, render, WeElement } from 'omi'
import './hello-element'

define('my-app', class extends WeElement {
  data = { abc: 'abc' }

  // define CustomEvent Handler
  onAbc = evt => {
    // get evt data by evt.detail
    this.data.abc = ' by ' + evt.detail.name
    this.update()
  }

  static css = `
      div{
          color: green;
      }`
  }

  render(props, data) {
    return (
      <div>
        Hello {data.abc}
        <hello-element
          onAbc={this.onAbc}
          msg="WeElement"
        />
      </div>
    )
  }
})

render(<my-app name="Omi v4.0" />, 'body')

Tell Babel to transform JSX into Omi.h() call:

{
  "presets": ["env", "omi"]
}

The following two NPM packages need to be installed to support the above configuration:

"babel-preset-env": "^1.6.0",
"babel-preset-omi": "^0.1.1",

If you use babel7, you can also use the following packages and configuration:

npm install --save-dev @babel/preset-env
npm install --save-dev @babel/preset-react
{
  "presets": [
    "@babel/preset-env",
    [
      "@babel/preset-react",
      {
        "pragma": "Omi.h",
        "pragmaFrag": "Omi.h.f"
      }
    ]
  ]
}

If you don't want to write CSS in JS, you can use to-string-loader of webpack, For example, the following configuration:

{
  test: /[\\|\/]_[\S]*\.css$/,
  use: [
    'to-string-loader',
    'css-loader'
  ]
}

If your CSS file starts with "_", CSS will use to-string-loader, such as:

import { tag, WeElement render } from 'omi'

define('my-app', class extends WeElement {

  css = require('./_index.css')
  ...
  ...
  ...

You can also forget the tedious configuration and use omi-cli directly, no need to configure anything.

TypeScript Auto Complete

import { h, WeElement, tag, classNames } from 'omi';
import * as styles from './_index.less';

interface ButtonProps {
  href?: string,
  disabled?: boolean,
  type?: 'default' | 'primary' | 'danger',
  htmltype?: 'submit' | 'button' | 'reset',
  onClick?: (e: any) => void
}

const TAG = 'o-button'

declare global {
  namespace JSX {
    interface IntrinsicElements {
      [TAG]: Omi.Props & ButtonProps
    }
  }
}

@tag(TAG)
export default class oButton extends WeElement<ButtonProps> {
...
...
...

Lifecycle

Lifecycle methodWhen it gets called
installbefore the component gets mounted to the DOM
installedafter the component gets mounted to the DOM
uninstallprior to removal from the DOM
beforeUpdatebefore update
updatedafter update
beforeRenderbefore render()
receivePropsparent element re-render will trigger it, return false will prevent update action

Debugging

Easy to debug via Omi DevTools Extension [Install from Chrome WebStore], using Omi DevTools you can simply debug and manage your UI without any configuration. Just install and debug.

Since Omi uses Web Components and Shadow-DOM, it doesn't need to have another elements panel such as React has. It just adds a panel to the Elements' sidebar and it's powerful as much as React DevTools.

Omi DevTools

View registered elements

console.log(Omi.elements)

Browsers Support

Omio - Omi for old browsers(IE8+)

Omi works in the latest two versions of all major browsers: Safari 10+, IE 11+, and the evergreen Chrome, Firefox, and Edge.

→ Browsers Support

→ Polyfills

<script src="https://unpkg.com/@webcomponents/webcomponentsjs@2.0.0/webcomponents-bundle.js"></script>

Contribution

Build a example:

npm start example_name

Build omi:

npm run build

Unit testing

npm run test

Contributors

Here is the todo list, welcome to contribute together.

Design philosophy

The Omi design was driven by The Zen of Python, by Tim Peters philosophy:

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren't special enough to break the rules.
  • Although practicality beats purity.
  • Errors should never pass silently.
  • Unless explicitly silenced.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one—and preferably only one—obvious way to do it.
  • Although that way may not be obvious at first unless you're Dutch.
  • Now is better than never.
  • Although never is often better than right now.
  • If the implementation is hard to explain, it's a bad idea.
  • If the implementation is easy to explain, it may be a good idea.
  • Namespaces are one honking great idea—let's do more of those!

Core Maintainers

Please contact us for any questions.

Thanks

License

MIT © Tencent