datenhahn / componentrenderer Goto Github PK
View Code? Open in Web Editor NEWA ComponentRenderer for the Vaadin Grid
License: Apache License 2.0
A ComponentRenderer for the Vaadin Grid
License: Apache License 2.0
When triggering ItemSetChange-Events the Extension walks through all components to check if they are still contained in the container and if not remove them. With rising row-numbers this slows down the adding/removing of container items n².
Hi,
i have tried to build a simple Grid and use the CompentRenderer AddOn.
But all my tries are not working. I just get an empty Grid as result if i add a ComponentColumn.
I couldnt figure out why. I am new to Vaadin and maybe i missed some dependencies?
I am using ivy and only added the tag "" to the xml-File. The rest of the ivy.xml is default.
I am very thankful for any help or advice. Thanks!
Here is my Code:
`
package view;
import java.util.ArrayList;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.server.FontAwesome;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Component;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import controller.TippappManager;
import entities.*;
import model.UserDaten;
import de.datenhahn.vaadin.componentrenderer.grid.ComponentGenerator;
import de.datenhahn.vaadin.componentrenderer.grid.ComponentGridDecorator;
public class ClassicGrid extends VerticalLayout {
public ClassicGrid (TippappManager manager, UserDaten userDaten) {
setSizeFull();
setMargin(true);
setSpacing(true);
Grid grid = new Grid();
grid.setContainerDataSource(new BeanItemContainer<Spiel>(Spiel.class));
ComponentGridDecorator<Spiel> componentGridDecorator = new ComponentGridDecorator<>(grid, Spiel.class);
int turnierID = userDaten.getUserTippgruppeByID(manager.getCurrentTippgruppe()).getTurnierId();
ArrayList<Spiel> offeneSpiele = userDaten.getSpiele().get(turnierID);
componentGridDecorator.addAll(offeneSpiele);
grid.setSizeFull();
grid.setEditorEnabled(true);
grid.setEditorBuffered(false);
ComponentGenerator<Spiel> generatorIcon = new ComponentGenerator<Spiel>() {
@Override
public Component getComponent(Spiel bean) {
return new Label(FontAwesome.HOURGLASS_2.getHtml(), ContentMode.HTML);
}
};
ComponentGenerator<Spiel> generatorText = new ComponentGenerator<Spiel>() {
@Override
public Component getComponent(Spiel bean) {
return new Label(FontAwesome.HOURGLASS_2.getHtml(), ContentMode.HTML);
}
};
componentGridDecorator.addComponentColumn("icon", generatorIcon);
componentGridDecorator.addComponentColumn("text", generatorText);
grid.setFrozenColumnCount(1);
//The Grid will be empty
grid.setColumns( "icon", "text");
//BUT this Grid will show the Columns "Spiel Id" and "Beschreibung" with the data
//grid.setColumns("icon", "text", "spielId", "beschreibung");
addComponent(grid);
}
}
`
Currently setRows() iterates through the whole collection. Is it planed to support a kind of lazy collection (e.g. like Viritin MGrid).
If I have a button to show the details row I want to be able to change the icon to "up"/"down" for showing/hiding details.
This does not work right now.
Example ComponentGenerator:
@Override
public Component getComponent(Object bean) {
Button button = new Button();
button.setWidth(2, Unit.EM);
button.setHeight(2, Unit.EM);
button.addStyleName(ValoTheme.BUTTON_ICON_ONLY);
button.setIcon(VaadinIcons.CHEVRON_DOWN);
button.addClickListener((Button.ClickListener) event -> {
boolean detailsVisible = grid.isDetailsVisible(bean);
event.getComponent().setIcon(detailsVisible ? VaadinIcons.CHEVRON_DOWN : VaadinIcons.CHEVRON_UP);
grid.setDetailsVisible(bean, !detailsVisible);
});
return button;
}
ComponentGridDecorator should implements Serializable
workround:
when in clustering envrionment,session and “all session object" should be serializable
In vaadin,all components should be serializable.
code:
public class ComponentGridDecorator implements Serializable
Somehow firefox is way more slow in rendering the grid when the component renderer is in use. That may be a mixture of occurrences which lead to the very bad performance.
What definitively happens is a lot of reflow which is not uncommon because of the various measurements vaadin has to take, but chrome seems to handle it either much faster or it evaluates better when it can measure batches and reflow afterwards.
Also it seems the requests for getting row-data are more blocking in firefox than in chrome, but that could be because of the slower rendering in firefox.
In your "Classic Grid with Decorator" demo:
Without the editor open, I can click the "Premium Kunde" checkboxes as expected.
With the editor open I can click on the "Premium Kunde" in the editor as expected.
But:
With the editor open, if I click on a "Premium Kunde" checkbox outside the editor, that checkbox is clicked OK, but then the editor is moved on top, and in the editor the checkbox shows with the old state.
Possible solutions:
For my use-case, (only use ComponentRenderer for checkboxes and buttons) I think I'd prefer the editor to be closed.
Hi,
trying to remove a column that is using a ComponentRenderer produces an exception:
java.lang.IllegalStateException: Renderer is not attached to any parent
at com.vaadin.ui.Grid$AbstractGridExtension.getParentGrid(Grid.java:4366)
at de.datenhahn.vaadin.componentrenderer.ComponentRenderer.setParent(ComponentRenderer.java:76)
at com.vaadin.server.AbstractClientConnector.removeExtension(AbstractClientConnector.java:597)
at com.vaadin.ui.Grid.internalRemoveColumn(Grid.java:5271)
at com.vaadin.ui.Grid.removeColumn(Grid.java:5260)
AbstractClientConnector.removeExtension invokes extension.setParent(null); ComponentRenderer.setParent does:
super.setParent(parent);
// VERY IMPORTANT: registers the DataGenerator extension
extend(getParentGrid());
But getParentGrid throws an exception if parent is null! I've seen a previous version of the class was checking if parent was set to null before invoking extend(getParentGrid())...
Thank you,
lorenzo
@RaimundBarbeln mentioned that with null components a json null exception is thrown
I tried to use ComponentRenderer with a Checkbox but the checkbox value cannot be changed by clicking the checkbox : nothing happens.
I have a valuechangelistener on the checkbox, but it is actually never entered when clicking the checkbox.
Another problem is that the checkbox is not centered, but sits on the top left of the cell, and I cannot figure a way to have it centered. (It has a defined width and height)
Hi,
You cannot select a row when you click to the right of a component within a cell.
Clicking to the left of the component correctly selects the row.
This can be seen in the demo project
This is kind of a show stopper for me, since it also does not fire the Click event for the grid, which I need for som columns.
Hello,
thanks for the great addon but we found a problem.
It is not possible to slowly scroll down the grid by dragging the scrollbar.
If you try to slowly scroll the grid by dragging the scrollbar ist stops midway and you have to start dragging again.
This is reproducable with all different implementation types used in your online demo.
When using a normal grid everything works fine.
Benchmark the renderer
I tried to recompile the default widgetset with the addon at the command line using Vaadin 7.6.6 and Oracle Java 1.8.0_92 in Ubuntu (15.04) and I failed. "-" in javascript function names are not allowed, are they? Can you help?
In the current directory I put all vaadin jars (without vaadin-widgets-7.6.6.jar) with its dependencies, the addon and in the subdirectory widgetset is the unpacked default vaadin-widgets-7.6.6.jar
Command line and output:
java -cp "*:widgetset" com.vaadin.tools.WidgetsetCompiler vaadin-componentrenderer
...
Compiling 1 permutation
Compiling permutation 0...
Compile of permutations succeeded
Compilation succeeded -- 32,973s
Linking into /home/franz/test/compiling/war/vaadin-componentrenderer
Invoking Linker Cross-Site-Iframe
Attempting to optimize JS
[ERROR] Unable to parse JavaScript
com.google.gwt.dev.js.JsParserException: StandardLinkerContext.optimizeJavaScript(17): missing ( before function parameters
function vaadin-componentrenderer() {
----------------^
at com.google.gwt.dev.js.JsParser$1.error(JsParser.java:111)
at com.google.gwt.dev.js.rhino.Context.reportError(Context.java:459)
at com.google.gwt.dev.js.rhino.TokenStream.reportSyntaxError(TokenStream.java:1594)
at com.google.gwt.dev.js.rhino.Parser.reportError(Parser.java:72)
at com.google.gwt.dev.js.rhino.Parser.mustMatchToken(Parser.java:64)
at com.google.gwt.dev.js.rhino.Parser.function(Parser.java:189)
at com.google.gwt.dev.js.rhino.Parser.parse(Parser.java:116)
at com.google.gwt.dev.js.JsParser.parseImpl(JsParser.java:134)
at com.google.gwt.dev.js.JsParser.parse(JsParser.java:89)
at com.google.gwt.dev.js.JsParser.parseInto(JsParser.java:94)
at com.google.gwt.core.ext.linker.impl.StandardLinkerContext.optimizeJavaScript(StandardLinkerContext.java:441)
at com.google.gwt.core.ext.linker.impl.SelectionScriptLinker.generateSelectionScript(SelectionScriptLinker.java:422)
at com.google.gwt.core.ext.linker.impl.SelectionScriptLinker.generateSelectionScript(SelectionScriptLinker.java:411)
at com.google.gwt.core.ext.linker.impl.SelectionScriptLinker.emitSelectionScript(SelectionScriptLinker.java:299)
at com.google.gwt.core.ext.linker.impl.SelectionScriptLinker.link(SelectionScriptLinker.java:212)
at com.google.gwt.core.ext.linker.impl.StandardLinkerContext.invokeFinalLink(StandardLinkerContext.java:336)
at com.google.gwt.dev.Link.doSimulatedShardingLink(Link.java:465)
at com.google.gwt.dev.Link.link(Link.java:182)
at com.google.gwt.dev.Compiler.run(Compiler.java:246)
at com.google.gwt.dev.Compiler.run(Compiler.java:158)
at com.google.gwt.dev.Compiler$1.run(Compiler.java:120)
at com.google.gwt.dev.CompileTaskRunner.doRun(CompileTaskRunner.java:55)
at com.google.gwt.dev.CompileTaskRunner.runWithAppropriateLogger(CompileTaskRunner.java:50)
at com.google.gwt.dev.Compiler.main(Compiler.java:127)
at com.vaadin.tools.WidgetsetCompiler$1.run(WidgetsetCompiler.java:80)
at java.lang.Thread.run(Thread.java:745)
Adding a full width(100%) component to a fully expanded column(column expand ratio is 1), does not expand the component. For example, following code in the demo generates initial width text field (not 100%) even though the column width is fully expanded:
myGrid.addColumn(COL_TEXTFIELD, Component.class)
.setRenderer(((ComponentRendererProvider)myGrid)
.createComponentRenderer()).setExpandRatio(1);
...
generatedPropertyContainer.addGeneratedProperty(COL_TEXTFIELD,
new TextFieldValueGenerator());
...
private static class TextFieldValueGenerator extends PropertyValueGenerator<Component>{
public Component getValue(Item item, Object itemId, Object propertyId) {
TextField field = new TextField();
field.setWidth("100%");
...
}
...
}
Isn't this the right approach?
-k
define the final api and behavior
First of all, thanks for the cool component!
I have a grid which is created using the ComponentGridDecorator and backed by a BeanItemContainer.
For the display in the grid I have a simple vlayout with 2 labels, first one is html, 2nd text.
Grid itself is set to 100%x100% after adding the data etc.
The vlayout with the labels is also set to 100%x100%
However, when I enable spacing in the vlayout only my first label is shown:
Any idea if this a problem on my side or with the component?
Hi,
in my project I get an IllegalStateException after removing a column and scroll some lines down.
To reproduce this error I take the ClassicGridTab class from the demo and insert a Button to remove a column during runtime just before addComponent(grid) in line 182:
addComponent(new Button("Remove " + GENERATED_DELETE + " Column", new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
if (grid.getColumn(GENERATED_DELETE) != null)
grid.removeColumn(GENERATED_DELETE);
}
}));
After pushing the button all renderer stop working.
I have test this with vaadin version 7.6.2 and 7.6.6 and with Firefox, Chrom and IExplorer.
And after scrolling down some line I get the same exception as in my project - maybe another issue:
java.lang.IllegalStateException: Renderer is not attached to any parent
at com.vaadin.ui.Grid$AbstractGridExtension.getParentGrid(Grid.java:4366)
at de.datenhahn.vaadin.componentrenderer.ComponentRenderer.generateData(ComponentRenderer.java:101)
at com.vaadin.server.communication.data.RpcDataProviderExtension.getRowData(RpcDataProviderExtension.java:395)
at com.vaadin.server.communication.data.RpcDataProviderExtension.pushRowData(RpcDataProviderExtension.java:383)
at com.vaadin.server.communication.data.RpcDataProviderExtension.access$900(RpcDataProviderExtension.java:65)
at com.vaadin.server.communication.data.RpcDataProviderExtension$2.requestRows(RpcDataProviderExtension.java:293)
when call grid.addComponentColumn, the property is a generated property.
when add filter of this property,like:
HeaderCell cell = filterRow.getCell(pid);
// Have an input field to use for filter
TextField filterField = new TextField();
filterField.setColumns(grid.getColumns().size());
// Update filter When the filter input is changed
filterField.addTextChangeListener(change -> {
// Can't modify filters so need to replace
GeneratedPropertyContainer container = (GeneratedPropertyContainer) grid.getContainerDataSource();
Filter oldFilter = null;
for (Filter filter : container.getContainerFilters()) {
if (filter.appliesToProperty(pid)) {
oldFilter = filter;
break;
}
}
if (oldFilter != null) {
container.removeContainerFilter(oldFilter);
}
if (!change.getText().isEmpty()) {
container.addContainerFilter(new SimpleStringFilter(pid, change.getText(), true, false));
}
it throws UnsupportedFilterException ,because ComponentPropertyGenerator.java does't support
modify the filter, .
patch code :
@Override
public Filter modifyFilter(Filter filter) throws UnsupportedFilterException {
return filter;
}
Chart components lose their size & visibility when their rows are not immediately visible in the grid. Any event that triggers a server request seems to cause these rows to become visible. Other component types (example combobox) do not seem to have this issue. See the attached image for what I'm trying to describe.
Steps to reproduce using the provided code example:
If you examine the attached "HiddenChartBeforeClickEvent.png" and compare against the attached "HiddenChartAfterClickEvent.png", I have underlined in the "After" image the markup attributes which are modified when the user clicks a row, thus causing the sparklines to become visible. Note the difference in the height & width attributes (changing from 0 to values >0) and the visibility attributes (changing from "hidden" to "visible").
Code used in a basic project UI class:
`protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
layout.addComponents(createGrid());
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
}
private Component createGrid() {
BeanItemContainer<MyPojo> container = new BeanItemContainer<MyPojo>(MyPojo.class);
for (int i = 0; i < 1000; ++i) {
container.addBean(new MyPojo(i));
}
GeneratedPropertyContainer gpc = new GeneratedPropertyContainer(container);
Grid grid = new Grid(gpc);
grid.setWidth(900, Unit.PIXELS);
grid.setSelectionMode(SelectionMode.MULTI);
gpc.addGeneratedProperty("chart", new ComponentPropertyGenerator<>(MyPojo.class, pojo -> {
return new Sparkline(130, 30, pojo.getNumbers());
}));
gpc.addGeneratedProperty("combo", new ComponentPropertyGenerator<>(MyPojo.class, pojo -> {
return new ComboBox("Test", Arrays.asList(pojo.getNumbers()));
}));
grid.getColumn("chart").setRenderer(new ComponentRenderer());
grid.getColumn("combo").setRenderer(new ComponentRenderer());
grid.setColumns("baz", "foo", "bar", "chart", "combo");
grid.addItemClickListener(
event -> grid.getColumn("chart").setHeaderCaption("Chart" + String.valueOf(++clickCount)));
return grid;
}
public class MyPojo {
String foo = "foo";
String bar = "bar";
int baz = -1;
final Number[] numbers;
final Random rand;
public MyPojo(int i) {
baz = i;
numbers = new Number[20];
rand = new Random(baz);
}
public Number[] getNumbers() {
IntStream.range(0, 20).forEach(index -> numbers[index] = rand.nextInt());
return numbers;
}
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public int getBaz() {
return baz;
}
}`
When changing row selection or scrolling components vanish and reappear when not using a generated property container
I am using a ComponentGrid for displaying search results with a Detail View.
First time the method is called all is okay. On the second time, I get the following error.
My related code
public void addResultsToTable(List<SomeData> resultData) {
grid.setRows(resultData);
grid.setDetailsGenerator(...)
grid.addComponentColumn("Details", someData -> createDetailsIcons(grid, someData));
grid.addComponentColumn("Document", someData -> createDocumentLabel(someData)).setExpandRatio(1);
grid.setColumns("Details", "Document");
panelGrid.setContent(grid);
grid.setSizeFull();
Exception:
java.lang.IllegalStateException: Renderer is not attached to any parent
at com.vaadin.ui.Grid$AbstractGridExtension.getParentGrid(Grid.java:4366)
at de.datenhahn.vaadin.componentrenderer.ComponentRenderer.generateData(ComponentRenderer.java:101)
at com.vaadin.server.communication.data.RpcDataProviderExtension.getRowData(RpcDataProviderExtension.java:395)
at com.vaadin.server.communication.data.RpcDataProviderExtension.pushRowData(RpcDataProviderExtension.java:383)
at com.vaadin.server.communication.data.RpcDataProviderExtension.access$900(RpcDataProviderExtension.java:65)
at com.vaadin.server.communication.data.RpcDataProviderExtension$2.requestRows(RpcDataProviderExtension.java:293)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:158)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:118)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:408)
at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:273)
at com.vaadin.server.communication.PushHandler$2.run(PushHandler.java:149)
at com.vaadin.server.communication.PushHandler.callWithUi(PushHandler.java:243)
at com.vaadin.server.communication.PushHandler.onMessage(PushHandler.java:503)
at com.vaadin.server.communication.PushAtmosphereHandler.onMessage(PushAtmosphereHandler.java:88)
at com.vaadin.server.communication.PushAtmosphereHandler.onRequest(PushAtmosphereHandler.java:78)
at org.atmosphere.cpr.AsynchronousProcessor.action(AsynchronousProcessor.java:199)
at org.atmosphere.cpr.AsynchronousProcessor.suspended(AsynchronousProcessor.java:107)
at org.atmosphere.container.Servlet30CometSupport.service(Servlet30CometSupport.java:66)
at org.atmosphere.cpr.AtmosphereFramework.doCometSupport(AtmosphereFramework.java:2075)
at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:571)
at org.atmosphere.websocket.DefaultWebSocketProcessor$3.run(DefaultWebSocketProcessor.java:333)
at org.atmosphere.util.VoidExecutorService.execute(VoidExecutorService.java:101)
at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:328)
at org.atmosphere.websocket.DefaultWebSocketProcessor.invokeWebSocketProtocol(DefaultWebSocketProcessor.java:425)
at org.atmosphere.container.JSR356Endpoint$1.onMessage(JSR356Endpoint.java:213)
at org.atmosphere.container.JSR356Endpoint$1.onMessage(JSR356Endpoint.java:210)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:393)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:494)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:289)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:130)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:60)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:203)
at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:198)
at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
It should be easy to add filters which filter not on the component (which does not support filtering), but the wrapped containers property which e.g. is a string.
The vaadin 8 port using the compatibility layer is done, but I would like to do a complete rewrite at some point. I am not 100% sure I will do that because Vaadin announced that they want to add component support to the grid with vaadin 8.1 making this extension probably obsolete
The standard-way demo should be still included in the demo app
With Vaadin 8 coming up, it is time to port the ComponentRenderer to Vaadin 8
Some thoughts about porting:
The goal is to make migration soft for current Users, but also to embrace Vaadin 8 concepts. A couple of possibilities come in mind:
When you want to use components in the body of the grid you may also want to use components in the header. Just a reference to the bug I just opened, in case someone has the same problems. .
First of all thanks for the great add on. As long the table is not sorted by a column it works perfectly.
But I have the issue, that when sorting the table descending by any column, the rendered component disappears. It is not in the DOM-Tree anymore.
When resorting the columns ascending they appear again.
Somehow the components are only rendered if the container is not sorted or if the sort is ascending only.
Tried it with the latest Version of Vaadin and this add-on.
As the Components are not in the DOM anymore it shouldn't be related to the browser.
Test performance and functionality in all browser and note limitations
Hello,
When I cick to a cell which has a component renderer the click event doesn't propagated to the grid. This is absolutely ok for buttons, comboboxes, checkboxes, etc. But for icons, labels and other non clickable component i think it should propagate click event to the grid.
An option to set if the component should propagate click event to the grid or not would be nice.
Thanks
Keyboard navigation should be supported
Vaadin: 7.7.10
ComponentRenderer: 1.0.3
Steps:
This occurs in every view that contains a grid which uses the ComponentRenderer addon.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.