Giter Site home page Giter Site logo

vizia / vizia Goto Github PK

View Code? Open in Web Editor NEW
1.4K 1.4K 57.0 9.29 MB

A declarative GUI library written in Rust

Home Page: https://docs.vizia.dev

License: MIT License

Rust 97.37% CSS 2.63%
binding css declarative-ui framework graphics gui reactive rust toolkit ui user-interface

vizia's Introduction

Vizia


A declarative desktop GUI framework for the Rust programming language.



Features

  • Cross-platform (Windows, Linux, MacOS)

    Build desktop applications which look and behave the same for Windows, Mac, and Linux.
  • Declarative

    Write GUI code in a declarative way in pure Rust (no DSL macros).
  • Reactive

    Views derive from application state. Change the state and the views which bind to it update automatically.
  • Flexible layout

    Create flexible layouts which adapt to changes in size. Powered by morphorm.
  • Powerful styling

    Take advantage of CSS with hot-reloading to fully customize the style of your application.
  • Animations

    Bring your application to life with animatable style properties.
  • Built-in views and themes

    Utilize over 25 ready-made views as well as two built-in themes (light and dark) to get you started. Includes 4250+ icons, provided by Tabler Icons.
  • Accessibility

    Make you applications accessible to assistive technologies such as screen readers, powered by accesskit.
  • Localization

    Adapt your application to different locales, including translating text with fluent.
  • GPU accelerated rendering

    Vizia leverages the GPU for fast graphical updates, powered by femtovg.
  • Audio plugin development

    Vizia provides an alternative baseview windowing backend for audio plugin development, for example with the nih-plug framework.

At a Glance

A simple counter application. Run with cargo run --example counter.

use vizia::prelude::*;

// Define some model data
#[derive(Lens)]
pub struct AppData {
    count: i32,
}

// Define events to mutate the data
pub enum AppEvent {
    Increment,
}

// Describe how the data is mutated in response to events
impl Model for AppData {
    fn event(&mut self, _: &mut EventContext, event: &mut Event) {
        event.map(|app_event, _| match app_event {
            AppEvent::Increment => {
                self.count += 1;
            }
        });
    }
}

fn main() {
    // Create an application
    Application::new(|cx| {

        // Build the model data into the tree
        AppData { count: 0 }.build(cx);

        // Declare views which make up the UI
        HStack::new(cx, |cx| {
          
            // Declare a button which emits an event
            Button::new(cx, |cx| Label::new(cx, "Increment"))
              .on_press(|cx| cx.emit(AppEvent::Increment));

            // Declare a label which is bound to part of the model, updating when it changes
            Label::new(cx, AppData::count).width(Pixels(50.0));
        })
        .child_space(Stretch(1.0))  // Apply style and layout modifiers
        .col_between(Pixels(50.0));
    })
    .title("Counter") // Configure window properties
    .inner_size((400, 100))
    .run();
}

Learning

Book

A quickstart guide for vizia is available here.

Docs

Auto-generated code documentation can be found here.

Examples

A list of examples is included in the repository.

To run an example with the winit (default) windowing backend:

cargo run --release --example name_of_example

Baseview

To run an example with the baseview windowing backend:

cargo run --release --example name_of_example --no-default-features --features baseview

Web

To run an example as a web application, first ensure that the wasm32-unknown-unknown toolchain is installed:

rustup target add wasm32-unknown-unknown

Then run an example with the following:

cargo run-wasm --release --example name_of_example

Note Some examples are not compatible with the web target and will intentionally panic if run on web.

Contributing and Community

For help with vizia, or to get involved with contributing to the project, come join us on our discord.

License and Attribution

Vizia is licensed under MIT.

Vizia logo designed by Lunae Somnia.

vizia's People

Contributors

0hypercube avatar adriannic avatar artemist avatar coastalwhite avatar daslixou avatar dgriffin91 avatar eltociear avatar frando avatar fredemus avatar freefull avatar geom3trik avatar glowcoil avatar greatness7 avatar isaacmclellan avatar iwoithe avatar lunaesomnia avatar notpeelz avatar ollpu avatar perlindgren avatar piedoom avatar rhelmot avatar robbert-vdh avatar sky1e avatar uklotzde avatar youknow-sys avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vizia's Issues

Move things out of `core` into their own crate

We should consider moving things from core into their own crate (in the same repository) if reasonable.

Possible candidates

  • Storage types. SparseSet got separated in #196 but the AnimatableSet and StyleSet can't easily be separated because they depend things in vizia_core like Entity, Animation or Rule.
  • Style system (probably when integrating the new style system)
  • Views
  • Create a backend crate or just a folder with winit and baseview in it. Currently makes more sense to leave them separate, but it is possible to combine them. One thing to note though when combining the Cargo.toml files is that femtovg uses the glutin feature which uses winit so it has to be conditionally disabled when using the baseview.

Todos

  • Have a separate crates folder with all the crates that make up vizia.
  • Name the folders like the crate itself (e.g. vizia_core, vizia_winit, ...).

Lens derive macro does not add generic arguments to impl

Trying to derive Lens for a struct with a generic (lifetime) argument gets you the following error:

20 | #[derive(Lens)]
   |              - help: consider introducing lifetime `'a` here: `<'a>`
21 | struct Data<'a> {
   |             ^^ undeclared lifetime

To fix this, you need to specify the generic types, the type variables, and the where clause in the trait impl. Syn has a convenient split_for_impl() function that gets you exactly this information so you only have to splice it in. I've done the same thing for vst3-sys and NIH-plug if you need a reference.

Textbox: add affinity

Selection needs an affinity: bool added to it indicating whether the active cursor is on the upstream or downstream side of a break in the text.

30pt text as 2x DPI scaling renders badly

We discussed this on the Discord, but I'll also post it here so it doesn't get forgotten about. This is what happens when you render a label with a 30 point font size and the Noto Sans Thin font and 2x scaling (application.with_scale_factor(WindowScalePolicy::ScaleFactor(2.0) with #80):

afbeelding

The other text, which is 15pt Noto Sans Light, looks fine. Let me know if you need more details for reproducing this.

Scroll to view

There should be some way to scroll to a particular view which is contained within some scrollview. Not sure of the exact API for this but perhaps an event, so cx.emit(ScrollEvent::ScrollToView(entity)).

Setting an ID value with #<name> seems to overwrite other values in css

The minimal code showing what I think is a bug:

use vizia::*;

const STYLE: &str = r#"
#test {
    background-color: blue;
}

/* Simply make the knob itself smaller */
knob {
    width: 100px;
    height: 100px;
}

knob .track {
    background-color: #red;
}


"#;

#[derive(Lens)]
pub struct DummyData {
    data: f32
}

impl Model for DummyData {
    // Update the knob value
    fn event(&mut self, cx: &mut Context, event: &mut Event) {
        if let Some(pan_event) = event.message.downcast() {
            match pan_event {
                DummyEvents::Dummy(n) => {
                    self.data = *n;
                }
            }
        }
    }
}

enum DummyEvents {
    Dummy(f32)
}

fn main() {
    Application::new(WindowDescription::new()
                         .with_inner_size(150,150), move |cx| {
        DummyData{
            data: 0.5
        }.build(cx);
        cx.add_theme(STYLE);

        Knob::new(cx, 0.2, DummyData::data, true)
            .on_changing(|cx, val| cx.emit(DummyEvents::Dummy(val)));

    }).run();
}

This is just a simple UI containing a knob where the bar's colour is red. The css contains a field where the name starts with a # as if it was an element selected by an ID, thus it should just change nothing to my understanding.

The UI that I would then expect is this:
grafik

But what actually happens is that all background-color values are overwritten with the value of #test, in this case blue:
grafik

I managed to get the same problem with VStacks, HStacks and Labels.

Targeting the root element from a CSS stylesheet

I would expect to be able to target the root element and set its background color, font size, text color etc. by targeting the :root selector in a stylesheet. (being able to use basic units in the stylesheets like ems, rems, and pixels instead of points for text sizes would also make setting a theme much simpler but I can understand it if that's out of scope for now)

Derive macro for Data

It would be pretty cool if we could say #[derive(Data)] on any type which also implements PartialEq.

Borders clip through ScrollViews

Running the following example:

use vizia::*;

fn main() {
    Application::new(WindowDescription::new().with_title("Basic"), |cx| {
        ScrollView::new(cx, 0.0, 0.0, false, true, |cx| {
            for i in 0..100 {
                Label::new(cx, &format!("Label {i}"))
                    .border_color(Color::black())
                    .border_width(Pixels(1.0));
            }
        })
        .width(Percentage(100.0))
        .height(Pixels(100.0));
    })
    .run();
}

Causes this to happen:

simplescreenrecorder-2022-03-27_21.01.16.mp4

These borders should not be drawn at all. I've found some other issues with ScrollView involving DPI scaling, I'll create another issue for those.

Emitting `WindowEvent::WindowClose` doesn't close the window

use vizia::prelude::*;

fn main() {
    Application::new(|cx| {
        Button::new(
            cx,
            |cx| cx.emit(WindowEvent::WindowClose),
            |cx| Label::new(cx, "Close window"),
        );
    })
    .run();
}

Given the code above, clicking on the button might be expected to close the window, but at the moment it doesn't do anything.

Release First Version

A list of desired features for the first release of VIZIA.

General

  • Declarative API
  • Reactivity
  • Events system
  • VST support
  • Multiwindow (this could be pushed to a later release)

Documentation

  • Basic documentation for docs.rs
  • Initial draft of vizia-book

Examples and Tutorials

External:

  • vizia-vst-demo
  • vizia-demo

Internal:

  • Example for each available view
  • Examples for layout properties
  • Examples for style properties

Widgets

Controls:

  • Button
  • Label
  • Checkbox
  • Radio
  • Slider
  • Knob
  • ScrollBar
  • Textbox
  • Image

Containers:

  • HStack
  • VStack
  • ZStack
  • Dropdown
  • Popup
  • List
  • ForEach
  • Table
  • ScrollView

Layout

  • Stacks
  • Grids

Styling

  • Inline styling
  • External stylesheets
  • #8
  • Default stylesheet

Properties:

  • display
  • visibility
  • opacity
  • z-order
  • transform
    • translate
    • rotate
    • scale
  • background
    • -color
    • -gradient
    • -image
  • border
    • width
    • color
  • border-radius
  • broder-shape
    • round
    • bevel
  • outer-shadow
    • h-offset
    • v-offset
    • blur
    • color
  • font
    • -color
    • -size

DPI

Vizia should scale appropriately for displays with different DPI values.

  • DPI support

Animation

For the first release most of the available style properties should be animatable, both in Rust code and as transitions in css.

  • Style transitions and keyframe animations

Text

For the first release there should be a textbox which supports editing multiline text in multiple langauges. Rich text can be pushed to a later version but is a goal of the framework.

  • Multiline text editing

Accessibility

Accessibility is an important feature for a UI framework. However, available crate options for adding accessibility support are limited at the time of writing. The in-development AccessKit crate could be used for screen reader support when in a more finished state. Screen reader support isn't the only accessibility feature, and for the first release I think it is necessary to include focus order keyboard navigation, probably with the tab key to go forward and shift+tab to go backwards.

Localization

Localization is another important and often neglected feature for a UI framework. This includes many aspects, such as Left-to-Right layout, translation, date formatting etc. For the first release there should be some basic localization provided by fluent:

Persistence of View/Model data

Currently when a view is built using Self{ }.build2(cx, |cx|{ }) the closure is run every time but the view is not added to the context if it already exists. This means that any changes to fields in Self { } are not updated when the view is rebuilt.

This isn't necessarily a problem because any data that did need to be updated could live in a model which is updated when rebuilt. So when you have some data AppData{ }.build(cx), this is updated with new values every time it is called, so calling it inside a binding updates that data every time the binding is rebuilt.

This whole system presents a few problems. The first is that there are two different places data can live and two different behaviours that can happen when rebuilt:

  1. Data inside a view struct, not updated when rebuilt
  2. Data inside a view struct, updated when rebuilt
  3. Data inside a model, not updated when rebuilt
  4. Data inside a model, updated when rebuilt

Data inside of views is local to the view wheras data inside of models can be shared accross multiple views.

The second problem is that even when data is updated there is no easy access to the previous version of the data without having to get a view/model from context and downcasting it to the right type. This becomes a problem if you want to conditionally update a view/model. A further problem is that sometimes it's necessary to persist model data, resulting in an ugly if cx.data::<AppData>().is_none() check.

I think what is needed is an explicit way for the user to determine if the view/model data should be updated or not if there is already a version in context. I'm not sure of the exact systax yet, but perhaps something like:

cx.build(|cx, prev|{
    if let Some(previous_data) = &prev {
        // Choose to not update or could return Some(Self{ }) which would update it
        return None;
    } else {
        // Create a new one if there's no previous one
        return Some(Self{ }); 
})

This isn't ideal because it shoudn't be possible to not create a new instance when there isn't a previous one but it's a start. I'm very much open to suggestions.

Interaction with the Textbox widget

Right now there's no way to programmatically control a text box widget from another widget holding one. It would be useful to be able to do things like focus the text box (as if the user clicked on it), move the caret around, and control the selection.

Question: Comparison with other project

How does that differ (ergonomics, customizability, performance) from:

  • tuix
  • druid
  • iced

Are you doing this is as a hobby or do you intent to use it for a project?
Do you plan on properly supporting modals, popups, dropdown (i.e with mutliple windows)?

From a first look by running the example it look visually beautiful. I will be happy to provide some feedback around ergonomics once I get to try it!

Keep up the good work :)

Overriding the * selector no longer works

As of a couple commits ago (I have not yet had the time to bisect), running the following example:

use vizia::*;

fn main() {
    Application::new(WindowDescription::new().with_title("Basic"), |cx| {
        cx.add_theme("* { font-size: 15; color: red; }");

        HStack::new(cx, |cx| {
            Label::new(cx, "Hello").font_size(50.0);
            Label::new(cx, "World").color(Color::yellow());
        });
    })
    .run();
}

No longer causes the .font_size() and .color() property setters to affect the label elements.

Add a "null" backend

We should have the ability to construct a backend without winit or baseview installed. There should still be a canvas, but writes to it should be discarded, or if that's not an option, written to an offscreen buffer. This will allow us to build testcases (including doc tests and perf tests) that can run in CI and build full Applications..

Proposal for custom style properties

I think that a necessary feature that we should support is custom style properties in css. These properties could look like css variables, for example:

knob {
  --radius: 30px;
}

During style matching, these properties could be passed to a style method within the View trait:

impl View for Knob {
  fn style(&mut self, cx: &mut Context, properties: Vec<(String, String)>) {
    // Parse and use the custom properties
  }
}

At the moment I'm not sure if these properties should be passed as-is to the view for parsing, or whether they should be pre-parsed and passed as one of a selection of possible property types. I'm open to suggestions and also alternatives to this implementation idea.

DPI scaling doesn't scale row-between/col-between amounts

Original title: DPI scaling adds padding to the end of ScrollViews

Continuing from #89, when you switch to the baseview backend and add a DPI scale factor to the example (winit doesn't let you explicitly set the scale factor) like this:

use vizia::*;

fn main() {
    Application::new(WindowDescription::new().with_title("Basic"), |cx| {
        ScrollView::new(cx, 0.0, 0.0, false, true, |cx| {
            for i in 0..100 {
                Label::new(cx, &format!("Label {i}"))
                    .border_color(Color::black())
                    .border_width(Pixels(1.0));
            }
        })
        .width(Percentage(100.0))
        .height(Pixels(100.0));
    })
    .with_scale_policy(WindowScalePolicy::ScaleFactor(2.0))
    .run();
}

Additional padding gets added to the end of the ScrollView. This amount of padding is proportional to the scaling factor.

simplescreenrecorder-2022-03-27_21.09.15.mp4

The * selector overrides attributes set on a handle

When doing something like this:

cx.add_theme("* { font-size: 20px; }");
HStack::new(cx, |cx| {
    Label::new(cx, "Hello, world!").font_size(40.0);
});

The label will still have a size of 20 logical pixels, as if !important was used in the style sheet.

Textbox: enable vertical scrolling

I truly don't know how I missed this, but if you have a textbox that overflows vertically and move the cursor off the bottom of the pane, it will not scroll down. Yikes!

Remove DerefContainer

It was a hack to maintain compatibility with code that expected Lens::get to return a reference. Now that get always clones its data, it should just return the value instead of trying to provide a layer of indirection.

Implement the new style system

A tracking issue for the work on the new style system.

Tracking issue

Style system

  • Add keyframe animations.
  • Add custom properties.
  • Add all/most of the properties we need and want.
  • Combine the selectors and property parsing.
  • Add tests for the selectors.
  • Write tests for parsing whole style sheets.
  • Test some real world examples to see if everything behaves properly.
  • Implement it in vizia (Maybe in a separate crate?)

Vizia

  • Add support for properties that vizia currently can't represent (border, outline, ...).

Properties

List of properties

Note

Every property marked with a * means that the property could be implemented by the new style system, but vizia doesn't support it currently.

TODOs

  • Remove selection-color once we have the pseudoselector version. Added a TODO note in the code.
  • Change border-corner-shape to support defining all four sides.
  • Remove inset property from BoxShadow.
  • Fix spelling mistake in inner-shadow-blur property.
  • Add separate translate, rotate and scale properties that use the Transform type.

General

  • display
  • visibility
  • overflow
  • opacity

Layout

  • layout-type
  • position-type
  • space
  • left
  • width
  • right
  • top
  • height
  • bottom
  • min-left
  • max-left
  • min-width
  • max-width
  • min-right
  • max-right
  • min-top
  • max-top
  • min-height
  • max-height
  • min-bottom
  • max-bottom
  • child-space
  • child-left
  • child-right
  • child-top
  • child-bottom
  • row-between
  • col-between

Border

Shorthand

  • border*

Color

  • border-color*
  • border-top-color*
  • border-right-color*
  • border-bottom-color*
  • border-left-color*

Shape

  • border-corner-shape
  • border-top-left-shape
  • border-top-right-shape
  • border-bottom-left-shape
  • border-bottom-right-shape

Radius

  • border-radius
  • border-top-left-radius
  • border-top-right-radius
  • border-bottom-left-radius
  • border-bottom-right-radius

Style

  • border-style*
  • border-top-style*
  • border-right-style*
  • border-bottom-style*
  • border-left-style*

Width

  • border-width
  • border-top-width
  • border-right-width
  • border-bottom-width
  • border-left-width

Outline

Shorthand

  • outline*

Color

  • outline-color*
  • outline-top-color*
  • outline-right-color*
  • outline-bottom-color*
  • outline-left-color*

Style

  • outline-style*
  • outline-top-style*
  • outline-right-style*
  • outline-bottom-style*
  • outline-left-style*

Width

  • outline-width
  • outline-top-width
  • outline-right-width
  • outline-bottom-width
  • outline-left-width

Background

  • background-color
  • background-image
  • background-gradient

Font

  • font-size
  • font-color
  • font-weight
  • font
  • selection-color
  • caret-color
  • caret-shape
  • user-select
  • text-wrap

Shadow

Outer

  • outer-shadow
  • outer-shadow-h-offset
  • outer-shadow-v-offset
  • outer-shadow-blur
  • outer-shadow-color

Inner

  • inner-shadow
  • inner-shadow-h-offset
  • inner-shadow-v-offset
  • inner-shadow-blur
  • inner-shadow-color

Other

  • transition
  • z-index
  • transform
  • cursor

Custom

  • custom property support

Selectors

List of selectors

Combinator

  • Child (>)
  • Descendant (space)
  • NextSibling (+)
  • LaterSibling (~)

Identifier

  • ID (#name)
  • LocalName (element)
  • Class (.class)

PseudoClass

  • FirstChild
  • LastChild
  • Root
  • Hover
  • Active
  • Focus
  • Enabled
  • Disabled
  • ReadOnly
  • ReadWrite
  • PlaceHolderShown
  • Default
  • Checked
  • Indeterminate
  • Blank
  • Valid
  • Invalid
  • InRange
  • OutOfRange
  • Required
  • Optional
  • UserValid
  • UserInvalid
  • Lang
  • Dir
  • Custom

PseudoElement

  • After (::after)
  • Before (::before)
  • Selection (::selection)
  • Custom

The PropGet trait has not been re-exported

Like PropSet makes setting styling properties much simpler, PropGet makes it a lot simpler to write widgets that need to be aware of their styling (for instance, to be able to subtract their borders from the total size, or to respect some common styling properties while doing custom rendering on a canvas). It would be nice to be able to use this for custom widgets.

Decide on defaults for min-width and min-height

For container elements, it makes sense for min-width and min-height to be auto (so they do not clip their children if the child width conflicts with the stretch width), but for leaf elements, it makes sense for min-width and min-height to be 0, since their default size is auto, and we should enable clipping contents if explicitly specified in size.

The questions are:

  • what should the default be, since it will have to be overridden in every element of the other category?
  • should the default be applied in morphorm, vizia, or the default style sheet?

css variables in the style sheet

It would be cool to be able to do something like

:root {
  --main-bg-color: brown;
}
.bode {
  color: var(--main-plot-color);
}
.waveform {
  color: var(--main-plot-color);
}

Would make customizing big style sheets a bit easier and would help with adding skins for my synth at some point

Move clipboard to window for Wayland support

As mentioned in #110

Fully implementing Wayland support will take more work as we need need a
RawWidnowHandle to request a proxy, which is not available until after
the Context is created.

The clipboard proxy should be created at the point that the window is created and the window handle is available rather than when the context is created. This requires either moving the clipboard handle to the window, or we can place the clipboard behind an Option in context.

Add feature configuration to remove wayland/x11 deps if desired

The default configuration should be to build for both x11 and wayland to maintain maximum out-of-the-box compatibility. However, we should also have the option to only compile for one of these if necessary - this would reduce build times substantially, I believe.

Textbox: support rtl scripts

it shouldn't be too hard, we just need to do the bidi analysis the same way femtovg does. I left TODOs in the relevant places in the code (mostly text/layout.rs)

Data trait derivation requires Data to be in scope

sticking #[derive(::vizia::prelude::Data)] on a class will fail!

error[E0405]: cannot find trait `Data` in this scope
   --> src/assets.rs:109:59
    |
109 |         #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone, ::vizia::prelude::Data)]
    |                                                           ^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
    |
   ::: src/celeste_mod/module.rs:28:1
    |
28  | uuid_cls!(ModuleID);
    | ------------------- in this macro invocation
    |
    = note: this error originates in the derive macro `::vizia::prelude::Data` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider importing one of these items
    |
1   | use crate::Data;
    |
1   | use vizia::state::Data;
    |

Commit 3bfd728 breaks custom plots

Having a custom plot view and putting it in a Stack somehow means it gets clipped in the wrong way:
image

The only way I've found to fix it is to add .overflow(Overflow::Visible) to every parent it has, which is less than ideal for a bigger project.

How to reproduce:
Comment in the HStack here: https://github.com/Fredemus/va-filter/blob/nih-plug/src/ui.rs#L174-L182, run by writing cargo run svf_filter svf_gui_bin.

Completely forgot to file an issue about this because I had vizia locked to a specific commit, sorry!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.