open-formulieren / open-forms-sdk Goto Github PK
View Code? Open in Web Editor NEWA Javascript SDK for Open Forms
Home Page: https://open-forms.readthedocs.io/en/stable/developers/sdk/index.html
License: Other
A Javascript SDK for Open Forms
Home Page: https://open-forms.readthedocs.io/en/stable/developers/sdk/index.html
License: Other
Part of the prototyping for a suitable formio renderer replacement architecture
The goal of this ticket is to explore rendering formio leaf nodes through a light binding with React (this means: exclude containers like repeating groups, columns, fieldsets..., which is part of step 2 if step 1 is viable).
The ideas to explore/validate here are:
Formik can be left out of scope for this experiment, as bullet 3 fulfills the requirements (since it listens to onChange
, onBlur
etc. which are standard DOM events).
Subtasks
<proto-date-picker ... />
)
day
, month
, year
YYYY-MM-DD
)<proto-date-picker name="myDate" value="2023-01-24" locale="nl" />
const WebComponentWrapper = ({formioKey, values, component, onChange}) => {
const currentValue = values?.[formioKey] || '';
const ref = useRef(null);
useEffect(
() => {
let mounted = false;
if (!ref.current) return;
mounted = true;
// pass necessary context - this is our public API
ref.current.ofValues = values;
ref.current.formioComponent = component;
// bind events
ref.current.addEventListener('change', (event) => onChange(event));
return () => {
if (!mounted) return;
ref.current.removeEventListener(...);
};
},
[ref],
);
return (
<proto-date-picker ref={ref} name={formioKey} value={currentValue} />
);
}
useEffect
hook to bind the relevant events for our own functionality)nl
/en
locale dropdown in the react state, which triggers a state/prop change and should be picked up by the webcomponent (arrange the date part dropdowns from day-month-year to month-day-year, for example,)We currently don't have all formio components hooked up to storybook, which excludes them from visual regression testing.
src/formio/components/Map.stories.mdx
We currently don't have all formio components hooked up to storybook, which excludes them from visual regression testing.
src/formio/components/Content.stories.mdx
Multi value inputs are wrapped in a table container, which now looks awful
See #336 (comment)
Primarily looking at the e-mail component for this, but it applies to TextField/NumberField too.
Font-weight of input box: in the form.io forms these are bolder
invalid styles are missing background color / border on the left
Check this for all components:
Expected behaviour: pressing Home resets the cursor to the start of the input, while End moves the cursor to the end.
Currently the keys have no effect at all.
There are a lot of act
related warnings now since the upgrade to React 18
On the backend we use black
to format. I think standard tries to be the same "no config" approach.
I'm using prettier
out of laziness; it also formats css, markdown and html.
But there are others:
Try the following fixers appropriate for the filetype:
'dprint' - Pluggable and configurable code formatting platform
'eslint' - Apply eslint --fix to a file.
'fecs' - Apply fecs format to a file.
'importjs' - automatic imports for javascript
'prettier' - Apply prettier to a file.
'prettier_eslint', 'prettier-eslint' - Apply prettier-eslint to a file.
'prettier_standard', 'prettier-standard' - Apply prettier-standard to a file.
'standard' - Fix JavaScript files using standard --fix
'xo' - Fix JavaScript/TypeScript files using xo --fix.
I have no preference other than consistency.
Git SHA: 835f4a26e82be4f6509432bc4ae6e3118e0bb93b
The page gets extra whitespace on the right, on mobile view,
this adds an extra horizontal scroll:
Solution: The element openforms-progress-indicator__mobile-header should get max. width: 100%
All elements in the page should have a maximum width that doesn't overflow. There should be no horizontal scroll.
All mobile views, starting from screens below width 765px
We currently don't have all formio components hooked up to storybook, which excludes them from visual regression testing.
src/formio/components/FileField.stories.mdx
component.url
and a mock is set up for this endpointThe following variants should be set up/documented:
The following controls should be available:
filePattern
validation (multiple checkboxes, you can find the default list of extensions/mime types here: https://github.com/open-formulieren/open-forms/blob/master/src/openforms/config/constants.py#L60For better NLDS support
Robbert has a POC of integrating Formio in Storybook and overriding styles with design tokens. Currently the hardcoded prefix sits in the way.
Very likely caused by the logic checking/updates. I've fixed this before where the props change while Formio is still (re)rendering and React doesn't like that.
Warning: unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering
Depends on #313
This will also require some backend changes to include meta-information about the logo (whether it's dark or light).
Tasks
of.login-button-logo.dark.focus.border-color
and of.login-button-logo.dark.focus.border-color
openforms-login-button-logo--{dark|light}
depending on backend information (TODO!)appearance
with possible values dark
and light
to LoginButtonIcon
component, which configures the modifier of the componentCheck if we can start gradual typescript adoption, normally CRA should support it out of the box?
Epic: open-formulieren/open-forms#2471
This is required for appointments to submit date-of-birth for which a datepicker is quite user-hostile.
Follow up from open-formulieren/open-forms#3060 to make the datepicker more accessible/easier to use
See also nl-design-system/utrecht#1746, nl-design-system/backlog#189 and nl-design-system/backlog#188
When using web-components (as in https://nl-design-system.github.io/utrecht/?path=/docs/form-io-component-readme--page), certain config options are required to be passed down to Formio components.
Relates to #52
Until currency it seems fine, but from that one the theme selector suddenly is no longer available and styling seems to break too.
Via #446 (review)
The sass does weird things because it's based on the card
component and on mobile spacing is different. It's also very contextual w/r to the font-size.
The combination of position: sticky
+ flexbox
+ having the expanded progress indicator overlay the rest of the page is challenging.
Additional input:
We currently don't have all formio components hooked up to storybook, which excludes them from visual regression testing.
src/formio/components/Fieldset.stories.mdx
We currently don't have all formio components hooked up to storybook, which excludes them from visual regression testing.
src/formio/components/Selectboxes.stories.mdx
Required for the backend 2.1.2 release.
.sdk-release
: open-formulieren/open-forms#2993How to reproduce:
This gives the error in the console:
Uncaught TypeError: can't define array index property past the end of an array with non-writable length
As an anonymous Submitter
When I let my Session expire
And I click here
And for some reason (time/server things) my CSRF token has become invalid
I do get the first login step
But clicking "Start formulier" does not start the submission, nor gives me feedback.
Analysis:
Backend correctly reponds with a 403 the POST:
{
"type": "http://127.0.0.1:8000/fouten/PermissionDenied/",
"code": "permission_denied",
"title": "You do not have permission to perform this action.",
"status": 403,
"detail": "CSRF Failed: CSRF token missing or incorrect.",
"instance": "urn:uuid:45c5dc8b-8240-4f3f-8553-94de91c5e995"
}
because the POST request contained these headers
Cookie: csrftoken=TrPftlIryvbjBGq63oh3Y7CnT0oVVUeeiPGOOS6hYOfND7lvJzQhfY7L3rMIYBkA; cookie_consent="analytical=2021-07-19T12:11:35.633000+00:00"; openforms_language=en; openforms_sessionid=lm1kgzn4k0sck8u8lof0osahlma4tcb1
X-CSRFToken: ksJHbv1k39EhazO9jROF6ZTbyMlPcTE2ss6yMNI6VsVbD7UuXAJJ2flUTMmFO7zu
and these tokens are not the same.
A refresh of the page resolves the issue.
Probably not related to open-formulieren/open-forms#2291
We currently don't have all formio components hooked up to storybook, which excludes them from visual regression testing.
src/formio/components/Signature.stories.mdx
We currently don't have all formio components hooked up to storybook, which excludes them from visual regression testing.
src/formio/components/Cosign.stories.js
This is about the "new" Co sign, we will not be documenting the CoSignOld
component.
1. we'll have to make sure that `groupLabel` is passed through `ctx.t(...)` in the SDK templates
Originally posted by @sergei-maertens in open-formulieren/open-forms#2501 (comment)
Together with typescript migration it might be beneficial to overhaul the toolchain
Requirements:
yarn start
must spin up dev serverSee #418 (comment)
De HTML dan de radio button in niet zoals de Utrecht component het verwacht.
Wij verwachten deze output:
https://nl-design-system.github.io/utrecht/storybook/?path=/docs/css-form-field-radio-group--label
Maar wij krijgen onderstaande als HTML output, dit gaat niet goed met CSS van het Utrecht component.
openforms-checkbox
en openforms-checkbox__checkmark
zijn wat onverwacht.
<div class="openforms-form-choices">
<div class="openforms-form-choices__choice utrecht-form-field utrecht-form-field--radio">
<div class="openforms-checkbox">
<input id="eyxrpxn-persoon" value="persoon" lang="nl" class="openforms-checkbox__input utrecht-radio-button utrecht-radio-button--html-input" type="radio" name="data[bentUEenPersoonOfEenBedrijf][eq8767r-eyxrpxn]" ref="input">
<div class="openforms-checkbox__checkmark"></div>
<label for="eyxrpxn-persoon" class="openforms-checkbox__label utrecht-form-label utrecht-form-label--radio">Persoon</label>
</div>
</div>
<div class="openforms-form-choices__choice utrecht-form-field utrecht-form-field--radio">
<div class="openforms-checkbox">
<input id="eyxrpxn-bedrijf" value="bedrijf" lang="nl" class="openforms-checkbox__input utrecht-radio-button utrecht-radio-button--html-input" type="radio" name="data[bentUEenPersoonOfEenBedrijf][eq8767r-eyxrpxn]" ref="input">
<div class="openforms-checkbox__checkmark"></div>
<label for="eyxrpxn-bedrijf" class="openforms-checkbox__label utrecht-form-label utrecht-form-label--radio">Bedrijf</label>
</div>
</div>
</div>
This will only build the changed stories depending on which files were changed: https://www.chromatic.com/docs/turbosnap#configure
Create a story in storybook that illustrates the error resulting from uploading invalid files. The story should facilitate testing for visual regression with Chromatic. To that end, the invalid file must already be uploaded when the user clicks on the Error flow.
Related: #416
Epic: open-formulieren/open-forms#2471
Now that we're using Formik, we can/should also implement client-side validation (using zod) for the appointment information. Certain aspects (like at least one product required, minimum amount of product is 1) cannot be properly validated using the form inputs and we should catch those errors before even attempting to submit data to the backend.
...but is a requirement.
The only mention of style-dictionary
in the documentation is in the configuration section of the backend briefly mentioning it as "advanced usage", I suggest adding to the list next to "NodeJS environment" in the Storybook documentation of the Open Forms SDK.
We use
style-dictionary
for design tokens, make sure it's installed.
To the storybook documentation of the SDK. Also I think "Prerequisites" is more meaningful than "NodeJS environment" when added to this particular list.
See https://storybook.js.org/docs/react/get-started/install
Currently, descriptions/help texts are lacking the lightish blue background and the vertical spacing is a bit off.
Tasks
The fragile mocks need to be replaced with actual semantic mocks that capture the behaviour of the API.
Once #367 is merged, we can use MSW for this.
Potential useful tutorial: https://www.wwt.com/article/using-mock-service-worker-to-improve-jest-unit-tests
The webpack config causes these to be loaded from a relative path to the page where the SDK is embedded, rather than relative to the sdk.js
module.
A quick fix right now is to always bundle these files instead of loading them dynamically, but this will make the bundle fatter as we add more translations.
Imports from 'react-use/esm' break jest testing.
Related to a pull request here #44 if a 403 response is returned from the server we only alert the user but do no other error handling.
Ideally we would show at least a better error response and possibly send the user to a proper page.
DOMException: Failed to execute 'setSelectionRange' on 'HTMLInputElement': The input element's type ('email') does not support selection.
This happens in the Component.restoreCaretPosition
method on Formio 4.12. It appears to be fixed in 4.13: https://github.com/formio/formio.js/blob/4.13.x/src/components/_classes/component/Component.js#L1564
This is currently causing the subsequent logic checks to fail (when the element is focused).
Preferred mitigation: upgrade to Formio 4.13, however we have run into other issues while trying that with the file upload mimetypes. Alternative solution is monkey-patching the method, as the Component
is base class for a lot of components and simply replacing it with our own subclass will not suffice.
Before this repo was created there were several styling files in the open-forms repo.
These styles should now be in this repo so the form components are styled properly
Minimal example with mocked API interactions
This will be beneficial to test/develop the alternative renderer.
We currently don't have all formio components hooked up to storybook, which excludes them from visual regression testing.
src/formio/components/EditGrid.stories.mdx
Testing with the following configuration:
{
"display": "form",
"components": [
{
"id": "en1l2p",
"key": "voornaam",
"mask": false,
"type": "textfield",
"input": true,
"label": "Voornaam",
"hidden": false,
"prefix": "",
"suffix": "",
"unique": false,
"widget": {
"type": "input"
},
"dbIndex": false,
"overlay": {
"top": "",
"left": "",
"style": "",
"width": "",
"height": ""
},
"tooltip": "",
"disabled": false,
"multiple": false,
"redrawOn": "",
"tabindex": "",
"validate": {
"custom": "",
"unique": false,
"pattern": "",
"multiple": false,
"required": true,
"maxLength": "",
"minLength": "",
"customPrivate": false,
"strictDateValidation": false
},
"autofocus": false,
"encrypted": false,
"hideLabel": false,
"inputMask": "",
"inputType": "text",
"modalEdit": false,
"protected": false,
"refreshOn": "",
"tableView": true,
"attributes": {},
"errorLabel": "",
"persistent": true,
"properties": {},
"spellcheck": true,
"validateOn": "change",
"clearOnHide": true,
"conditional": {
"eq": "",
"show": null,
"when": null
},
"customClass": "",
"description": "Geef je voornaam op",
"inputFormat": "plain",
"placeholder": "",
"defaultValue": null,
"dataGridLabel": false,
"labelPosition": "top",
"showCharCount": false,
"showWordCount": false,
"calculateValue": "",
"calculateServer": false,
"allowMultipleMasks": false,
"customDefaultValue": "",
"allowCalculateOverride": false
},
{
"id": "ezmx8ja",
"key": "tussenvoegsel",
"mask": false,
"type": "textfield",
"input": true,
"label": "Tussenvoegsel",
"hidden": false,
"prefix": "",
"suffix": "",
"unique": false,
"widget": {
"type": "input"
},
"dbIndex": false,
"overlay": {
"top": "",
"left": "",
"style": "",
"width": "",
"height": ""
},
"tooltip": "",
"disabled": false,
"multiple": false,
"redrawOn": "",
"tabindex": "",
"validate": {
"custom": "",
"unique": false,
"pattern": "",
"multiple": false,
"required": false,
"maxLength": "",
"minLength": "",
"customPrivate": false,
"strictDateValidation": false
},
"autofocus": false,
"encrypted": false,
"hideLabel": false,
"inputMask": "",
"inputType": "text",
"modalEdit": false,
"protected": false,
"refreshOn": "",
"tableView": true,
"attributes": {},
"errorLabel": "",
"persistent": true,
"properties": {},
"spellcheck": true,
"validateOn": "change",
"clearOnHide": true,
"conditional": {
"eq": "",
"show": null,
"when": null
},
"customClass": "",
"description": "",
"inputFormat": "plain",
"placeholder": "",
"defaultValue": null,
"dataGridLabel": false,
"labelPosition": "top",
"showCharCount": false,
"showWordCount": false,
"calculateValue": "",
"calculateServer": false,
"allowMultipleMasks": false,
"customDefaultValue": "",
"allowCalculateOverride": false
},
{
"id": "eyq2zns",
"key": "achternaam",
"mask": false,
"type": "textfield",
"input": true,
"label": "Achternaam",
"hidden": false,
"prefix": "",
"suffix": "",
"unique": false,
"widget": {
"type": "input"
},
"dbIndex": false,
"overlay": {
"top": "",
"left": "",
"style": "",
"width": "",
"height": ""
},
"tooltip": "",
"disabled": false,
"multiple": false,
"redrawOn": "",
"tabindex": "",
"validate": {
"custom": "",
"unique": false,
"pattern": "",
"multiple": false,
"required": false,
"maxLength": "",
"minLength": "",
"customPrivate": false,
"strictDateValidation": false
},
"autofocus": false,
"encrypted": false,
"hideLabel": false,
"inputMask": "",
"inputType": "text",
"modalEdit": false,
"protected": false,
"refreshOn": "",
"tableView": true,
"attributes": {},
"errorLabel": "",
"persistent": true,
"properties": {},
"spellcheck": true,
"validateOn": "change",
"clearOnHide": true,
"conditional": {
"eq": "",
"show": null,
"when": null
},
"customClass": "",
"description": "",
"inputFormat": "plain",
"placeholder": "",
"defaultValue": null,
"dataGridLabel": false,
"labelPosition": "top",
"showCharCount": false,
"showWordCount": false,
"calculateValue": "",
"calculateServer": false,
"allowMultipleMasks": false,
"customDefaultValue": "",
"allowCalculateOverride": false
},
{
"id": "ehm8k1",
"key": "email",
"mask": false,
"type": "email",
"input": true,
"label": "Email",
"hidden": false,
"prefix": "",
"suffix": "",
"unique": false,
"widget": {
"type": "input"
},
"dbIndex": false,
"kickbox": {
"enabled": false
},
"overlay": {
"top": "",
"left": "",
"style": "",
"width": "",
"height": ""
},
"tooltip": "",
"disabled": false,
"multiple": false,
"redrawOn": "",
"tabindex": "",
"validate": {
"custom": "",
"unique": false,
"pattern": "",
"multiple": false,
"required": false,
"maxLength": "",
"minLength": "",
"customPrivate": false,
"strictDateValidation": false
},
"autofocus": false,
"encrypted": false,
"hideLabel": false,
"inputMask": "",
"inputType": "email",
"modalEdit": false,
"protected": false,
"refreshOn": "",
"tableView": true,
"attributes": {},
"errorLabel": "",
"persistent": true,
"properties": {},
"spellcheck": true,
"validateOn": "change",
"clearOnHide": true,
"conditional": {
"eq": "",
"show": null,
"when": null
},
"customClass": "",
"description": "",
"inputFormat": "plain",
"placeholder": "",
"showInEmail": false,
"defaultValue": null,
"dataGridLabel": false,
"labelPosition": "top",
"showCharCount": false,
"showWordCount": false,
"calculateValue": "",
"calculateServer": false,
"allowMultipleMasks": false,
"customDefaultValue": "",
"allowCalculateOverride": false
},
{
"id": "encqyof",
"key": "iban",
"mask": false,
"type": "iban",
"input": true,
"label": "Rekeningnummer",
"hidden": false,
"prefix": "",
"suffix": "",
"unique": false,
"widget": {
"type": "input"
},
"dbIndex": false,
"overlay": {
"top": "",
"left": "",
"style": "",
"width": "",
"height": ""
},
"tooltip": "",
"disabled": false,
"multiple": false,
"redrawOn": "",
"tabindex": "",
"validate": {
"custom": true,
"unique": false,
"pattern": "",
"multiple": false,
"required": false,
"maxLength": "",
"minLength": "",
"customPrivate": false,
"strictDateValidation": false
},
"autofocus": false,
"encrypted": false,
"hideLabel": false,
"inputMask": "",
"inputType": "text",
"modalEdit": false,
"protected": false,
"refreshOn": "",
"tableView": true,
"attributes": {},
"errorLabel": "",
"persistent": true,
"properties": {},
"spellcheck": true,
"validateOn": "change",
"clearOnHide": true,
"conditional": {
"eq": "",
"show": null,
"when": null
},
"customClass": "",
"description": "",
"inputFormat": "plain",
"placeholder": "",
"defaultValue": null,
"dataGridLabel": false,
"labelPosition": "top",
"showCharCount": false,
"showWordCount": false,
"calculateValue": "",
"calculateServer": false,
"allowMultipleMasks": false,
"customDefaultValue": "",
"allowCalculateOverride": false
},
{
"id": "euqhli",
"key": "amount",
"type": "number",
"input": true,
"label": "Amount",
"hidden": false,
"prefix": "",
"suffix": "",
"unique": false,
"widget": {
"type": "input"
},
"dbIndex": false,
"overlay": {
"top": "",
"left": "",
"style": "",
"width": "",
"height": ""
},
"tooltip": "",
"disabled": false,
"multiple": false,
"redrawOn": "",
"tabindex": "",
"validate": {
"max": "",
"min": "",
"step": "any",
"custom": "",
"unique": false,
"integer": "",
"multiple": false,
"required": false,
"customPrivate": false,
"strictDateValidation": false
},
"autofocus": false,
"encrypted": false,
"hideLabel": false,
"modalEdit": false,
"protected": false,
"refreshOn": "",
"tableView": false,
"attributes": {},
"errorLabel": "",
"persistent": true,
"properties": {},
"validateOn": "change",
"clearOnHide": true,
"conditional": {
"eq": "",
"show": null,
"when": null
},
"customClass": "",
"description": "",
"placeholder": "",
"showInEmail": false,
"defaultValue": null,
"dataGridLabel": false,
"labelPosition": "top",
"showCharCount": false,
"showWordCount": false,
"calculateValue": "",
"calculateServer": false,
"allowMultipleMasks": false,
"customDefaultValue": "",
"allowCalculateOverride": false
},
{
"id": "elt1rlp",
"key": "extras",
"type": "selectboxes",
"input": true,
"label": "Extra's",
"hidden": false,
"inline": false,
"prefix": "",
"suffix": "",
"unique": false,
"values": [
{
"label": "Melk",
"value": "melk"
},
{
"label": "Suiker",
"value": "suiker"
},
{
"label": "Nog meer suiker",
"value": "nogMeerSuiker"
}
],
"widget": null,
"dbIndex": false,
"overlay": {
"top": "",
"left": "",
"style": "",
"width": "",
"height": ""
},
"tooltip": "",
"disabled": false,
"fieldSet": false,
"multiple": false,
"redrawOn": "",
"tabindex": "",
"validate": {
"custom": "",
"unique": false,
"multiple": false,
"required": false,
"customPrivate": false,
"strictDateValidation": false
},
"autofocus": false,
"encrypted": false,
"hideLabel": false,
"inputType": "checkbox",
"modalEdit": false,
"protected": false,
"refreshOn": "",
"tableView": false,
"attributes": {},
"errorLabel": "",
"persistent": true,
"properties": {},
"validateOn": "change",
"clearOnHide": true,
"conditional": {
"eq": "",
"show": null,
"when": null
},
"customClass": "",
"description": "",
"placeholder": "",
"defaultValue": null,
"dataGridLabel": false,
"labelPosition": "top",
"showCharCount": false,
"showWordCount": false,
"calculateValue": "",
"calculateServer": false,
"allowMultipleMasks": false,
"customDefaultValue": "",
"allowCalculateOverride": false
},
{
"id": "e0qy7er",
"key": "keuze",
"data": {
"url": "",
"json": "",
"custom": "",
"values": [
{
"label": "Optie 1",
"value": "optie1"
},
{
"label": "Optie 2",
"value": "optie2"
}
],
"resource": ""
},
"type": "select",
"input": true,
"label": "Keuze",
"limit": 100,
"filter": "",
"hidden": false,
"idPath": "id",
"prefix": "",
"suffix": "",
"unique": false,
"widget": null,
"dataSrc": "values",
"dbIndex": false,
"overlay": {
"top": "",
"left": "",
"style": "",
"width": "",
"height": ""
},
"tooltip": "",
"disabled": false,
"lazyLoad": true,
"multiple": false,
"redrawOn": "",
"tabindex": "",
"template": "<span>{{ item.label }}</span>",
"validate": {
"custom": "",
"unique": false,
"multiple": false,
"required": false,
"customPrivate": false,
"strictDateValidation": false
},
"autofocus": false,
"encrypted": false,
"hideLabel": false,
"minSearch": 0,
"modalEdit": false,
"protected": false,
"refreshOn": "",
"tableView": true,
"attributes": {},
"errorLabel": "",
"persistent": true,
"properties": {},
"validateOn": "change",
"clearOnHide": true,
"conditional": {
"eq": "",
"show": null,
"when": null
},
"customClass": "",
"description": "",
"fuseOptions": {
"include": "score",
"threshold": 0.3
},
"ignoreCache": false,
"placeholder": "",
"searchField": "",
"authenticate": false,
"defaultValue": null,
"selectFields": "",
"customOptions": {},
"dataGridLabel": false,
"labelPosition": "top",
"readOnlyValue": false,
"searchEnabled": true,
"showCharCount": false,
"showWordCount": false,
"uniqueOptions": false,
"valueProperty": "",
"calculateValue": "",
"clearOnRefresh": false,
"useExactSearch": false,
"calculateServer": false,
"searchThreshold": 0.3,
"allowMultipleMasks": false,
"customDefaultValue": "",
"allowCalculateOverride": false
}
]
}
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.