Skip to content

Commit e6830e5

Browse files
authored
Merge branch 'master' into test/add-swap-source-tests
2 parents 178c043 + adc7bd5 commit e6830e5

File tree

3 files changed

+168
-36
lines changed

3 files changed

+168
-36
lines changed

QUICKSTART.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
## Quick Start
2+
3+
### Installation
4+
5+
```bash
6+
# Using npm
7+
npm install rimmel rxjs
8+
9+
# Using yarn
10+
yarn add rimmel rxjs
11+
12+
# Using pnpm
13+
pnpm add rimmel rxjs
14+
```
15+
16+
### Basic Usage
17+
18+
```js
19+
import { BehaviorSubject, scan } from 'rxjs';
20+
import { rml } from 'rimmel';
21+
22+
const Component = () => {
23+
const count = new BehaviorSubject(0).pipe(
24+
scan(x=>x+1)
25+
);
26+
27+
return rml`
28+
<button onclick="${count}">
29+
click me (${count})
30+
</button>
31+
`;
32+
};
33+
34+
const App = () => {
35+
return rml`
36+
<h1>Hello <img class="icon" src="https://rimmel.js.org/assets/rimmel.png"> World</h1>
37+
${Component()}
38+
39+
<hr>
40+
Starter for <a href="https://github.com/reactivehtml/rimmel" target="_blank">Rimmel.js</a>
41+
`;
42+
};
43+
44+
document.body.innerHTML = App();
45+
```
46+
47+
## Key Features
48+
49+
- **Stream-First Architecture**: Everything is a stream - events, data, UI updates
50+
- **No Virtual DOM**: Direct DOM updates via optimized "sinks"
51+
- **Tiny Bundle Size**: Core is just 2.5KB, tree-shakeable imports
52+
- **Zero Build Requirements**: Works with plain JavaScript
53+
- **Automatic Memory Management**: Handles subscription cleanup
54+
- **Built-in Suspense**: Automatic loading states with BehaviorSubject
55+
- **Web Components Support**: Create custom elements easily
56+
- **TypeScript Support**: Full type definitions included
57+
58+
## Core Concepts
59+
60+
### Sources (Input)
61+
- DOM Events (`onclick`, `onmousemove`, etc)
62+
- Promises
63+
- RxJS Observables
64+
- Custom Event Sources
65+
66+
### Sinks (Output)
67+
- DOM Updates
68+
- Class Management
69+
- Style Updates
70+
- Attribute Changes
71+
- Custom Sinks
72+
73+
## Available Sinks
74+
75+
The library includes specialized sinks for common UI operations:
76+
77+
- `InnerHTML` - Update element content
78+
- `InnerText` - Safe text updates
79+
- `Class` - Manage CSS classes
80+
- `Style` - Update styles
81+
- `Value` - Form input values
82+
- `Disabled` - Toggle disabled state
83+
- `Readonly` - Toggle readonly state
84+
- `Removed` - Remove elements
85+
- `Sanitize` - Safe HTML rendering
86+
- `AppendHTML` - Append content
87+
- `PrependHTML` - Prepend content
88+
89+
## Development
90+
91+
```bash
92+
# Install dependencies
93+
npm install
94+
95+
# Run tests
96+
npm run test
97+
98+
# Build library
99+
npm run build
100+
101+
# Run demo app
102+
npm run kitchen-sink
103+
```
104+
105+
## Examples
106+
107+
Check out our examples:
108+
109+
- [Basic Demos](https://stackblitz.com/@dariomannu/collections/rimmel-js-getting-started)
110+
- [Advanced Patterns](https://stackblitz.com/@dariomannu/collections/rimmel-js-experiments)
111+
- [Web Components](https://stackblitz.com/@dariomannu/collections/web-components)
112+
- [Web Workers](https://stackblitz.com/@dariomannu/collections/web-workers)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Rimmel is a powerful, fast and lightweight JavaScript UI library for creating we
1010

1111
It implements [RML](https://github.com/ReactiveHTML/reactive-markup), the Reactive Markup which makes your HTML work with Streams in a seamless way.
1212

13+
## To Busy to read?, Check [TL;DR 📜](./QUICKSTART.md)<br>
14+
1315
## Getting started
1416
If you are new to reactive streams, there is a [3m crash-course](https://medium.com/@fourtyeighthours/the-mostly-inaccurate-crash-course-for-reactive-ui-development-w-rxjs-ddbb7e5e526e) tailored for UI development with Rimmel, arguably the simplest RxJS introduction around to get you started.
1517

src/sources/swap-source.test.ts

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
import type { Observable } from 'rxjs';
22

33
import { Subject } from 'rxjs';
4-
import { Swap, swap } from './swap-source';
54
import { MockElement, MockEvent } from '../test-support';
5+
import { Swap, swap } from './swap-source';
66

77
describe('Swap Event Adapter', () => {
8+
89
it('Swaps a value from an element with a static string', () => {
910
const oldValue = 'old data';
1011
const newValue = 'new data';
12+
1113
const el = MockElement({
12-
tagName: "INPUT",
14+
tagName: 'INPUT',
1315
type: 'text',
1416
value: oldValue,
1517
});
18+
1619
const eventData = MockEvent('input', {
1720
target: el as HTMLInputElement
1821
});
22+
1923
const handlerSpy = jest.fn();
2024
const source = Swap(newValue, handlerSpy);
2125

@@ -24,59 +28,70 @@ describe('Swap Event Adapter', () => {
2428
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
2529
expect(el.value).toEqual(newValue);
2630
})
27-
})
28-
29-
it('Swaps a value from an element with empty string by default', () => {
30-
const oldValue = 'old data';
31-
const el = MockElement({
32-
tagName: 'INPUT',
33-
type: 'text',
34-
value: oldValue,
35-
});
36-
const eventData = MockEvent('input', {
37-
target: el as HTMLInputElement
38-
});
39-
const handlerSpy = jest.fn();
31+
32+
it('Swaps a value in an element using a function', () => {
33+
const oldValue = 'old data';
34+
35+
const el = MockElement({
36+
tagName: 'INPUT',
37+
type: 'text',
38+
value: oldValue,
39+
});
40+
41+
const eventData = MockEvent('input', {
42+
target: el as HTMLInputElement
43+
});
44+
45+
const replaceFn = (v: string) => v.toUpperCase();
46+
47+
const handlerSpy = jest.fn();
4048
const source = Swap(undefined, handlerSpy);
4149

42-
source.next(eventData);
50+
source.next(eventData);
4351

44-
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
45-
expect(el.value).toEqual('');
46-
});
52+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
53+
expect(el.value).toEqual('OLD DATA');
54+
});
4755

48-
it('Swaps a value using a function that transforms based on the old value', () => {
49-
const oldValue = '5';
50-
const replacementFn = (v: string) => String(Number(v) * 2);
51-
const el = MockElement({
52-
tagName: 'INPUT',
53-
type: 'text',
54-
value: oldValue,
55-
});
56-
const eventData = MockEvent('input', {
57-
target: el as HTMLInputElement
58-
});
59-
const handlerSpy = jest.fn();
60-
const source = Swap(replacementFn, handlerSpy);
56+
it('Swaps a value in an element with empty string by default', () => {
57+
const oldValue = 'old data';
58+
59+
const el = MockElement({
60+
tagName: 'INPUT',
61+
type: 'text',
62+
value: oldValue,
63+
});
64+
65+
const eventData = MockEvent('input', {
66+
target: el as HTMLInputElement
67+
});
6168

62-
source.next(eventData);
69+
const handlerSpy = jest.fn();
70+
const source = Swap()(handlerSpy);
71+
source.next(eventData);
72+
73+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
74+
expect(el.value).toEqual('');
75+
});
6376

64-
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
65-
expect(el.value).toEqual('10');
6677
});
6778

6879
describe('swap Event Operator', () => {
80+
6981
it('Swaps and emits a value from an element with a static string', () => {
7082
const oldValue = 'old data';
7183
const newValue = 'new data';
84+
7285
const el = MockElement({
7386
tagName: 'INPUT',
7487
type: 'text',
7588
value: oldValue,
7689
});
90+
7791
const eventData = MockEvent('input', {
7892
target: el as HTMLInputElement
7993
});
94+
8095
const handlerSpy = jest.fn();
8196
const pipeline = new Subject<typeof eventData>().pipe(swap(newValue)) as Observable<string> & Subject<typeof eventData>;
8297

@@ -89,11 +104,13 @@ describe('swap Event Operator', () => {
89104

90105
it('Swaps and emits a value from an element with empty string', () => {
91106
const oldValue = 'old data';
107+
92108
const el = MockElement({
93109
tagName: 'INPUT',
94110
type: 'text',
95111
value: oldValue,
96112
});
113+
97114
const eventData = MockEvent('input', {
98115
target: el as HTMLInputElement
99116
});
@@ -107,7 +124,7 @@ describe('swap Event Operator', () => {
107124
expect(el.value).toEqual('');
108125
});
109126

110-
127+
111128

112129
it('Swaps a value using a function that generates new value from old', () => {
113130
const oldValue = 'test';
@@ -155,3 +172,4 @@ describe('swap Event Operator', () => {
155172
expect(el.value).toEqual('replacement');
156173
});
157174
});
175+

0 commit comments

Comments
 (0)