{"id":23140857,"url":"https://github.com/raycar5/valv","last_synced_at":"2025-08-17T13:31:36.211Z","repository":{"id":38176131,"uuid":"157343873","full_name":"raycar5/valv","owner":"raycar5","description":"A tiny but powerful web framework built on top of lit-html and rxjs","archived":false,"fork":false,"pushed_at":"2022-06-23T03:18:17.000Z","size":1072,"stargazers_count":10,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-03-06T12:23:29.954Z","etag":null,"topics":["framework","lit-html","rxjs","typescript","web"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/raycar5.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"code-of-conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-13T08:12:21.000Z","updated_at":"2021-04-25T23:13:41.000Z","dependencies_parsed_at":"2022-09-02T11:42:11.798Z","dependency_job_id":null,"html_url":"https://github.com/raycar5/valv","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raycar5%2Fvalv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raycar5%2Fvalv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raycar5%2Fvalv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raycar5%2Fvalv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raycar5","download_url":"https://codeload.github.com/raycar5/valv/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230128500,"owners_count":18177599,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","lit-html","rxjs","typescript","web"],"created_at":"2024-12-17T14:11:52.144Z","updated_at":"2024-12-17T14:11:52.841Z","avatar_url":"https://github.com/raycar5.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# | Valv |\n\nA tiny but powerful web framework built on top of [lit-html](https://github.com/Polymer/lit-html) and [rxjs](https://github.com/ReactiveX/rxjs)\n\n## [Demo](https://valv-hn.firebaseapp.com/top/1) ([Source](https://github.com/raycar5/valv-hn))\n\n## Features\n\n- Easy ui design and composition thanks to lit-html\n- Powerful state management thanks to rxjs\n- Fast! (no traversing trees to see what changed)\n- SPA router in the box\n- 100% Web component compatible\n- Nice api for web animations\n- Tiny (10KB gzipped)\n\n## Naming\n\nDue to the extensive use of rx in this library I've opted for the following naming convention for the frequently used types:\n\n- Observables: observable\\$\n- Subjects: \\$subject\\$\n- Observers: \\$observer\n\n## Guide\n\nValv is all about controlling the flow of streams, whenever an event happens, it gets put into an observable and the job of the application is to build a pipeline with rxjs that turns that event into a UI update.\n\n**It is an absolute requirement that you understand rxjs in order to use this framework, so please read the documentation at [ReactiveX](http://reactivex.io/intro.html) if you haven't done so.**\n\nValv has 2 main parts, Widgets and Blocs:\n\n### Widgets\n\nA widget is a function that takes context, some props and returns a lit-html TemplateResult, it is wrapped in the `Widget` function for convinience.\n\n```typescript\ninterface MyWidgetProps {\n  text: string;\n}\nconst MyWidgetDefaultProps = { text: 'HelloWorld' };\nconst MyWidget = Widget((context, { text }: MyWidgetProps = MyWidgetDefaultProps) =\u003e \n  html`\n    \u003cdiv\u003e${text}\u003c/div\u003e\n  `;\n);\n```\n\nRendering a widget is easy!\n\n```typescript\nrender(MyWidget(context), document.getElementById('body'));\n```\n\nWidgets are composable.\n\n```typescript\n// Props can have any type you want\nconst BoldWidget = Widget(\n  (context, widget: Widget) =\u003e\n    html`\n      \u003cb\u003e${widget(context)}\u003c/b\u003e\n    `\n);\n```\n\n### Blocs\n\nA Bloc is any class that uses observers as input and observables as output, the framework does not enforce this, you can put any class you like into a BlocRepo but hopefully after this guide you'll see it's a good way to structure your code.\n\n```typescript\nclass MyBloc {\n  public readonly input: Observer\u003cany\u003e; // We don't care about input values, just that the event happened\n  public readonly output: Observable\u003cnumber\u003e;\n\n  // All the pipes are plugged together in the constructor\n  constructor() {\n    const subject = new Subject\u003cnumber\u003e();\n    const input = subject; // We only expose the Observer side of the subject\n    const output = subject.pipe(map((x, index) =\u003e index)); // Number will increase as events come through the input\n  }\n}\n```\n\nWidgets access blocs through the bloc repository or BlocRepo.\nThe code below displays a button and a number that increases every time you press the button.\n\n```typescript\nconst context = new Context(); // Context right now only contains the BlocRepo but might include more information in the future\ncontext.blocs.register(MyBloc);\ncontext.blocs.register(MyParameterizedBloc, new MyParameterizedBloc(32)); // You can also provide an instance\n\nconst MyWidget = Widget(context =\u003e {\n  const myBloc = context.blocs.of(MyBloc);\n  return html`\n    \u003cbutton @click=\"${eventToObserver(Mybloc.input)}\"\u003e\u003c/button\u003e ${awaito(myBloc.output)}\n  `;\n});\n```\n\n## awaito\n\nBut wait what is that function `awaito`?\n\nIt stands for await observable and it's the main tool that Valv provides, it takes an observable of anything that can be rendered in lit-html (text,numbers,TemplateResults) and whenever the observable emits a new value, the previous dom gets replaced with the new one.\n\nIt also provides a convinient map function as a second parameter if you want to construct the UI at the call site.\n\n```typescript\nawaito(\n  numberObservable,\n  number =\u003e\n    html`\n      \u003ci\u003e\u003cb\u003e${number}\u003c/b\u003e\u003c/i\u003e\n    `\n);\n```\n\n## Todo\n\n- A lot of Documentation\n- SSR\n- PWA facilities\n- more fancy animation stuff\n\n### Made with [typescript-library-starter](https://github.com/alexjoverm/typescript-library-starter)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraycar5%2Fvalv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraycar5%2Fvalv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraycar5%2Fvalv/lists"}