From 8a968ee212ed272279c5375f48c815d7a6f60fbd Mon Sep 17 00:00:00 2001 From: marlon-erler Date: Mon, 8 Jul 2024 13:06:40 +0200 Subject: [PATCH] update --- static/bun.lockb | Bin 0 -> 11053 bytes static/node_modules/bloatless-react/README.md | 69 ++++++-- static/node_modules/bloatless-react/index.ts | 152 +++++++++++++----- .../node_modules/bloatless-react/package.json | 2 +- static/package.json | 14 +- 5 files changed, 183 insertions(+), 54 deletions(-) create mode 100755 static/bun.lockb diff --git a/static/bun.lockb b/static/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..70622aeaf7bdabf245d45c3192d21e417b3d8730 GIT binary patch literal 11053 zcmeHNd0bP+_78Yj1Y}CDPw1Cs66IxRvH zHEx!&4&=vZ?K!)-tuA7!_O%zC`6i$f?8hqzvLBRbc-<6r!0QA8g%!@B(jp0ZU>H>b zAx#04YM>mIAQ03+*$GP6eiyG_0Hr3Vp8zH7w-@K9V>C?S3sC}F=sP-=iO8|NqD{0O}614<|l3W5?SIUH&vyhfyK=(s3R zX0Y7G>qc4O_ve#kRh;HkNbI`NIFhE9a(Gov?em?-)wDcb=Pc+mjLFW;B&&S0`bpKw z@Jy`us}L3Yq^>>efkNkz>Tu2R$x3?aBec+i@9yoabTLt#V=1xqX<>7rvQFKc9`7P| zY7o=eIPc(z)R=zR*>~I0qued_{8CG;bU1D$9oKo8(C=Z?Be`&jmRrw(Qwa^`uDK!Q z(n^vCrkhv&t0y90>e!gY$|f7iN~b&XX6UFKYm`~B*S$OL@}oyTO<+fkp z_wCdBy_VdWt$Q7~Pb`A73X(SX*y8>J_vxbv$yH(SN)`Khyg!S!3UmWkp4fH(1+8xR`9<5yX||c z^j6#;M#xv(OMoXq;Nc2GWA_4E?w=W<)$7C#=W1L$WcqruZ%XH0s@*%I0;}2!i$;hk zjhAZ{Pm7J-yS8!KGQX{zCD9$JeTx>4O}@M6bBWk&XIRs%8E8 zZ)fdso;$$RSC)_l0KqZNC#>&cpssIDe7{7~z=HW4&(54>-Rx7nyOoO`9#4?Iu+{dI z%*s4($}FA2vHKy)#&UDj)Vl7TlQC%6=&j@GzN6oYi;Hn_h1VmtdwZIEQ<7Z5A6GZ@ z8>-uHNq^McaO;77MLVbIP;2d4o7bI7AC#Z{ZqwvjJ5_Q^b7@z%7?d=*o*22Qpi7#4 zzAT%IOORa%GzG^NpD^spBUtncnaTFVfKDy*llNW*QO;D{HfuW8{(D>Bg?2^1X&X3+ z*ClWM+Bnqmbl$u}+mxfXNMG={GHAL&y~NPb4#(98h~PNj6V{vGrMqX~j-~TY!Jg`e z3?+`9)zm%~6rAwYlG6LK{yM42M_T1ZYqd!knmeA1{&HO^=G?GjlhJC;K98)(+ukwK zxF7TZBG{(+gq?l&*iPN$KYMnm_OyA`#hJlu`Qs<#wzzxb z9{if_*lX}z=-nq%eo(4YwM{PT3SVqhg6j(b!S=@|Y@O<*P}`&ZKECH7=jVO#ZbqUV zDfuyG)X?>-M|H7L>(jT1SAMX`*cLvi)#%jtrNfTtzoq6~&pbV;`m9&aCXEId)PsLn zWq=5_MLuD%$zHA+n31bmnu7h;?)N4=Nxcy0$}}_#9JpPUIWg3@s{E-Q<7k`N1A5!! zoTy1buOFFsNtnO!e$l(ESBH{2pNA_6afSC;Y>i#{Xv@OtIUN(U@UiVnoel?5vXwJ- z_-;Rg?f*?DcaLMR$@u6x=NS(Zj5a>}UUk1_*6L=h!zDmmdA_#b z44CmnfNk0Q6}rU8*Z;^>c(-y!x0;dOKhJ3uEAQHt?!H~8KP#k2X2|kZj_GE{ht^&( zYag|gwh!J6r0SC=Mji)vaB*lxyH!H5X<$iUUo@EzJ-I0CbfZvfi2g1#= zxXhvI@!UysH4hxQrnvX({?6y?QcKwjG)aBB$LZ`_;9sLV{oEh-D>NxBEI0Hg%&#FAEL0>;{M6@_sP*yhRm&5MePvfK>x&7S4xTDOkvit2J%kg?C?<&?4J!XS-Z#}!f+ zy&S#QysAgWWy@GZxRfy`C@iC?-2d0`J}#~-*AL*?mQR@Txyt393Dy;|`y-PZ*4EG3 zZ%nNj^!if8KQG6tQHrysq}w}%=&*e3oxH5UJ@>&E*$?mQ=`B|7a43-WZj@WSf{TlB zapAo~gzX4ipUE&(I~{f~p-)vYXynkob*mW@)5pqW1J>UDIsIWk#!cgpS0fw#s0t|_ zZozyloG!(hDw->)DSCPzs^2)Biz~^+RY9Pj#5`T(msk+(0xM0&58s$F@vOnAE5j?@ zM|}h9?ba#Z8(+M1Fx`B0qh7UC_cQ;YT2`q4el@?8sjZE6tIgW%oE?gI{*{Gm4Ls}f z30qsxQhnJ}$-B3GOybtn`tpH8O5-_mlrw9d)U~7-1jjUA{^yuZW&5+G$0lO0BzpwS zi;GLWQM>ev^P2h}Hopw4#Qgwq!Mg@NVRvKqt(n`=IhI@hU2pg4#L%|j3l=LSb{^Sy zw_D=2wUf18=Gxc2sdD7JndfDaHvOhnXkhC_JGI$#ef?K^tiM}~)-l|B!8;8;VfuCU z$7V9j!68$2$f;PvULa#EV_g|lA{Xxg&O55q?!K(YI#WKHOCmljd8jDp7 zh7GRI4G&Tsh~9$h3vt0a5k6s|l`psa*2p0a>m5+s(E9eL{3%tNHa$~*rs6`)>?mAS z>X&yiq)fq*qTSe$pZLJ7acjr;kHuj_TYuFEdKC09MlCtt`fPg4>bj_lL}pE>Y*}4ddR)fyD#ve=S~K?*)efpAj9!aW zAj%&+g6j)$h2Je;s!y)^Y$2=~wmYuH%o>)jdTFbvy1ZlKvY}U}2r5NdB(s%$&tChE zB=+>wB}ygiHvVSg**kyi9!uRnC(FZ5r-!%y@(yJf`ys+ci?-{Kvl=%RC@;;TMK-;n zcRKx8Xfn6WTq`5*;nP{PP3vOyN|Nq+wbEzB?`zR6tP3n+JCm&0lafijihjvsTz%8H zXZ$n-3QFuz?S*LR%9IAeaD|CXjoz9ery2gby&bPN2im%AQ@1L`rk7RF`xq5|eUBVH z$v3mtPIlD#MsnOI=_mEjg~mpOTwGpUg5pyMj~S(R@0qp4>5f&+jrgBa*kNiX6I1LN zODNX)8U{&nz3VLpzdd;=EwS&CjoXQW6{$G`cXqs)vOa0xl=;-I*zT|Mgm8u9M(ovk z!ci6Z)AQ@JPrq96q_$n9G{h+T@C1KI=ag7!f>p}o*w-MBtS z^+5%YoI@blCWvDPoNmMSnZ*ZA2mQd9X()duDG_WzvLIOsGOs8@DJe+{V^dgc0>KB! zz?>$^{b3U3B(f#RoIr>G9AH%%$_@%RiGTy_PDA-a0Vf-mK^aB?rx0*pZWZMq1)MU# z0iH`kSxN!t8a@unV+uGZgNrhq0?r_if%c$WsDOjAz$jZP;GjG)%BKoAD3gpbt^y9q zHKUxYfCD31=r@$L6>w0#8s&8b9F);UnO^}1<-AdDSinJ9ag;q4a8TYH<(CB*2sZb361<-(e8pzRv+T{xn3QNLi_Kh5qQ}R@ut*GMv>EqgxVs*n2A(66#jFgaK9+9J%_WnqX>0G zt-?KNR5~Z=J#F|cLXx~TYM3RNuQacT#$eE+|I{qcz^qR>9*{Jtl608AQ} zEX(=kNgqfHYfY!Hp?ShhjCczD&nBZ*;hroGoxyrf7WmYX{DD__ZSR@PYXVUJVOsE& z=641&gnunGM?M|{>c9tq6_HT5bA}JYt$iX6-){hgfBlIVLiYop2-QTyK~^CoQS1^- zCZLj_@`)PprZbov0EB-n5(anj^HI1pk#O*(#Ye*HB4KftE+2_o6A1_H416fmAQBqB z`|!c=x=2{~D&yTc5mp&b5Y0!Snn>CJ9KKcH`V%pPHhO>}R1*;gS%r|e^-sjYk1pN< z5mZD&K?(_j17m4|ZvX%Q literal 0 HcmV?d00001 diff --git a/static/node_modules/bloatless-react/README.md b/static/node_modules/bloatless-react/README.md index c1f3772..5630017 100644 --- a/static/node_modules/bloatless-react/README.md +++ b/static/node_modules/bloatless-react/README.md @@ -7,7 +7,7 @@ Bloatless-React is a very minimal and flexible alternative to React. - Supports reactivity through States - Supports JSX for components - Written in TypeScript -- Really minimal (under 200 lines) +- Really minimal (under 300 lines) - No bloated server or compiler required - just bundle the JavaScript into a static .html file # Setup @@ -70,7 +70,7 @@ summary.subscribe(console.log); ## ListStates -A ListState\ is a State whose value is a Set\. A ListState allows specific subscriptions to detect when items get added and removed. This allows dynamic lists to run efficiently. +A `ListState` is a State whose value is a `Set`. A ListState allows specific subscriptions to detect when items get added and removed. This allows dynamic lists to run efficiently. ```TypeScript // Import @@ -78,7 +78,7 @@ import * as React from 'bloatless-react'; // Define Item type class Item implements React.Identifiable { - uuid = new React.UUID(); + id = React.UUID(); constructor(public text: string) {} } @@ -99,6 +99,17 @@ listState.add(newItem); listState.remove(newItem); ``` +## Persistence + +States can persist through reloads via LocalStorage. To implement this, modify your code like this: +```diff +-const myState = new React.State("hello"); ++const myState = React.restoreState("my-state", "hello"); + +-const myListState = new React.ListState(); ++const myListState = React.restoreListState("my-list-state"); +``` + # UI For a minimalist stylesheet, I'd recommend checking out my project called [carbon-mini](https://github.com/marlon-erler/carbon-mini/). @@ -107,28 +118,38 @@ Bloatless React provides a modified polyfill for the React API. This means that ## Handling Events -The `on:` directive adds an EventListener +The `on:` directive adds an EventListener. This directive also supports the `on:enter` event. ```TypeScript + ``` ## Changing Properties -The `subscribe:` directive subscribes to a State and changes the element's property. The property will be changed through the DOM API, so you can use innerText and innerHTML. You can not use this to set attributes. +The `subscribe:` directive subscribes to a State and changes a property of the element. Use this for properties available via the DOM model (ie. innerText, innerHTML). ```TypeScript const name = new React.State("John Doe"); ``` +## Setting Attributes + +The `set:` directive subscribes to a State and changes an attribute of the element. + +```TypeScript + +``` + ## Toggling Attributes -The `toggle:` directive toggles attributes on the HTML element without assigning a value. This is useful for the `disabled` attribute. +The `toggle:` directive toggles attributes on the HTML element without assigning a value. This is useful for the `disabled` attribute. You can use states or normal variables. ```TypeScript const isDisabled = new React.State(false); + ``` ## Binding Input Values @@ -151,14 +172,16 @@ import * as React from 'bloatless-react'; // Define Item class Item implements React.Identifiable { - uuid = new React.UUID(); + id = React.UUID(); constructor(public text: string) {} } // This ListItemConverter creates an HTML element based on an Item -const convertItem: React.ListItemConverter = (item, listState) => { +const convertItem: React.ListItemConverter = (item) => { function remove() { - listState.remove(item); + // you should keep track of your data in a separate model + // keep your removeListItem() method there + myDataModel.removeListItem(item); } return ( @@ -204,4 +227,30 @@ document.body.append( - Improve documentation ## 1.1.3 -- Add `on:enter` directive \ No newline at end of file +- Add `on:enter` directive + +## 1.2.0 +**BREAKING CHANGES** +- Replace UUID class with function; + - `new UUID()` => `UUID() returns string` +- Replace definition of `Identifiable` + - now has `id: string` instead of `uuid: UUID()` + +Other changes: +- Add State persistence +- Add error description when utilizing `subscribe:children` incorrectly +- Fix bug where ListState subscriptions were not called +- Improve documentation + +## 1.2.1 +- Add `set:` directive + +## 1.2.2 +- Remove list parameter from `ListItemConverter` +- Add `clear()` method to `ListState` + +## 1.2.3 +- Allow `toggle:` to be used without a state + +## 1.2.4 +- On `subscribe:children`, scroll new elements into view \ No newline at end of file diff --git a/static/node_modules/bloatless-react/index.ts b/static/node_modules/bloatless-react/index.ts index f96c379..76f5ca0 100755 --- a/static/node_modules/bloatless-react/index.ts +++ b/static/node_modules/bloatless-react/index.ts @@ -5,20 +5,16 @@ import { v4 } from "uuid"; */ /* Types */ export interface Identifiable { - uuid: UUID; + id: string; +} + +export interface Stringifiable { + toString: () => string; } /* Utility */ -export class UUID { - readonly value: string; - - constructor() { - this.value = v4(); - } - - toString(): string { - return this.value; - } +export function UUID(): string { + return v4(); } /* State */ @@ -30,10 +26,12 @@ export class State { private _value: T; private _bindings = new Set>(); + // init constructor(initialValue: T) { this._value = initialValue; } + // value get value(): T { return this._value; } @@ -44,6 +42,7 @@ export class State { this.callSubscriptions(); } + // subscriptions callSubscriptions(): void { this._bindings.forEach((fn) => fn(this._value)); } @@ -52,43 +51,66 @@ export class State { this._bindings.add(fn); fn(this._value); } + + // stringify + toString(): string { + return JSON.stringify(this._value); + } } export class ListState extends State> { private additionHandlers = new Set>(); - private removalHandlers = new Map>(); + private removalHandlers = new Map>(); - constructor() { - super(new Set()); + // init + constructor(initialItems?: T[]) { + super(new Set(initialItems)); } + // list add(...items: T[]): void { items.forEach((item) => { this.value.add(item); this.additionHandlers.forEach((handler) => handler(item)); }); + this.callSubscriptions(); } remove(...items: T[]): void { items.forEach((item) => { this.value.delete(item); - const uuid = item.uuid; + const id = item.id; - if (!this.removalHandlers.has(uuid)) return; - this.removalHandlers.get(uuid)!(item); - this.removalHandlers.delete(uuid); + if (!this.removalHandlers.has(id)) return; + this.removalHandlers.get(id)!(item); + this.removalHandlers.delete(id); }); + this.callSubscriptions(); } + clear() { + this.remove(...this.value.values()); + } + + // handlers handleAddition(handler: AdditionSubscription): void { this.additionHandlers.add(handler); + [...this.value.values()].forEach(handler); } handleRemoval(item: T, handler: RemovalSubscription): void { - this.removalHandlers.set(item.uuid, handler); + this.removalHandlers.set(item.id, handler); + } + + // stringification + toString(): string { + const array = [...this.value]; + const json = JSON.stringify(array); + return json; } } +// UTILITY export function createProxyState( statesToSubscibe: State[], fn: () => T @@ -100,9 +122,49 @@ export function createProxyState( return proxyState; } +function persistState(localStorageKey: string, state: State) { + state.subscribe(() => { + const stringifiedValue = state.toString(); + localStorage.setItem(localStorageKey, stringifiedValue); + }); +} + +export function restoreState( + localStorageKey: string, + initialStateValue: T +): State { + const storedString = + localStorage.getItem(localStorageKey) ?? + JSON.stringify(initialStateValue); + const convertedValue = JSON.parse(storedString); + + const state = new State(convertedValue); + persistState(localStorageKey, state); + + return state; +} + +export function restoreListState( + localStorageKey: string +): ListState { + const storedString = localStorage.getItem(localStorageKey) ?? ""; + + let initialItems: any[] = []; + + try { + const array = JSON.parse(storedString); + if (!Array.isArray(array)) throw ""; + initialItems = array; + } catch {} + + const state = new ListState(initialItems); + persistState(localStorageKey, state); + + return state; +} + export type ListItemConverter = ( - item: T, - listState: ListState + item: T ) => HTMLElement; /* @@ -139,20 +201,27 @@ export function createElement( } case "subscribe": { if (directiveValue == "children") { - const [listState, toElement] = value as [ - listState: ListState, - ( - item: any, - listState: ListState - ) => HTMLElement - ]; - listState.handleAddition((newItem) => { - const child = toElement(newItem, listState); - listState.handleRemoval(newItem, () => - child.remove() - ); - element.append(child); - }); + element.style.scrollBehavior = "smooth"; + try { + const [listState, toElement] = value as [ + listState: ListState, + ( + item: any, + listState: ListState + ) => HTMLElement + ]; + + listState.handleAddition((newItem) => { + const child = toElement(newItem, listState); + listState.handleRemoval(newItem, () => + child.remove() + ); + element.append(child); + child.scrollIntoView(); + }); + } catch { + throw `error: cannot process subscribe:children directive because ListItemConverter is not defined. Usage: "subscribe:children={[list, converter]}"; you can find a more detailed example in the documentation`; + } } else { const state = value as State; state.subscribe( @@ -173,9 +242,20 @@ export function createElement( break; } case "toggle": { + if (value.subscribe) { + const state = value as State; + state.subscribe((newValue) => + element.toggleAttribute(directiveValue, newValue) + ); + } else { + element.toggleAttribute(directiveValue, value); + } + break; + } + case "set": { const state = value as State; state.subscribe((newValue) => - element.toggleAttribute(directiveValue, newValue) + element.setAttribute(directiveValue, newValue) ); break; } diff --git a/static/node_modules/bloatless-react/package.json b/static/node_modules/bloatless-react/package.json index b86d958..2746ccd 100644 --- a/static/node_modules/bloatless-react/package.json +++ b/static/node_modules/bloatless-react/package.json @@ -1,6 +1,6 @@ { "name": "bloatless-react", - "version": "1.1.3", + "version": "1.2.4", "description": "very minimal and flexible react alternative", "repository": { "type": "git", diff --git a/static/package.json b/static/package.json index 9fc3e21..845797b 100644 --- a/static/package.json +++ b/static/package.json @@ -1,9 +1,9 @@ { - "dependencies": { - "bloatless-react": "^1.1.3", - "esbuild": "latest" - }, - "scripts": { - "build": "esbuild src/index.tsx --bundle --outdir=dist" - } + "dependencies": { + "bloatless-react": "^1.2.4", + "esbuild": "latest" + }, + "scripts": { + "build": "esbuild src/index.tsx --bundle --outdir=dist" + } }