Good morning everybody. So my name is Florence Bria. I'm a UI
principal engineer in Amadeus. It's a big company doing software for the travel industry.
And I'm here to present Tansu, which is a framework library, a reactive library, Agnostik,
that we are using in our new widget library. So a bit of context. My team is doing a lot
of widget libraries since many years, like more than 15 years. And we are of open source,
a lot of them. The last one, which is very popular, is MGBoostrap. We've up to 2 million
downloads per month. And so we are kind of developing a new one, Agnosti, which is using
Tansu. And I'm going to explain what Tansu is. So first a bit of Tansu, then the features.
Then we will see Agnosti in a few slides. And then we will finish by why Tansu is helping
us a lot by developing this Agnosti library. So Tansu is a senior library. So it's a
library to do a state management in a reactive way. And there's a lot now of frameworks that
are implementing such a library. Okay, so we have Soligys, which has also a senior library,
Angular. Now since last year, as a senior library, Quick from the start, Svelte Store,
is also some kind of solid library. Vue is having a reactive system also. React has
a preact signal, which is also from Agnosti, by the way. And we have developed ourselves
on open source Tansu. Tansu is basically based almost on the Svelte Store, but is independent
from Svelte. So let's dig into Tansu. So Tansu is providing a few state management
such as Primitive, so for best store. The most important one is the writeable. So you
can create a store by using the writeable and give it an initial value. And then on this
store, the idea is that anyone can subscribe on the store to get the actual value of the
store. And on the store, there's a set method and an update method to change the value of
the store. So the set method, you provide the value directly. The update method, you provide
an update function where you get the current value of the store, and then you need to return
the new value of the store. So this is the primitive for our library. Then we have a way
to extend our base primitive, like the writeable. The writeable is taking any store-like object
and is transforming it into a writeable. So it is adding basically a set function if you are not
providing it, but you can provide your own custom function like we are doing here for the base store.
We are providing a set function that is basically setting the double of the value put in the set.
And you provide also, you can increase the store by providing some API, like the increment function
that we have here that is incrementing the value of the store. Like any other store, the
writeable, you can subscribe to it and you can use all the API, including the set and the increment.
Then we have a writeable. A writeable basically is a writeable, but we found a set and update method.
It is just for us to provide a way for people to subscribe to the value, but not interact with the value.
Same as for writeable, we have as readable utilities to transform a store and add some API to interact
with the store and return a readable. So for example, here we have a writeable as a private store
and we return a readable store where we have an API to interact with the current store.
Then we have some computed store. So the computed store, they are a dependency on other store
and they are going to return new value depending on those primitive value.
So the derived store is taking, you need to provide when you are creating a derived store,
you need to provide a list of dependencies. And then you have a function where you are going to return
the actual value of the store. So for example, here we have x dollar and y dollar which are the dependencies.
And we have the function that is going to take the value of the store, the two stores, x and y here,
and we need to return the new value. So here we are summing the two values.
The return of the derived is already readable, so you can subscribe.
And as soon as there is a change in a value of one of the dependencies, the derived function is computed again.
And it is computed synchronously. So this is important for us because we want to be able to plug in any framework,
our library, so the best way is to be synchronized so that it has no side effects when we are plugging into any framework.
And it reacts but on the other hand.
There is another API to be able to handle a source and conus event with the derived.
So we have the way to provide on top of the dependency a set function.
And then it is up to us instead of returning the value for the derived, it is up to us to use the set function
so when we want, change internally the value of the store.
In this case, we can also provide an initial value because we don't know if the set is going to be a code synchronously from the start.
Then of course the derived is also readable, anyone can subscribe to get the actual value.
And if there is a dependency that is changing a value, the derived is run automatically.
Then we have another so computed the store which is computed.
This one is automatically computing the list of dependency.
So if you are using any other store inside your function that is computing the value of the computed store,
in fact, the dependency will be kind of known by the library.
And if any dependency is changing its value, the computed is going to be executed.
What I didn't tell you also is that the writable or the readable,
they are also function that you can call to actually get the value.
So here when we are doing like XC$ and parenthesis, so we are calling the basically writable and we get the actual value.
So the computed here, if you are using any store inside the computed function,
we will know if one of the store is changing its value and we can react to this.
Thank you.
In a change on first name and last name, the derived function will be executed.
But I don't want to have intermediate change if the data will not be constant.
So here if I am changing first name and then last name, if there was no batch,
like we would have computed within the derived, shall occupy in our case.
And with the batch, we are able to kind of stop the reactivity system.
And until the end of the batch, nothing is basically happening, nothing is computed,
and shall come, this is only send at the end of the batch.
So the data stay consistent.
And it's pretty important for our library to handle, because you don't want to send data to the UI that are not consistent.
So what are the time-sensitive features?
It's from what I've just did, so it's critton and type script.
We have no dependency whatsoever.
It's synchronous, like I said, all the derived functions, all the computations are called synchronously.
So if you subscribe to a store, you are called also synchronously.
It's lazy.
Lazy means that basically if the store is not subscribed, if there's no subscriber to any store, the computation is not going to happen.
So we are not doing any computation that doesn't have any meaning, because there's no subscriber, basically.
So we are only computing if there's at least one subscriber on the store.
And there's a feature that I didn't talk about, which is the unused.
It's pretty interesting for our use case, which is doing widget libraries.
The unused is a possibility, so on every basic store, you have the possibility to provide an unused function.
This unused function is basically executing the first, when the number of subscribers goes to 0 to 1.
So we are able to initialize things only when the store is used.
And this unused function is returning a function that is executing when there's no more subscribers, so when the number goes to 1 to 0.
So this is pretty interesting to put your application in a state, like put some event listener, only when actually the application and the store is used in your application.
It's live in the page.
Something that I also didn't talk about is that when you are subscribing to any store, of course at some point you need to clean up and you need to avoid memos.
You need to unsubscribe also to the store.
So every subscription is returning a function to unsubscribe.
So here is the example.
So what about Agnes UI and what's the context of where we are using Tansu?
Agnes UI is a widget library.
Actually, it's more than one widget library.
Agnes UI is actually kind of seven libraries.
There's one library that is the core that is framework agnostic.
That is only written in Tanscript.
It's basically a bunch of builders of factory, let's say, component factory and utils factory.
So it's function that takes some props, some inputs, and what are returning, they are returning the older state of the widget and all the action possible that the widget can trigger.
So if you have a pagination, if you click on the next button for the pagination, it will trigger a change in the internal state management returning by those builders.
And basically, a new data will be computed for you so that you can show it on the screen.
So Agnes UI does this core that is framework agnostic.
And then we have, let's say, put some wrappers, some utils to be able to use it in multiple frameworks.
So we have the possibility to use it as headless.
So without any CSS, we basically do CSS of your choice, in React, in Svelte, and in Angular.
But basically, you can use it in any other framework.
It's just that we have only the time for those three and only the user of those three.
We also are providing because, well, we are maintaining ngboostrap.
And so a lot of our customers, even internally in my company, are using Angular, so ngboostrap.
We are providing also the bootstrap wrapper.
So this is the look of basically the bootstrap component here.
So in some of Agnes UI, it's framework agnostic also.
So there's a core framework agnostic.
It's reactive because we are using Tonsu, we will see how.
There's some wrapper bootstrap.
You can have a less component.
You can use Telwin to customize it.
We are going to show them in the website using Telwin on some kind of CSS of our choice.
It's highly customizable, highly configurable.
So you can basically, the activity is also put in place for the customization and the configuration.
So you have some kind of configuration mechanism that is injected inside our core.
And that you can use specifically.
And it's API-copied, meaning that all our components in every framework, they have the same API.
Okay, same props, same output, same behavior.
So for our company, for us, like where we are using multiple framework,
and maybe we want to have consistency inside our product,
it's quite useful to have the same look and feel for every application that we are developing.
So what's the usage of Tonsu and NSU?
And how it is helping us developing those libraries?
So it's helping us for many things.
And we will see that we need, in fact, all the features that we have seen before.
So for example, we have a create and accession details.
Okay, and the idea is to be able to know, we are giving like a list of elements,
and we want to know if those elements are visible inside,
are entering inside the screen.
Okay, and we are using the intersection observer.
So the intersection observer is a synchronous JavaScript utility that you can use
that is going to call us every time there's a change inside what we are observing.
So here, I didn't show you, but we are then observing the different element.
Okay, so every time the intersection observer basically is sending us some new entries.
Okay, we are changing this map, and what we are returning basically is this map in the visible element.
So we are using the fact that with the derive, we have a set function
that can be called as a chronosly by us, to put the new value of the visible element.
And with this, we are transforming like this intersection observer in a change of the state
and the visible element, visible element, dollar state.
Okay, and we are doing this in a framework or a nostic way,
so we can plug these utils in any framework or for choice.
One other thing that is also very interesting is that we are doing with a library
and people, when we are doing widgets, there's like a props or anything.
Here we have the case where in a pagination widget, okay, and we are computing the page number.
So the page number that we need to display on the screen, and it depends on two things.
It depends on the page size, so the number of items you want to display on the page
and the complete list of items that you have in your collection.
Okay, if with Tamsun, we are able basically to write that the page count is computed out of this expression.
Okay, and if any of the dependency is changing, this page count is going to be automatically computed again.
Okay, so we don't have to say, okay, if collection size is changing,
I'm going to do this computation, this computation, this computation, and it's going to change page count.
And if page size is changing, I'm going to do this computation, this computation,
and page count is computed again.
So you have a way to basically declare your state, your internal state of the widget in a very simple way.
So the thing we are going to see about Tamsun and our usage in AgnusRide is the unused.
So the unused is a pretty interesting thing that we see on Zvelstore, but not on a lot of other scenarios.
I don't think there are other scenarios that we have in this feature.
It's the way, like we said, to execute some code only when the store is used in your application.
So for example here, we are having the red-head ball of the active element.
Basically, on use means if some point in my application I'm using this store,
this function is going to be executed, and I'm going to start listening to the focus in and focus event
on this document or document element.
So I'm going to be able to react to those events only if the store is used in my application.
And then if the store is no longer used in my application, I'm going to unsubscribe directly to those events.
So basically you can set up things and remove things only if they are actually used in your application.
And it's quite difficult to do this if you don't have such utilities.
And also it's transforming everything that we are computing here at some point as it is red-head ball in a store
so that it can use in a reactive way anywhere in your application.
Okay, what's next?
Good.
So what we want to do with Agnes-Wide, but basically we want to extend the component list,
we want to extend the utilities, add more documentation for user, contributor.
We hope the people from MNGBusap will migrate and we hope people also will start using it in many other framework community
because now we are not only on Agnes-Wide but we are targeting React, ZVED, or any other possible framework.
So we are starting to advertise it and we will help people contribute to it.
So have a look at the two websites, of course, they are open source,
they are mighty lessons, you can use them for your design system or for whatever project on your own.
So thank you.
If you have questions.
We have a bit of time for questions, so if anyone has a question raise your hand.
Perfect.
I think you have a suggestion, you said you decided to build your own signal library when there are already existing ones.
Is there a specific reason?
Are there some needs that weren't publicizing the library?
So the question is, are there some needs that are not present in other libraries?
Yes, indeed.
So only pre-act signals is framework diagnostic.
On the whole list I showed you.
All the other signal libraries are included inside the framework but are linked to the reactivity system of the framework.
So we did one which was framework diagnostic to be able to code only the core in a framework diagnostic way and use it in any framework for choice.
So we had not a lot of choice.
We basically started before pre-act signal I think also.
So we have the Tansu library since a bit more than two years.
So yes, this is much it.
After, why did we don't choose pre-act signal?
So there's a lot of features that is not present on pre-act signal.
For example, the unused is not there.
They have the batch function.
I don't think they are lazy.
I don't know.
Maybe yes they are lazy.
Well, it's close to what we have.
We thought a few features that were missing.
But before we started before them, at least we have open source I think before them.
And we need them at the time.
So there was no other scenario that fits on it.
Yes, so Mobex is quite interesting.
So we had a look at Mobex.
It's quite verbose for me.
It's interesting, but I don't know if they have the core that is basically framework and the stick and they are using the yes.
So we had a look at the time, but maybe it was too many years ago.
And when we developed Tansu, basically it was we developed not Tansu for NSUI.
We developed it before.
And the idea was because we were contributing to the Angular framework at the time.
Angular was missing some scenarios of library internally.
So this was a project to basically implement step management in all our applications,
but also try to have a similar library that could fit our Angular need.
So we are not really interested in having this library where we need to use it.
We use a different framework and the different flavor.
I think in Mobex, there's not a core that you can use for not having a stick one.
So maybe we'll use this one.
But I was not so...
We played with it and we were not convinced at the time.
Any other question?
Just a second.
If the next speaker, Diklan, can come and start sitting up.
Sorry.
So thanks, the talk.
And the question is, do you, in any kind of...
...like performance testing compared to other solutions in the market?
benchmarking, whatever?
Well, performance testing is always quite complex.
Nope.
In a sense that it's quite difficult because you have always a framework that operates in the other framework.
So what should we focus on?
Which performances?
What we try is to be as lazy as possible.
We are lazy basically.
So in terms of computation, we should compute only what we need.
We have also this on use setup that allows you to only compute things only when there's a subscriber on the store.
So we think we are pretty efficient, but we have not done kind of...
...complete performance testing.
We are synchronous, so we are pretty fast.
Also, in terms of when there's a change on the store, it computes every logic to compute all the derives and it's sending directly the data to the subscribers.
It's a pretty small laboratory that insures itself.
Yeah, we have time for last question if there is one.
Just a reminder, try to always go to the middle so that others can also sit.
And we can't be standing on the sides.
That's a rule from the firefighters.
So we really can't have anyone standing on the sides.
There's a lot of last question.
Yes, you can ask a question.
Yeah, so we are thinking about providing a side effect function that is basically just subscribing to have the side effect run.
Yes, indeed.
Currently, we didn't have the use in our Orang Nostra laboratory, so we didn't implement it, but we were always talking about this like one month ago.
So this is something we will have maybe in the future.
Otherwise, yes, you need to subscribe to it to get its side effect.
Thank you.