Most chapters of this documentation is outdated temporarily now, the arch of elvisjs has just changed these days. The new documentation is coming, welcome to join and work with us together!

Calling Elvis

Rust crate doc downloads Discord Chat LICENSE

Is anybody home? The Evlis Book mainly talks about the usage of elvis, here is our roadmap, come and join us !~

Help Wanted

  • A static http/websocket development server #16
  • Documentation of ElvisJS #65
  • A Markdown parser using ElvisJS #66
  • Optimize the State machine #67

Getting Started

# Install elvis package manager
$ cargo install epm

# New your awesome-app
$ epm new my-awesome-app

# Start development server
$ cd my-awesome-app && epm dev
[INFO  warp::server] listening on http://0.0.0.0:3000

Hello, World!


#![allow(unused)]
fn main() {
//! src/lib.rs
use elvis::{
    prelude::*,
    widgets::{layouts::Center, Text},
};

#[page]
struct Index;

impl LifeCycle for Index {
    fn create(&self) -> Node {
        Center::with(Text::with("Hello, World!")).into()
    }
}
}

Examples

LICENSE

Heartbreak Hotel.

Arch

This chapter is about the arch of ElvisJS.

backend

This crate is a backend which can compile elvis code just in time, for example, it compiles the rust code into wasm, and starts a web server to serve our web APP.

core

The core library abstracts the UI without invoking into any platform.

We don't need to know how it works if you just want to develop Apps using ElvisJS, because the elvis library provides all we need in the core library.

Node

The common UI of ElvisJS are composed with nodes which are defined in this core library, just the frame and transform stuffs.

UI

Attribute && Class

Attribute and Class are defined in this library as well, both of them are served for the Node, attribute controls the features of Node, and the class affect on the style of Node.

Values

All static values are defined in this library, for example, Color, Unit.

Layout

Finally, we are reaching the layout part. The difference of ElvisJS and other UI library in rust/wasm is ElvisJS provides UI itself, not just give you guys a parser parsing rust to Javascript or html.

The layout part is only defined in the core library, for example, the web library is just a re-export of the layout part of this core library, migrating the ElvisJS UI into browser.

Lifecycle

The lifecycle of ElvisJS defines like

LifeCycleDescription
CreateTriggers while creating the component in node tree
RenderTriggers while rendering the component in node tree
UpdateTriggers when the state machine changes
DropTriggers while droping the component

State machine

State machine of Elvisjs equipped for user-defined components, the fields of the struct are the storage of the state machine, which we can modify in gestures.

derive

This is a proc-macro crate for constructing our elvis code, named elvis-derive. It can help us writing Elvis Apps easily.

Don't worry about the usage of this repo, you can see how to master them in our examples.

epm

This the package manager for ElvisJS. You can generate elvis App using this repo, yep, after cargo install epm.

support

This is a inside proc-macro crate, only for development, you can find the source code here if you are interested in it.

web

This is the web interface of ElvisJS, like the core crate, it is contained in the elvis crate, don't worry about how to use this unless you want to hack.

Benchmark

A counter App is the target where elvis tests runs.

/* PATH='counter/pages/index.js' */
import { Center, Text, StatefulWidget } from "calling-elvis";

class Counter extends StatefulWidget {
  state = {
    hello: "Is anybody home?"
  }

  create() {
    let start = new Date().getTime();
    for (let i = 0; i < 100000000; i++) {
      this.state.hello = i.toString();
    }
    let end = new Date().getTime();
    console.log((end - start) + "ms");
  }

  render() {
    return Center(
      Text(this.state.hello, {
        bold: true,
        italic: true,
        size: 6,
      })
    );
  }

  update() {
    console.log("update...");
  }

  dispose() {
    console.log("dispose...");
  }
}

export default Counter;
# PATH='counter/'
yarn dev
ModelProcessorMemory
mbp.Catalina.17.13-inch2.3 GHz Dual-Core Intel Core i516 GB 2133 MHz LPDDR3

DOM Update

library10,000100,0001,000,00010,000,000100,000,000
elvis~1ms~10ms~100ms~900ms~8s
react~35ms~125ms~830ms~8000msbroken
vue~7ms~40ms~280ms~3000ms~26s

Bundle Size

librarysize
elvis432kb
react484kb
vue592kb

Library

Elvis currently contains:

Widgets

Widgets coming as they are!

Elvis just provides Image and Text for now, we've got apis to compose new Widgets in rust api, join us if you like calling Elvis.


#![allow(unused)]
fn main() {
/// Elvis' Attribute is cool.
/// 
/// The no-cool part is, Elvis has a big Attributes Enum system, 
/// it's more responsible to use enum than chars.
pub struct Attribute {
  key: Attributes,
  value: AttributeValues,
}

/// Abstract ElvisElement
pub trait ElvisElement {
  // give birth to a new life
  fn append(&mut self);
  
  // deserialize html string to ElvisElement
  fn de(s: String) -> ElvisElement;
  
  // call her/his father
  fn father(&self) -> ElvisElement;
  
  // if you don't have a girlfriend, new one.
  fn new<T>() -> T;
  
  // serlalize ElvisElement to html string
  fn ser(&self) -> String;
}

/// Love is All you Need!
///
/// Speaking easily, you don't need to write the trait above,
/// Elvis has a proc-macro to impl the trait defaultly, 
/// just paint your child.
#[derive(ElvisElement)]
pub struct MyElement {
  css: String,
  tag: String,
  attrs: Vec<Attribute>,
  child: Arc<AsRef<ElvisElement>>,
}
}

Javascript

It's quite intresting dressing widgets up for a new face, we can do anything changing Elvis we want in Javascript, it depends on if you have the power to save the world, as we know, you are the one.

Image

/* Image */
import { Center, Elvis, Text, Image } from "calling-elvis";

// Generate a `Image`
const myImage = Image({
  src: "https://images-assets.nasa.gov/image/S65-34635/S65-34635~orig.jpg", 
  child: Text("hallo, spaceboy!"),
});

Elvis.call(Center(myImage));

If you don't want Image playing in background anonymously, just remove the child field.

Declaration:

function Image({ 
  src: string,
  child: Widget,
}): Widget;

Text

/* Text */
import { Center, Colors, Elvis, Text } from "calling-elvis";

Elvis.call(
  Center(
    Text("Calling Elvis", {
      bold: true,
      color: Colors.PinkAccent(),
      italic: true,
      size: 42.0,
      weight: 700,
      height: 20,
      stretch: 100,
    }),
  ),
);

Text might be the most popular spider from Mars, Does it know the Great Ziggy Stardust?

Declaration:

function Text(text: string, {
  bold: boolean,
  color: Colors,
  italic: boolean,
  size: number,    // rem
  weight: number,  // rem
  height: number,  // rem
  stretch: number, // percent
}): Widget;

DOM

Like the other UI libraries, webassemblly Elvis arms a Virtual-DOM, too.

import { Center, Elvis, GestureDetector, StatefulWidget, Text } from "calling-elvis";

class MyState extends StatefulWidget {
  state = {
    count: 0,
  }
  
  create() {
    console.log("The first step is creating the dom.");
  }
  
  update() {
    console.log("Then we can update it, just like what you saw.");
  }
  
  render() {
    return Center(
      GestureDetector(
        Text("I'm a Elvis fans."),
        onTap: () => this.setState({ 
            count: this.count + 1 
        });
      ),
    );
  }
  
  dispose() {
    console.log("Don't be crue, if you want to drop me.");
  }
}

I won't tell you that Elvis is not only a React fan but also a Flutter fan, and you'll never know he is a Emacs fan.

LifeCycle

Already knew you care about lifecycles, Elvis will never force you to recite 11 or more methods, Elvis just got 3, and it is enough ; )

Furthermore, to be serious, we don't recommend you to write big projects such as facebook, reddit, or twitter calling Elvis, Elvis is under Proof-of-Concept for now, and...you know, we strongly recommend you to use Elvis building your persenol website, make the web better, more interesting, awesome as it has never been 🌈

And if you are building a rocky start-up project, believe me, CALLING ELVIS, and the force will be with you!

Life Story, Love and Glory.

Diff

Elvis' diff algorithm is quite simple flying with wasm, we compare the new node and the old one using dps, and then patch the updates to the old one.

Patch

Elvis prefers to cure the naughty node's father node, if there are complex changes inside it, for Example:

<father :="I'm the naughty nodes' father">
  <naughty>Up</naughty>
  <naughty>Side</naughty>
  <naughty>Down</naughty>
</father>

Now we upside down the Up, Side, Down List:

<father :="plz don't make me heartbreak for twice.">
  <naughty>Down</naughty>
  <naughty>Side</naughty>
  <naughty>Up</naughty>
</father>

Elvis with not trying to swap <li>Up</li> and <li>Down</li>, it will operate DOM twice over, we just replace the whole <ol>...</ol> for once.

Events

Note: In progress.

Follows MDN docs Events

Events share properties to State, kind like a third-part plugin in Elvis, it implelements both in rust and typescript.

Javascript

/* Events */
import { Component, Elvis, Events } from "calling-elvis";
const { Text } = Elvis;

class myClickAbleText extends State {
  state = {
    msg: "",
  }

  consturstor(msg: string) {
    this.msg = msg;
  }

  click() {
    window.alert(`{}, roger that!`);
  }

  render() {
    return Text("Click me plz.");
  }
}

Elvis Events are outside of native components, we writing them straight just like writing lifecycles. Elvis strongly recommands our folks writing persnal components even publishing them to github.

Rust


#![allow(unused)]
fn main() {
pub enum Events {
  Cancel,
  Error,
  Scroll,
  Select,
  Show,
  Wheel,
  // Clipborad events
  Copy,
  Cut,
  Paste,
  // Composition events
  CompositionEnd,
  CompositionStart,
  CompositionUpdate,
  // Focus events
  Blur, 
  Focus,
  Focusin,
  Focusout,
  // Fullscreen events
  KeyDown,
  KeyPress,
  KeyUp,
  // Mouse events
  AuxClick,
  Click,
  ContextMenu,
  DbClick,
  MouseDown,
  MouseEnter,
  MouseLeave,
  MouseMove,
  MouseOut,
  MouseOver,
  MouseUp,
  WebkitMouseForceChanged,
  WebkitMouseForceDown,
  WebkitMouseForceWillBegin,
  WebkitMouseForceUp,
  // Touch events,
  TouchCancel,
  TouchEnd,
  TouchMove,
  TouchStart
}
}

Events enum list all events in MDN events, the implementation in rust depends on gloo.

Gestures

Note: In Progress.

Event and Gesture are out of wasm, this is not cool, but still awesome, Elvis implements these two trouble maker in Typescript, because wasm-bindgen doesn't support passing javascript Object to rust for now, BUT, it will be totally cool one day.

GestureDetector


#![allow(unused)]
fn main() {
pub trait GestureDetector {
  fn on_double_tap(e: Event);
  fn on_force_press_end(e: Event);
  fn on_force_press_peak(e: Event);
  fn on_force_press_start(e: Event);
  fn on_force_press_update(e: Event);
  fn on_horizontal_drag_cancel(e: Event);
  fn on_horizontal_drag_down(e: Event);
  fn on_horizontal_drag_end(e: Event);
  fn on_horizontal_drag_start(e: Event);
  fn on_horizontal_drag_update(e: Event);
  fn on_long_press(e: Event);
  fn on_long_press_end(e: Event);
  fn on_long_press_move_update(e: Event);
  fn on_long_press_start(e: Event);
  fn on_long_press_up(e: Event);
  fn on_pan_cancel(e: Event);
  fn on_pan_down(e: Event);
  fn on_pan_end(e: Event);
  fn on_pan_start(e: Event);
  fn on_pan_update(e: Event);
  fn on_scale_end(e: Event);
  fn on_scale_start(e: Event);
  fn on_scale_update(e: Event);
  fn on_secondary_tap_cancel(e: Event);
  fn on_secondary_tap_down(e: Event);
  fn on_secondary_tap_up(e: Event);
  fn on_vertical_drag_cancel(e: Event);
  fn on_vertical_drag_down(e: Event);
  fn on_vertical_drag_end(e: Event);
  fn on_vertical_drag_start(e: Event);
  fn on_vertical_drag_update(e: Event);
}
}

Are we still cool now?

Gesture in calling-elvis implements with typescript, but elvis still keeps these apis, so we still can rust the web happily with Elvis without confuse.

Layout

Follows MDN doc CSS Layout.

Widgets 📦

Elvis Layout mainly contains Flex and Grid, btw, Elvis offerrs two basic widgets Container and SizedBox for simple usages.

Container

/* Container */
import { Container, Colors, Elvis, Alignments } from "calling-elvis";

// Generate a `Container`
const myContainer = Container(
  Text("Where is my AJ-1?"), {
    align: Alignments.Center,
    color: Colors.Black(),
    height: 42,
    margin: 10,
    padding: 10,
    width: 42,
});

Elvis.call(myContainer);

The Alignments enum is from Flex, to be honest, Container component is a part of Flex family, but he is too brilliant to stay in Flex family, Layout calls him.

Declaraction:

function Container(child: Widget, {
  align: Alignments,
  color: Colors,
  height: number,        // rem
  margin: number,        // rem
  padding: number,       // rem
  width: number,         // rem
}): Widget;

List

/* List */
import { Elvis, List, Text } from "calling-elvis";

// Generate a `List`
let myList = List([
  Text("poor-orphan-1"),
  Text("poor-orphan-2"),
  Text("poor-orphan-3"),
]);

Elvis.call(myList);

(Sorry about that), List is a set of poor orphan children, they don't have any style, just blowing in the wind.

Declaraction:

function List(widgets: Widget[]): Widget;

SizedBox

/* SizedBox */
import { Elvis, SizedBox, Text } from "calling-elvis";

// Generate a `SizedBox`
const mySizedBox = SizedBox(
  Text("My SizedBox"), {
    height: 42,
    width: 42,
});

Elvis.call(mySizedBox);

SizedBox just has width and height two arguments, we use this component to take some white space usually.

Declaraction:

function SizedBox(widget: Widget, {
  height: number, // rem
  width: number,  // rem
}): Widget;

Flex

Follows MDN doc CSS Flexible Box Layout

Widgets 📦

The very basic core of Flex Layout widgets is called Flex, unfortunatly, it contains almost all css flex properties, no easy to use, that's why elvis composes some newbie components as well.

Align

/* Align */
import { Align, Alignments, Elvis, Text } from "calling-elvis";

// Generate an `Align`
let myAlign = Align(
  Text("Align Elvis"), {
    align: Alignments.Center,
});

Elvis.call(myAlign);

Align inherits the core usage of Alignment, it's quite simple, just one property.

Declaration:

function Align(
  child: Widget, {
  align: Alignments, 
}): Widget;

Center

/* Center */
import { Center, Elvis, Text } from "calling-elvis";

// Generate an `Center`
let myCenter = Center(
  Text("My website only have a line of chars 🦀 "),
);

Elvis.call(myCenter);

Center is a very nice widget, if your website only have a line of chars, use it!

Declaration:

function Center(child: Widget): Widget;

Col

/* Col */
import { Col, Elvis, Text } from "calling-elvis";

// Generate a `Col`
const myCol = Col([
  Text("All is above you all is sky"),
  Text("All is behind you all is sea"),
]);

Elvis.call(myCol);

Col towards column, the typical flow in html, with flexible enhance.

Declaration:

function Col(widgets: Widget[]): Widget;

Flex

/* Flex */
import { Elvis, Flex, List, Text } from "calling-elvis";

const myFlex = Flex(
  List([
    Text("hi, I'm the Lunatic Widget's child No.1"),
    Text("hi, I'm the Lunatic widget's child No.2"),
    Text("hi, I'm the Lunatic Widget's child No.3"),
  )], {
    align: Alignments.Center,
    basis: FlexBasis.Fill(),
    direction: FlexDirection.ColumnReverse,
    grow: 1,
    order: 1,
    shrink: 1,
    wrap: true,
});

Elvis.call(myFlex);

This is the Lunatic Widget to Ground Control, I'm stepping throw the Window.

Declaration:

function Flex(widget: Widget, {
  align: Alignments,
  basis: FlexBasis,
  direction: FlexDirection,
  grow: number,             // no unit
  order: number,            // no unit
  shrink: number,           // no unit
  wrap: boolean,
}): Widget;

Row

/* Row */
import { Elvis, Row, Text, } from "calling-elvis";

// Generate a `Row`
let myRow = Row([
  Text("I'm Wrong"),
  Text("I'm Right"),
]);

Elvis.call(myRow);

Both Col and Row are using flex-start, if you want to reverse the children of them, better to work on the list order.

Declaration:

function Row(widgets: Widget[]): Widget;

Enums 🍩

Elvis Layout Aligns follows the MDN doc [CSS Box Alignment][2], but simplify it into a enum Aligment here, Alignment is used by all Flex components and Container in Elvis.

Alignment

Here is the Alignment defination in rust source code.


#![allow(unused)]
fn main() {
/// Magical Alignment
pub enum Alignment {
  BottomCenter,
  BottomLeft,
  BottomRight,
  Center,
  CenterLeft,
  CenterRight,
  TopCenter,
  TopLeft,
  TopRight,
}
}

FlexBasis


#![allow(unused)]
fn main() {
pub enum FlexBasis {
  Fill,
  MaxContent,
  MinContent,
  FitContent,
  Number(Unit), // Rem
}
}

Well, lunatic FlexBasis in Rust source code.

FlexDirection


#![allow(unused)]
fn main() {
pub enum FlexDirection {
  Column,
  ColumnReverse,
  Row,
  RowReverse,
}
}

Lunatic FlexDirection, you know it.

Grid

Follows MDN doc Grids.

Here is the Grid section, Just let Elvis show you how Grid Grid.

Grid

/* Grid */
import { Grid, GridAuto, GridFlow, GridTemplate, Elvis, Text } from "calling-elvis";

// Generate an `Grid`
const myGrid = Grid(
  List(
    Text("Mercury"),
    Text("Venus"),
    Text("Earth"),
    Text("Mars"),
    Text("Jupiter"),
    Text("Saturn"),
    Text("Uranus"),
    Text("Neptune"),
    Text("Pluto"),
  ), {
    col: Grid.Auto(),
    col_gap: 1,
    flow: GridFlow.Column(),
    row: Grid.Auto(),
    row_gap: 1,
    template_col: GridTemplate.Inherit(),
    template_row: GridTemplate.Inherit(),
});

Elvis.call(mySizedBox);

Take it ease, only one Grid widget in Elvis.

/* Grid */
import { Grid, Elvis, List, Text } from "calling-elvis";

// Generate an `Grid`
const myGrid = Grid(
  List(
    Text("Mercury"),
    Text("Venus"),
    Text("Earth"),
    Text("Mars"),
    Text("Jupiter"),
    Text("Saturn"),
    Text("Uranus"),
    Text("Neptune"),
    Text("Pluto"),
  ),
);

Elvis.call(myGrid);

Grid is quite complex in some way, usually, we just Grid our contains.

Declaration

function Grid(widget: Widget, {
  col: GridAuto,
  col_gap: number,              // Rem
  flow: GridFlow,
  row: GridAuto,
  row_gap: number,              // Rem
  template_col: GridTemplate,
  template_row: GridTemplate,
}): Widget;

Enums 🍩

Grid Grid is hard to pronounce, most of time we don't need to do this.

GridAuto


#![allow(unused)]
fn main() {
pub enum GridAuto {
    Auto,
    Fixed(Unit),
    Inherit,
    Initial,
    MaxContent,
    MinContent,
    MinMax(Unit, Unit),
    Plain(Vec<Unit>),
    Unset,
}
}

GridAuto affect the width of Grid children, and the Auto choice use the minmax function in css, if doesn't pass the second argument, it will be auto in meaning.

GridFlow


#![allow(unused)]
fn main() {
pub enum GridFlow {
    Column,
    Row,
    Dense,
    ColumnDense,
    RowDense,
    Inherit,
    Initial,
    Unset,
}
}

GridFlow::Column by default.

GridTemplate


#![allow(unused)]
fn main() {
pub enum GridTemplate {
    FitContent(Unit),
    Inherit,
    Initial,
    MinMax(Unit, Unit),
    None,
    Plain(Vec<Unit>),
    Repeat(i32, Unit),
    SubGrid,
    Unset,
}
}

In the Plain choice, Vec's length will be the column count of grid, and every Unit is the width of each column, Repeat just make this easier, every child are in the same width.

MultiColumn

Follows MDN doc Multiple-column layout.

The very easy way to layout our pages, maybe fantastic in the old web, it means, the true web, I like it.

MultiColumn

/* MultiColumn */
import { Colors, Elvis, MultiColumn, MultiColumnLineStyle, Text } from "calling-elvis";

// Generate an `MultiColumn`
const myMultiColumn = MultiColumn(
  List(
    Text("Mercury"),
    Text("Venus"),
    Text("Earth"),
    Text("Mars"),
    Text("Jupiter"),
    Text("Saturn"),
    Text("Uranus"),
    Text("Neptune"),
    Text("Pluto"),
  ), {
    color: Colors.Black(),
    count: 3,
    gap: 20,
    style: MultiColumnLineStyle.Groove,
});

Elvis.call(mySizedBox);

Homework: code a New York Times.

Declaration

function MultiColumn(
  widget: Widget, {
    color: Colors,
    count: number,                  // no unit
    gap: number,                    // Rem
    style: MultiColumnLineStyle,
}): Widget;

Enums 🍩

The style of MultiColumnLine.

MultiColumnLineStyle


#![allow(unused)]
fn main() {
pub enum MultiColumnLineStyle {
  None,
  Hidden,
  Dotted,
  Dashed,
  Solid,
  Double,
  Groove,
  Ridge,
  Inset,
  OutSet
}
}

If I were you, I will choose MultiColumnStyle.Groove, don't ask why.

Router

/* Entry */
import { Elvis, Router } from "calling-elvis";
import { Africa, America, Asia, Europe, Oceania } from "./map";

new Elvis({
  home: Oceania,
  routes: {
    "africa": Africa,
    "america": America,
    "asia": Asia,
    "europe": Europe,
    "oceania": Oceania,
  },
})

A ship got a Navigator, we call it an App.

TODO*

We generate our routes in the entry of our Apps usually, Elvis has inner url parser in the navigator process, both url parameters and object style arguments are supported, so if we want to fly to the Mars, just do it.

Values

Values might be the most instresting part in Elvis, it focus on the design of our art.

Most of Elvis widgets have enum properties, instead of writing them in plain text, it terms to 'less thinking', free as a bird, free as in freedom.

Homework

Paint out the Starry Starry Night, Shell we?

Color

Follows MDN doc CSS Color Value


#![allow(unused)]
fn main() {
pub enum Colors {
  RGBA(i32, i32, i32, f64),
  Hex(String),
  HSL(Unit, i32, i32),
  Plain(String),
  Amber,
  AmberAccent,
  Black,
  Blue,
  BlueAccent,
  BlueGrey,
  Brown,
  Cyan,
  CyanAccent,
  DeepOrange,
  DeepOrangeAccent,
  DeepPurple,
  DeepPurpleAccent,
  Green,
  GreenAccent,
  Grey,
  Indigo,
  IndigoAccent,
  LightBlue,
  LightBlueAccent,
  LightGreen,
  LightGreenAccent,
  Lime,
  LimeAccent,
  Orange,
  OrangeAccent,
  Pink,
  PinkAccent,
  Purple,
  PurpleAccent,
  Red,
  RedAccent,
  Teal,
  TealAccent,
  Transparent,
  White,
  Yellow,
  YellowAccent,
}
}

So many colors...well, the Colors above is Elvis' color system.

Unit

Follows CSS Values 3 drafted in csswg.org.


#![allow(unused)]
fn main() {
pub enum Unit {
  Ch(f64),
  Cm(f64),
  Dpi(f64),
  Dpcm(f64),
  Dppx(f64),
  Em(f64),
  Fr(f64),
  In(f64)
  Mm(f64),
  Pc(f64),
  Pt(f64),
  Px(f64),
  Q(f64),
  Rem(f64),
  Vh(f64),
  Vmax(f64),
  Vmin(f64),
  Vw(f64),
}
}

If we don't use Unit.X() in number field, Elvis will choose Unit.Px by default.

Absolute Lengths

unitnameequivalence
cmcentermeters1cm = 96px/2.54
mmmillimeters1mm == 1/10th of 1cm
Qquarter-millimeters1Q = 1/40th of 1cm
ininches1in = 2.54cm = 96px
pcpicas1pc = 1/6th of 1in
ptpoints1pt = 1/72th of 1in
pxpixels1px = 1/96th of 1in

Relative Lengths

unitrelative to
emfont size of element
exx-height of element's font
chwidth of the "0" (ZERO, U+0030) glyph in the element’s font
remfont size of the root element
vw1% of viewport’s width
vh1% of viewport’s height
vmin1% of viewport’s smaller dimension
vmax1% of viewport’s larger dimension

Others

unitrepresents
dpiDots per inch
dpcmDots per centmeter
dppxDots per px unit
frThis unit represents one fraction of the available space in the grid container.

Tutorial

Well this is an official tutorial for Elvis.

Installation

Use create-elvis-app

yarn create elvis-app

Use create-elvis-app to generate elvis structure directly.

Use elvis-cli

# create directly
mkdir my-awesome-app && cd my-awesome-app

# add elvis-cli
yarn global add elvis-cli

# add elvis library
yarn add calling-elvis

# elvis-cli will generate pages defaultly
elvis dev

Starting manually

# create directly
mkdir my-awesome-app && cd my-awesome-app

# add elvis library
yarn add calling-elvis
/* router */
import { Elvis } from "calling-elvis";
import Home from "./home";

new Elvis({
  home: Index(),
}).calling();
/* home */
import { Center, Text } from "calling-elvis";
const Home = () => Center(
  Text("Calling Elvis!")
);

export default Home;

Starting Rustly

Checkout elvis' rust doc

Widgets

Single Widget

Elvis has StatefulWidget and Widget, usually, StatefulWidget composed by class and Widget composed by variable or function.

StatefulWidget

class MyWidget extends StatefulWidget {
  state = {
    intro: "Is anybody home?",
  }
  
  render() {
    return Center(
      Text(this.state.intro),
    );
  }
  
  create() {
    console.log("create...");
    this.state.intro = "Roll up for the magical mystery tour!";
  }
  
  update() {
    console.log("update...");
  }
  
  dispose() {
    console.log("dispose...");
  }
}

Widget

// for variable
const myWidget = Center(Text("This is a line of chars"));
// for function
const myWidget = (line: string) => Center(Text(line));

Complex Widgets

We got some widgets made by our selvies sometimes.

class Child extends StatefulWidget {
  state = {
    name: "",
  }

  constructor(name: string) {
     this.state.name = name;
  }
  
  render() {
    return Text("My name is" + this.state.name);
  }
}

Wrong example: Do not new Widget inside widgets, unless you want to pass props to it.

class Parent extends StatefulWidget {
  render() {
    return Center(new Child("Elvis"));
  }
}

If you did this, the Child widget will re-render everytime the parent widget changes, cause every StatefulWidget got a reflect pointer in wasm, each of them gots its own widget tree and stylesheet.

class Parent extends StatefulWidget {
   widgets = {
     child: new Child("Elvis"),
   }
   
   render() {
     return Center(this.widgets.child);
   }
}
class Parent extends StatefulWidget {
   state = {
     name: "Elvis",
   }
   
   render() {
     return Center(new Child(this.state.name));
   }
}

Community

Discord Chat

Wechat

wechat-qr-code

Contribute

There are some basic tips for someone who wants to contribute to evlis.

Firstly, welcome to post any questions about evlis via Github Issue, Email, etc. We will give you reaction as soon as possible.

Make A Change

  1. Clone the repository on your local machine.

    When contributing to an open source project using GIT, you will have a copy or “clone” of the source code repository in your local development environment.Here, you can make your changes and commit them locally to create a revision history, allowing changes to be tracked and easily rolled back if required.

    $ git clone https://github.com/elvisjs/elvis.git
    
  2. Build the source

    Evlis is implemented purely in Rust, So, please make sure you have installed Rust before made contributions. There is not specific Rust version requirement, we recommend you to fetch the latest Rust version.

    Once you have installed Rust, type the following in the terminal to start compiling:

    $ cd elvis
    $ cargo build
    

    The resulting binary can be found in evlis/target/debug.

Raising A Pull Request

Once you feel satisfied with the changes, you can raise a pull request on Github.

Our maintainer will then approve the changes or request some changes before it get merged.

Happy contributions!