@davidsouther/jiffies 2026.4.1 → 2026.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/README.md +0 -3
  2. package/lib/esm/assert.d.ts +26 -0
  3. package/lib/esm/assert.js +38 -0
  4. package/lib/esm/awaitable.js +1 -0
  5. package/lib/esm/case.d.ts +1 -0
  6. package/lib/esm/case.js +5 -0
  7. package/lib/esm/components/accordion.d.ts +5 -0
  8. package/lib/esm/components/accordion.js +9 -0
  9. package/lib/esm/components/alert.d.ts +7 -0
  10. package/lib/esm/components/alert.js +31 -0
  11. package/lib/esm/components/button_bar.d.ts +8 -0
  12. package/lib/esm/components/button_bar.js +25 -0
  13. package/lib/esm/components/card.d.ts +8 -0
  14. package/lib/esm/components/card.js +31 -0
  15. package/lib/esm/components/children.d.ts +2 -0
  16. package/lib/esm/components/children.js +7 -0
  17. package/lib/esm/components/form.d.ts +5 -0
  18. package/lib/esm/components/form.js +13 -0
  19. package/lib/esm/components/index.d.ts +9 -0
  20. package/lib/esm/components/index.js +10 -0
  21. package/lib/esm/components/inline_edit.d.ts +12 -0
  22. package/lib/esm/components/inline_edit.js +48 -0
  23. package/lib/esm/components/link.d.ts +5 -0
  24. package/lib/esm/components/link.js +11 -0
  25. package/lib/esm/components/logger.d.ts +6 -0
  26. package/lib/esm/components/logger.js +22 -0
  27. package/lib/esm/components/modal.d.ts +2 -0
  28. package/lib/esm/components/modal.js +10 -0
  29. package/lib/esm/components/nav.d.ts +11 -0
  30. package/lib/esm/components/nav.js +27 -0
  31. package/lib/esm/components/property.d.ts +9 -0
  32. package/lib/esm/components/property.js +16 -0
  33. package/lib/esm/components/select.d.ts +10 -0
  34. package/lib/esm/components/select.js +3 -0
  35. package/lib/esm/components/tabs.d.ts +20 -0
  36. package/lib/esm/components/tabs.js +45 -0
  37. package/lib/esm/components/virtual_scroll.d.ts +42 -0
  38. package/lib/esm/components/virtual_scroll.js +94 -0
  39. package/lib/esm/debounce.d.ts +1 -0
  40. package/lib/esm/debounce.js +11 -0
  41. package/lib/esm/diff.d.ts +15 -0
  42. package/lib/esm/diff.js +50 -0
  43. package/lib/esm/display.d.ts +5 -0
  44. package/lib/esm/display.js +11 -0
  45. package/lib/esm/dom/css/border.d.ts +11 -0
  46. package/lib/esm/dom/css/border.js +27 -0
  47. package/lib/esm/dom/css/constants.d.ts +31 -0
  48. package/lib/esm/dom/css/constants.js +28 -0
  49. package/lib/esm/dom/css/core.d.ts +5 -0
  50. package/lib/esm/dom/css/core.js +24 -0
  51. package/lib/esm/dom/css/fstyle.d.ts +5 -0
  52. package/lib/esm/dom/css/fstyle.js +32 -0
  53. package/lib/esm/dom/css/sizing.d.ts +5 -0
  54. package/lib/esm/dom/css/sizing.js +10 -0
  55. package/lib/esm/dom/dom.d.ts +36 -0
  56. package/lib/esm/dom/dom.js +217 -0
  57. package/lib/esm/dom/fc.d.ts +10 -0
  58. package/lib/esm/dom/fc.js +32 -0
  59. package/lib/esm/dom/form/form.app.d.ts +1 -0
  60. package/lib/esm/dom/form/form.app.js +19 -0
  61. package/lib/esm/dom/form/form.d.ts +27 -0
  62. package/lib/esm/dom/form/form.js +65 -0
  63. package/lib/esm/dom/html.d.ts +112 -0
  64. package/{src/dom/html.ts → lib/esm/dom/html.js} +2 -14
  65. package/lib/esm/dom/hydrate.d.ts +39 -0
  66. package/lib/esm/dom/hydrate.js +187 -0
  67. package/lib/esm/dom/index.js +2 -0
  68. package/lib/esm/dom/navigation/index.d.ts +76 -0
  69. package/lib/esm/dom/navigation/index.js +292 -0
  70. package/lib/esm/dom/observable.d.ts +2 -0
  71. package/lib/esm/dom/observable.js +6 -0
  72. package/lib/esm/dom/provide.d.ts +3 -0
  73. package/lib/esm/dom/provide.js +7 -0
  74. package/lib/esm/dom/render.d.ts +8 -0
  75. package/lib/esm/dom/render.js +28 -0
  76. package/lib/esm/dom/router/link.d.ts +6 -0
  77. package/lib/esm/dom/router/link.js +3 -0
  78. package/lib/esm/dom/router/router.d.ts +13 -0
  79. package/lib/esm/dom/router/router.js +52 -0
  80. package/lib/esm/dom/svg.d.ts +64 -0
  81. package/{src/dom/svg.ts → lib/esm/dom/svg.js} +2 -15
  82. package/lib/esm/dom/types/css.d.ts +6590 -0
  83. package/lib/esm/dom/types/css.js +1 -0
  84. package/lib/esm/dom/types/dom.js +1 -0
  85. package/lib/esm/dom/types/html.d.ts +614 -0
  86. package/lib/esm/dom/types/html.js +1 -0
  87. package/lib/esm/dom/xml.d.ts +1 -0
  88. package/lib/esm/dom/xml.js +4 -0
  89. package/lib/esm/equal.d.ts +11 -0
  90. package/lib/esm/equal.js +43 -0
  91. package/lib/esm/fs.d.ts +72 -0
  92. package/lib/esm/fs.js +227 -0
  93. package/lib/esm/fs_node.d.ts +15 -0
  94. package/lib/esm/fs_node.js +45 -0
  95. package/lib/esm/generator.d.ts +1 -0
  96. package/lib/esm/generator.js +10 -0
  97. package/lib/esm/lock.d.ts +1 -0
  98. package/lib/esm/lock.js +23 -0
  99. package/lib/esm/log.d.ts +69 -0
  100. package/lib/esm/log.js +211 -0
  101. package/lib/esm/observable/event.d.ts +35 -0
  102. package/lib/esm/observable/event.js +46 -0
  103. package/lib/esm/observable/observable.d.ts +134 -0
  104. package/lib/esm/observable/observable.js +349 -0
  105. package/lib/esm/range.d.ts +1 -0
  106. package/lib/esm/range.js +7 -0
  107. package/lib/esm/result.d.ts +31 -0
  108. package/lib/esm/result.js +66 -0
  109. package/lib/esm/safe.d.ts +1 -0
  110. package/lib/esm/safe.js +10 -0
  111. package/lib/esm/server/http/apps.d.ts +5 -0
  112. package/lib/esm/server/http/apps.js +23 -0
  113. package/lib/esm/server/http/css.d.ts +5 -0
  114. package/lib/esm/server/http/css.js +43 -0
  115. package/lib/esm/server/http/index.d.ts +16 -0
  116. package/lib/esm/server/http/index.js +78 -0
  117. package/lib/esm/server/http/response.d.ts +4 -0
  118. package/lib/esm/server/http/response.js +43 -0
  119. package/lib/esm/server/http/sitemap.d.ts +2 -0
  120. package/lib/esm/server/http/sitemap.js +22 -0
  121. package/lib/esm/server/http/static.d.ts +2 -0
  122. package/lib/esm/server/http/static.js +22 -0
  123. package/lib/esm/server/http/typescript.d.ts +5 -0
  124. package/lib/esm/server/http/typescript.js +40 -0
  125. package/lib/esm/server/live-reload.d.ts +46 -0
  126. package/lib/esm/server/live-reload.js +161 -0
  127. package/lib/esm/server/main.d.ts +2 -0
  128. package/lib/esm/server/main.js +23 -0
  129. package/lib/esm/server/ws/frame.d.ts +2 -0
  130. package/lib/esm/server/ws/frame.js +35 -0
  131. package/lib/esm/server/ws/handshake.d.ts +4 -0
  132. package/lib/esm/server/ws/handshake.js +32 -0
  133. package/lib/esm/server/ws/index.d.ts +14 -0
  134. package/lib/esm/server/ws/index.js +68 -0
  135. package/lib/esm/ssg/bundle.d.ts +14 -0
  136. package/lib/esm/ssg/bundle.js +73 -0
  137. package/lib/esm/ssg/copy-public.d.ts +6 -0
  138. package/lib/esm/ssg/copy-public.js +34 -0
  139. package/lib/esm/ssg/discover.d.ts +15 -0
  140. package/lib/esm/ssg/discover.js +117 -0
  141. package/lib/esm/ssg/main.d.ts +2 -0
  142. package/lib/esm/ssg/main.js +122 -0
  143. package/lib/esm/ssg/rewrite.d.ts +9 -0
  144. package/lib/esm/ssg/rewrite.js +15 -0
  145. package/lib/esm/ssg/ssg.d.ts +26 -0
  146. package/lib/esm/ssg/ssg.js +84 -0
  147. package/lib/esm/transpile.d.mts +3 -0
  148. package/lib/esm/transpile.mjs +12 -0
  149. package/package.json +19 -10
  150. package/src/404.html +0 -14
  151. package/src/assert.ts +0 -56
  152. package/src/case.ts +0 -5
  153. package/src/components/_notes +0 -33
  154. package/src/components/button_bar.ts +0 -42
  155. package/src/components/inline_edit.ts +0 -78
  156. package/src/components/logger.ts +0 -35
  157. package/src/components/select.ts +0 -22
  158. package/src/components/test.ts +0 -5
  159. package/src/components/virtual_scroll.test.ts +0 -30
  160. package/src/components/virtual_scroll.ts +0 -199
  161. package/src/context.test.ts +0 -58
  162. package/src/context.ts +0 -67
  163. package/src/debounce.ts +0 -14
  164. package/src/diff.test.ts +0 -48
  165. package/src/diff.ts +0 -82
  166. package/src/display.ts +0 -18
  167. package/src/dom/README.md +0 -102
  168. package/src/dom/css/border.ts +0 -47
  169. package/src/dom/css/constants.ts +0 -34
  170. package/src/dom/css/core.ts +0 -28
  171. package/src/dom/css/fstyle.ts +0 -42
  172. package/src/dom/css/sizing.ts +0 -11
  173. package/src/dom/dom.ts +0 -183
  174. package/src/dom/fc.test.ts +0 -43
  175. package/src/dom/fc.ts +0 -80
  176. package/src/dom/form/form.app.ts +0 -50
  177. package/src/dom/form/form.ts +0 -82
  178. package/src/dom/form/index.html +0 -15
  179. package/src/dom/html.test.ts +0 -74
  180. package/src/dom/observable.test.ts +0 -43
  181. package/src/dom/observable.ts +0 -11
  182. package/src/dom/provide.ts +0 -11
  183. package/src/dom/router/link.ts +0 -14
  184. package/src/dom/router/router.ts +0 -72
  185. package/src/dom/test.ts +0 -11
  186. package/src/dom/types/css.ts +0 -10088
  187. package/src/dom/types/dom.ts +0 -0
  188. package/src/dom/types/html.ts +0 -629
  189. package/src/dom/xml.ts +0 -11
  190. package/src/equal.test.ts +0 -23
  191. package/src/equal.ts +0 -66
  192. package/src/favicon.ico +0 -0
  193. package/src/flags.test.ts +0 -43
  194. package/src/flags.ts +0 -53
  195. package/src/fs.test.ts +0 -106
  196. package/src/fs.ts +0 -300
  197. package/src/fs_node.ts +0 -57
  198. package/src/fs_win.test.ts +0 -11
  199. package/src/generator.test.ts +0 -27
  200. package/src/generator.ts +0 -12
  201. package/src/hooks/_notes +0 -6
  202. package/src/index.html +0 -82
  203. package/src/is_browser.js +0 -1
  204. package/src/lock.test.ts +0 -17
  205. package/src/lock.ts +0 -23
  206. package/src/log.ts +0 -155
  207. package/src/observable/_notes +0 -26
  208. package/src/observable/event.ts +0 -93
  209. package/src/observable/observable.test.ts +0 -73
  210. package/src/observable/observable.ts +0 -484
  211. package/src/pico/_variables.scss +0 -66
  212. package/src/pico/components/_accordion.scss +0 -112
  213. package/src/pico/components/_button-group.scss +0 -51
  214. package/src/pico/components/_card.scss +0 -47
  215. package/src/pico/components/_dropdown.scss +0 -203
  216. package/src/pico/components/_modal.scss +0 -181
  217. package/src/pico/components/_nav.scss +0 -79
  218. package/src/pico/components/_progress.scss +0 -70
  219. package/src/pico/components/_property.scss +0 -34
  220. package/src/pico/content/_button.scss +0 -152
  221. package/src/pico/content/_code.scss +0 -63
  222. package/src/pico/content/_embedded.scss +0 -0
  223. package/src/pico/content/_form-alt.scss +0 -276
  224. package/src/pico/content/_form.scss +0 -259
  225. package/src/pico/content/_misc.scss +0 -0
  226. package/src/pico/content/_table.scss +0 -28
  227. package/src/pico/content/_toggle.scss +0 -132
  228. package/src/pico/content/_typography.scss +0 -232
  229. package/src/pico/layout/_container.scss +0 -40
  230. package/src/pico/layout/_document.scss +0 -0
  231. package/src/pico/layout/_flex.scss +0 -46
  232. package/src/pico/layout/_grid.scss +0 -24
  233. package/src/pico/layout/_scroller.scss +0 -16
  234. package/src/pico/layout/_section.scss +0 -8
  235. package/src/pico/layout/_sectioning.scss +0 -55
  236. package/src/pico/pico.scss +0 -60
  237. package/src/pico/reset/_accessibility.scss +0 -34
  238. package/src/pico/reset/_button.scss +0 -17
  239. package/src/pico/reset/_code.scss +0 -15
  240. package/src/pico/reset/_document.scss +0 -48
  241. package/src/pico/reset/_embedded.scss +0 -39
  242. package/src/pico/reset/_form.scss +0 -97
  243. package/src/pico/reset/_misc.scss +0 -23
  244. package/src/pico/reset/_nav.scss +0 -5
  245. package/src/pico/reset/_progress.scss +0 -4
  246. package/src/pico/reset/_table.scss +0 -8
  247. package/src/pico/reset/_typography.scss +0 -25
  248. package/src/pico/themes/default/_colors.scss +0 -65
  249. package/src/pico/themes/default/_dark.scss +0 -148
  250. package/src/pico/themes/default/_light.scss +0 -149
  251. package/src/pico/themes/default/_styles.scss +0 -272
  252. package/src/pico/themes/default.scss +0 -34
  253. package/src/pico/utilities/_accessibility.scss +0 -3
  254. package/src/pico/utilities/_loading.scss +0 -52
  255. package/src/pico/utilities/_reduce-motion.scss +0 -27
  256. package/src/pico/utilities/_tooltip.scss +0 -101
  257. package/src/range.ts +0 -7
  258. package/src/result.test.ts +0 -101
  259. package/src/result.ts +0 -107
  260. package/src/safe.ts +0 -12
  261. package/src/scope/describe.ts +0 -81
  262. package/src/scope/display/console.ts +0 -26
  263. package/src/scope/display/dom.ts +0 -36
  264. package/src/scope/display/junit.ts +0 -64
  265. package/src/scope/execute.ts +0 -110
  266. package/src/scope/expect.ts +0 -169
  267. package/src/scope/fix.ts +0 -30
  268. package/src/scope/index.ts +0 -11
  269. package/src/scope/scope.ts +0 -21
  270. package/src/scope/state.ts +0 -13
  271. package/src/server/http/apps.ts +0 -26
  272. package/src/server/http/css.ts +0 -49
  273. package/src/server/http/index.ts +0 -127
  274. package/src/server/http/response.ts +0 -57
  275. package/src/server/http/sitemap.ts +0 -48
  276. package/src/server/http/static.ts +0 -30
  277. package/src/server/http/typescript.ts +0 -46
  278. package/src/server/main.ts +0 -23
  279. package/src/test.mjs +0 -33
  280. package/src/test_all.ts +0 -35
  281. package/src/transpile.mjs +0 -16
  282. package/src/zip/spec.txt +0 -3260
  283. package/tsconfig.json +0 -34
  284. /package/{src/awaitable.ts → lib/esm/awaitable.d.ts} +0 -0
  285. /package/{src/dom/index.ts → lib/esm/dom/index.d.ts} +0 -0
  286. /package/{src/dom/form/form.test.ts → lib/esm/dom/types/dom.d.ts} +0 -0
@@ -0,0 +1,94 @@
1
+ import { debounce } from "../debounce.js";
2
+ import { FC, State } from "../dom/fc.js";
3
+ import { div } from "../dom/html.js";
4
+ export function arrayAdapter(data) {
5
+ return (offset, limit) => data.slice(offset, offset + limit);
6
+ }
7
+ export function fillVirtualScrollSettings(settings) {
8
+ const { minIndex = 0, maxIndex = Number.MAX_SAFE_INTEGER, startIndex = 0, itemHeight = 20, count = maxIndex - minIndex + 1, tolerance = count, } = settings;
9
+ return { minIndex, maxIndex, startIndex, itemHeight, count, tolerance };
10
+ }
11
+ export function initialState(settings) {
12
+ // From Denis Hilt, https://blog.logrocket.com/virtual-scrolling-core-principles-and-basic-implementation-in-react/
13
+ const { minIndex, maxIndex, startIndex, itemHeight, count, tolerance } = settings;
14
+ const bufferedItems = count + 2 * tolerance;
15
+ const itemsAbove = Math.max(0, startIndex - tolerance - minIndex);
16
+ const viewportHeight = count * itemHeight;
17
+ const totalHeight = (maxIndex - minIndex + 1) * itemHeight;
18
+ const toleranceHeight = tolerance * itemHeight;
19
+ const bufferHeight = viewportHeight + 2 * toleranceHeight;
20
+ const topPaddingHeight = itemsAbove * itemHeight;
21
+ const bottomPaddingHeight = totalHeight - (topPaddingHeight + bufferHeight);
22
+ return {
23
+ scrollTop: 0,
24
+ settings,
25
+ viewportHeight,
26
+ totalHeight,
27
+ toleranceHeight,
28
+ bufferedItems,
29
+ topPaddingHeight,
30
+ bottomPaddingHeight,
31
+ data: [],
32
+ rows: [],
33
+ };
34
+ }
35
+ export function getData(minIndex, maxIndex, offset, limit, get) {
36
+ const start = Math.max(0, minIndex, offset);
37
+ const end = Math.min(maxIndex, offset + limit - 1);
38
+ const data = get(start, end - start);
39
+ return [...data];
40
+ }
41
+ export function doScroll(scrollTop, state, get) {
42
+ const { totalHeight, toleranceHeight, bufferedItems, settings: { itemHeight, minIndex, maxIndex }, } = state;
43
+ const index = minIndex + Math.floor((scrollTop - toleranceHeight) / itemHeight);
44
+ const data = getData(minIndex, maxIndex, index, bufferedItems, get);
45
+ const topPaddingHeight = Math.max((index - minIndex) * itemHeight, 0);
46
+ const bottomPaddingHeight = Math.max(totalHeight - (topPaddingHeight + data.length * itemHeight), 0);
47
+ return { scrollTop, topPaddingHeight, bottomPaddingHeight, data };
48
+ }
49
+ export const VirtualScroll = FC("virtual-scroll", (element, props) => {
50
+ const settings = fillVirtualScrollSettings(props.settings);
51
+ const state = {
52
+ ...initialState(settings),
53
+ ...element[State],
54
+ };
55
+ element[State] = state;
56
+ const scrollTo = ({ target } = { target: state }) => {
57
+ const scrollTop = target?.scrollTop ?? state.topPaddingHeight;
58
+ const updatedSate = {
59
+ ...state,
60
+ ...doScroll(scrollTop, state, props.get),
61
+ };
62
+ setState(updatedSate);
63
+ };
64
+ const viewportElement = div({
65
+ style: { height: `${state.viewportHeight}px`, overflowY: "scroll" },
66
+ events: {
67
+ // @ts-expect-error
68
+ scroll: debounce(scrollTo, 0),
69
+ },
70
+ });
71
+ setTimeout(() => {
72
+ viewportElement.scroll?.({ top: state.scrollTop });
73
+ });
74
+ const setState = (newState) => {
75
+ state.scrollTop = newState.scrollTop;
76
+ state.topPaddingHeight = newState.topPaddingHeight;
77
+ state.bottomPaddingHeight = newState.bottomPaddingHeight;
78
+ state.data = newState.data;
79
+ state.rows = state.data.map(props.row);
80
+ viewportElement.update(div({
81
+ class: "VirtualScroll__topPadding",
82
+ style: { height: `${state.topPaddingHeight}px` },
83
+ }), ...(state.rows ?? []).map((row, i) => div({
84
+ class: `VirtualScroll__item_${i}`,
85
+ style: { height: `${settings.itemHeight}px` },
86
+ }, row)), div({
87
+ class: "VirtualScroll__bottomPadding",
88
+ style: { height: `${state.bottomPaddingHeight}px` },
89
+ }));
90
+ };
91
+ scrollTo();
92
+ return viewportElement;
93
+ });
94
+ export default VirtualScroll;
@@ -0,0 +1 @@
1
+ export declare function debounce<T extends unknown[]>(fn: (...args: T) => void, ms?: number): (...args: T) => void;
@@ -0,0 +1,11 @@
1
+ export function debounce(fn, ms = 32) {
2
+ let timer;
3
+ return (...args) => {
4
+ clearTimeout(timer);
5
+ timer = setTimeout(() => {
6
+ clearTimeout(timer);
7
+ return fn(...args);
8
+ }, ms);
9
+ return timer;
10
+ };
11
+ }
@@ -0,0 +1,15 @@
1
+ export declare const DiffA: unique symbol;
2
+ export declare const DiffB: unique symbol;
3
+ export type DiffIndex = string | number;
4
+ export type DiffPrimitive = string | number | boolean | null | undefined;
5
+ interface DiffEntry {
6
+ key: DiffIndex;
7
+ left: DiffPrimitive;
8
+ right: DiffPrimitive;
9
+ }
10
+ interface DiffList {
11
+ key: DiffIndex;
12
+ children: (DiffEntry | DiffList)[];
13
+ }
14
+ export declare function diff<T>(a: Partial<T>, b: Partial<T>): (DiffEntry | DiffList)[];
15
+ export {};
@@ -0,0 +1,50 @@
1
+ import { range } from "./range.js";
2
+ import { isSome, None, Some } from "./result.js";
3
+ export const DiffA = Symbol("A");
4
+ export const DiffB = Symbol("B");
5
+ function doDiff(va, vb, k) {
6
+ if (Array.isArray(va)) {
7
+ // @ts-expect-error
8
+ return diffArray(va, vb, k);
9
+ }
10
+ if (typeof va === "object") {
11
+ const d = diffObject(va ?? {}, vb ?? {}, k);
12
+ if (d.children.length === 0) {
13
+ return None();
14
+ }
15
+ return Some(d);
16
+ }
17
+ if (Object.is(va, vb)) {
18
+ return None();
19
+ }
20
+ // @ts-expect-error
21
+ return { key: k, left: va, right: vb };
22
+ }
23
+ function diffArray(a, b, key) {
24
+ const indexes = Math.max(a.length, b.length);
25
+ const children = range(0, indexes)
26
+ .map((i) => doDiff(a[i], b[i], i))
27
+ .filter(isSome);
28
+ return children.length > 0 ? { key, children } : None();
29
+ }
30
+ function diffObject(a, b, key = "") {
31
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
32
+ const children = [...keys]
33
+ // @ts-expect-error
34
+ .map((k) => doDiff(a[k], b[k], k))
35
+ .filter(isSome);
36
+ return {
37
+ key,
38
+ children,
39
+ };
40
+ }
41
+ export function diff(a, b) {
42
+ if (typeof a !== "object" && !Object.is(a, b)) {
43
+ // @ts-expect-error
44
+ return [{ key: "", left: a, right: b }];
45
+ }
46
+ return (Array.isArray(a)
47
+ ? // @ts-ignore
48
+ (diffArray(a, b, "") ?? { children: [] })
49
+ : diffObject(a, b, "")).children;
50
+ }
@@ -0,0 +1,5 @@
1
+ export type Display = string | {
2
+ toString(): string;
3
+ };
4
+ export declare const isDisplay: (a: unknown) => a is Display;
5
+ export declare const display: (a: unknown | Display) => string;
@@ -0,0 +1,11 @@
1
+ export const isDisplay = (a) => typeof a?.toString === "function" ||
2
+ typeof a === "string";
3
+ export const display = (a) => {
4
+ if (isDisplay(a)) {
5
+ const str = a.toString();
6
+ if (str === "[object Object]")
7
+ return JSON.stringify(a);
8
+ return str;
9
+ }
10
+ return JSON.stringify(a);
11
+ };
@@ -0,0 +1,11 @@
1
+ import type { Properties } from "../types/css.ts";
2
+ import type { Side, Size } from "./constants.ts";
3
+ export declare function rounded(size?: Size, side?: Side): Properties<0 | (string & {}), string & {}>;
4
+ export declare function border({ side: _side, style: _style, radius: _radius, width: _width, color: _color, }: {
5
+ side?: Side;
6
+ style?: "solid" | "dotted" | "dashed" | "double" | "none";
7
+ radius?: Size;
8
+ width?: 0 | 1 | 2 | 4 | 8;
9
+ color?: string;
10
+ }): {};
11
+ export declare function inset(width: 0 | 1 | 2 | 4 | 8, color1?: string, color2?: string): {};
@@ -0,0 +1,27 @@
1
+ import { getSide, getSize, isSide } from "./core.js";
2
+ export function rounded(size = "", side = "") {
3
+ if (isSide(size)) {
4
+ side = size;
5
+ size = "";
6
+ }
7
+ const sized = getSize(size);
8
+ return getSide(side).reduce((prev, curr) => {
9
+ if (curr === "") {
10
+ prev.borderRadius = sized;
11
+ }
12
+ else {
13
+ // @ts-expect-error
14
+ prev[`border${curr}Radius`] = sized;
15
+ }
16
+ return prev;
17
+ }, {});
18
+ }
19
+ export function border({ side: _side = "", style: _style = "solid", radius: _radius = "", width: _width = 1, color: _color = "black", }) {
20
+ return {};
21
+ }
22
+ export function inset(width, color1 = "gray", color2 = "lightgray") {
23
+ return {
24
+ ...border({ side: "tl", width, color: color1, radius: "none" }),
25
+ ...border({ side: "br", width, color: color2, radius: "none" }),
26
+ };
27
+ }
@@ -0,0 +1,31 @@
1
+ export declare const Sizes: {
2
+ none: string;
3
+ sm: string;
4
+ "": string;
5
+ md: string;
6
+ lg: string;
7
+ xl: string;
8
+ "2xl": string;
9
+ "3xl": string;
10
+ full: string;
11
+ };
12
+ export declare const Sides: {
13
+ "": string;
14
+ t: string;
15
+ r: string;
16
+ l: string;
17
+ b: string;
18
+ tl: string;
19
+ tr: string;
20
+ bl: string;
21
+ br: string;
22
+ };
23
+ export declare const Widths: {
24
+ "1/4": string;
25
+ "1/2": string;
26
+ "3/4": string;
27
+ full: string;
28
+ };
29
+ export type Size = keyof typeof Sizes;
30
+ export type Side = keyof typeof Sides;
31
+ export type Width = keyof typeof Widths;
@@ -0,0 +1,28 @@
1
+ export const Sizes = {
2
+ none: "0px",
3
+ sm: "0.125rem",
4
+ "": "0.25rem",
5
+ md: "0.375rem",
6
+ lg: "0.5rem",
7
+ xl: "0.75rem",
8
+ "2xl": "1rem",
9
+ "3xl": "1.5rem",
10
+ full: "9999px",
11
+ };
12
+ export const Sides = {
13
+ "": "",
14
+ t: "Top",
15
+ r: "Right",
16
+ l: "Left",
17
+ b: "Bottom",
18
+ tl: "TopLeft",
19
+ tr: "TopRight",
20
+ bl: "BottomLeft",
21
+ br: "BottomRight",
22
+ };
23
+ export const Widths = {
24
+ "1/4": "25%",
25
+ "1/2": "50%",
26
+ "3/4": "75%",
27
+ full: "100%",
28
+ };
@@ -0,0 +1,5 @@
1
+ import { type Side, type Size, Sizes } from "./constants.ts";
2
+ export declare function isSide(v: string): v is Side;
3
+ export declare function isSize(v: string): v is Size;
4
+ export declare function getSize(size: keyof typeof Sizes): string;
5
+ export declare function getSide(side: Side): string[];
@@ -0,0 +1,24 @@
1
+ import { Sides, Sizes } from "./constants.js";
2
+ export function isSide(v) {
3
+ return Sides[v] !== undefined;
4
+ }
5
+ export function isSize(v) {
6
+ return Sizes[v] !== undefined;
7
+ }
8
+ export function getSize(size) {
9
+ return Sizes[size];
10
+ }
11
+ export function getSide(side) {
12
+ switch (side) {
13
+ case "t":
14
+ return [...getSide("tl"), ...getSide("tr")];
15
+ case "r":
16
+ return [...getSide("tr"), ...getSide("br")];
17
+ case "b":
18
+ return [...getSide("br"), ...getSide("bl")];
19
+ case "l":
20
+ return [...getSide("tl"), ...getSide("bl")];
21
+ default:
22
+ return [Sides[side]];
23
+ }
24
+ }
@@ -0,0 +1,5 @@
1
+ import type { Properties } from "../types/css.ts";
2
+ export type FStyle = Properties | {
3
+ [k: string]: FStyle;
4
+ };
5
+ export declare function compileFStyle(fstyle: FStyle, prefix?: string): string;
@@ -0,0 +1,32 @@
1
+ import { dashCase } from "../../case.js";
2
+ export function compileFStyle(fstyle, prefix = "") {
3
+ const properties = [];
4
+ const rules = [];
5
+ for (const [key, value] of Object.entries(fstyle)) {
6
+ if (typeof value === "string") {
7
+ properties.push({ key, value });
8
+ }
9
+ else {
10
+ rules.push({ key, value });
11
+ }
12
+ }
13
+ let rule = "";
14
+ if (properties.length > 0) {
15
+ rule += `${prefix} {\n`;
16
+ for (const { key, value } of properties) {
17
+ rule += ` ${dashCase(key)}: ${value};\n`;
18
+ }
19
+ rule += "}\n\n";
20
+ }
21
+ for (const { key, value } of rules) {
22
+ if (key.startsWith("@media")) {
23
+ rule += `${key} {\n`;
24
+ rule += compileFStyle(value, " ");
25
+ rule += "}\n\n";
26
+ }
27
+ else {
28
+ rule += compileFStyle(value, `${prefix} ${key}`);
29
+ }
30
+ }
31
+ return rule;
32
+ }
@@ -0,0 +1,5 @@
1
+ import { type Width } from "./constants.ts";
2
+ export declare function width(amount: Width, block?: "inline"): {
3
+ width: string;
4
+ display?: string;
5
+ };
@@ -0,0 +1,10 @@
1
+ import { Widths } from "./constants.js";
2
+ export function width(amount, block) {
3
+ if (amount === undefined && Widths[block] !== undefined) {
4
+ amount = block;
5
+ }
6
+ return {
7
+ ...(block === "inline" ? { display: "inline-block" } : {}),
8
+ width: Widths[amount] ?? "0",
9
+ };
10
+ }
@@ -0,0 +1,36 @@
1
+ import type { Properties as SVGProperties } from "./types/css.ts";
2
+ export declare const XHTML_NAMESPACE_URI = "http://www.w3.org/1999/xhtml";
3
+ export declare const SVG_NAMESPACE_URI = "http://www.w3.org/2000/svg";
4
+ declare const Events: unique symbol;
5
+ export declare const CLEAR: unique symbol;
6
+ export type EventHandler = EventListenerOrEventListenerObject;
7
+ export type DenormChildren = Node | string | typeof CLEAR | null | undefined | false;
8
+ export type DOMElement = Element & ElementCSSInlineStyle;
9
+ export type DomAttrs = {
10
+ class: string | string[];
11
+ style: Partial<SVGProperties> | string;
12
+ role: "button" | "list" | "listbox";
13
+ events: Partial<{
14
+ [K in keyof HTMLElementEventMap]: EventHandler | null;
15
+ }>;
16
+ };
17
+ export type Attrs<E extends Omit<Element, "update">, S = object> = Partial<Omit<{
18
+ [k in keyof E]: string | number | boolean;
19
+ }, "style" | "toString"> & S & DomAttrs>;
20
+ export type DenormAttrs<E extends Omit<Element, "update">, S = object> = Attrs<E, S> | DenormChildren;
21
+ declare global {
22
+ interface Element {
23
+ [Events]: Map<string, EventHandler>;
24
+ update(attrs?: DenormAttrs<Element>, ...children: DenormChildren[]): this;
25
+ }
26
+ }
27
+ export type DOMUpdates<E extends Element = Element> = [DenormAttrs<E>, ...DenormChildren[]] | DenormChildren[];
28
+ export declare function normalizeArguments<E extends Element>(attrs?: DenormAttrs<E>, children?: DenormChildren[], defaultAttrs?: Attrs<E>): [Attrs<E>, DenormChildren[]];
29
+ export declare function up<E extends Element>(element: Omit<E, "update">, attrs?: DenormAttrs<E>, ...children: DenormChildren[]): E;
30
+ export declare function update(element: Omit<Element, "update">, attrs: Attrs<Element>, children: DenormChildren[]): Element;
31
+ /**
32
+ * Reconcile `element`'s mounted children against expected `children`, mutating the live DOM in place.
33
+ */
34
+ export declare function reconcileChildren(element: Node, children: (string | Node)[]): void;
35
+ export declare function patchNode(kept: Element, fresh: Element): void;
36
+ export {};
@@ -0,0 +1,217 @@
1
+ import { assert, assertExists } from "../assert.js";
2
+ if (typeof window === "undefined") {
3
+ const { JSDOM } = await import("jsdom");
4
+ // biome-ignore lint/suspicious/noGlobalAssign: Load JSDom globally
5
+ window = global.window = new JSDOM().window;
6
+ global.HTMLElement ??= window.HTMLElement;
7
+ global.customElements ??= window.customElements;
8
+ // Unconditional: jsdom's dispatchEvent instanceof-checks its own Event class, so Node's native Event must be replaced.
9
+ global.Event = window.Event;
10
+ global.MouseEvent ??= window.MouseEvent;
11
+ global.Element ??= window.Element;
12
+ }
13
+ export const XHTML_NAMESPACE_URI = "http://www.w3.org/1999/xhtml";
14
+ export const SVG_NAMESPACE_URI = "http://www.w3.org/2000/svg";
15
+ const Events = Symbol("events");
16
+ export const CLEAR = Symbol("Clear children");
17
+ // Node.ELEMENT_NODE; the Node global is not installed in the jsdom bootstrap
18
+ // above, so the numeric constant is used directly (cf. nodeType 3 for text).
19
+ const ELEMENT_NODE = 1;
20
+ function isAttrs(attrs) {
21
+ if (!attrs) {
22
+ return false;
23
+ }
24
+ if (typeof attrs === "object") {
25
+ return !attrs.nodeType;
26
+ }
27
+ return false;
28
+ }
29
+ export function normalizeArguments(attrs, children = [], defaultAttrs = {}) {
30
+ let attributes;
31
+ if (isAttrs(attrs)) {
32
+ attributes = attrs;
33
+ }
34
+ else {
35
+ if (attrs !== undefined) {
36
+ children.unshift(attrs);
37
+ }
38
+ attributes = defaultAttrs;
39
+ }
40
+ // Drop conditional/absent children (React's `{cond && <X/>}` idiom): null,
41
+ // undefined, and false. `0` and `""` are kept — they are legitimate text
42
+ // nodes, and dropping them would reintroduce the React `0`-renders-nothing bug.
43
+ return [attributes, children.flat().filter((c) => c != null && c !== false)];
44
+ }
45
+ export function up(element, attrs, ...children) {
46
+ return update(element, ...normalizeArguments(attrs, children));
47
+ }
48
+ /**
49
+ * (Re)attach a single listener for `type`, replacing any handler `events`
50
+ * already tracks for it, so each event has exactly one live handler — no
51
+ * stacking, no orphans. `events` is the element's own `[Events]` map; it
52
+ * stays the single source of truth.
53
+ */
54
+ function setListener(target, events, type, handler) {
55
+ if (events.has(type)) {
56
+ target.removeEventListener(type, assertExists(events.get(type)));
57
+ }
58
+ target.addEventListener(type, handler);
59
+ events.set(type, handler);
60
+ }
61
+ /** Detach the listener `events` tracks for `type`, if any, and forget it. */
62
+ function clearListener(target, events, type) {
63
+ if (events.has(type)) {
64
+ target.removeEventListener(type, assertExists(events.get(type)));
65
+ events.delete(type);
66
+ }
67
+ }
68
+ export function update(element, attrs, children) {
69
+ element[Events] ??= new Map();
70
+ const $events = element[Events];
71
+ for (const [k, v] of Object.entries(attrs.events ?? {})) {
72
+ if (v === null) {
73
+ clearListener(element, $events, k);
74
+ }
75
+ else if (v !== undefined) {
76
+ setListener(element, $events, k, v);
77
+ }
78
+ }
79
+ element.toggleAttribute("data-hydrate", $events.size > 0);
80
+ const _style = element.style;
81
+ if (_style) {
82
+ if (typeof attrs.style === "string") {
83
+ _style.cssText = attrs.style;
84
+ }
85
+ else {
86
+ for (const [k, v] of Object.entries(attrs.style ?? {})) {
87
+ // @ts-expect-error Object.entries is unable to statically look into args
88
+ _style[k] = v;
89
+ }
90
+ }
91
+ }
92
+ for (const [k, v] of Object.entries(attrs)) {
93
+ if (k === "style") {
94
+ continue;
95
+ }
96
+ if (k === "events") {
97
+ continue;
98
+ }
99
+ if (k === "class") {
100
+ const cs = Array.isArray(v) ? v : String(v).split(/\s+/m).filter(Boolean);
101
+ for (const c of cs) {
102
+ if (c.startsWith("!")) {
103
+ element.classList.remove(c.substring(1));
104
+ }
105
+ else {
106
+ element.classList.add(c);
107
+ }
108
+ }
109
+ continue;
110
+ }
111
+ if (!v) {
112
+ element.removeAttribute(k);
113
+ }
114
+ else if (v === true) {
115
+ element.setAttribute(k, k);
116
+ }
117
+ else {
118
+ element.setAttribute(k, String(v));
119
+ }
120
+ }
121
+ if (children?.length > 0) {
122
+ reconcileChildren(element, children[0] === CLEAR ? [] : children);
123
+ }
124
+ element.update ??= (attrs, ...children) => update(element, ...normalizeArguments(attrs, children));
125
+ return element;
126
+ }
127
+ /**
128
+ * Reconcile `element`'s mounted children against expected `children`, mutating the live DOM in place.
129
+ */
130
+ export function reconcileChildren(element, children) {
131
+ const desired = findDesiredNodes(element, children);
132
+ const { mountedSet, unclaimed } = findUnclaimedNodes(desired, element);
133
+ patchUnclaimedNodes(desired, mountedSet, unclaimed);
134
+ clearUnwantedNodes(desired, element);
135
+ insertDesiredNodes(element, desired);
136
+ }
137
+ function findDesiredNodes(element, children) {
138
+ const doc = element.ownerDocument ?? window.document;
139
+ const desired = children.map((child) => typeof child === "string" ? doc.createTextNode(child) : child);
140
+ return desired;
141
+ }
142
+ function insertDesiredNodes(element, desired) {
143
+ let cursor = element.firstChild;
144
+ for (const node of desired) {
145
+ if (node === cursor) {
146
+ cursor = cursor.nextSibling;
147
+ }
148
+ else {
149
+ element.insertBefore(node, cursor);
150
+ }
151
+ }
152
+ }
153
+ function clearUnwantedNodes(desired, element) {
154
+ const keep = new Set(desired);
155
+ for (const mounted of Array.from(element.childNodes)) {
156
+ if (!keep.has(mounted)) {
157
+ element.removeChild(mounted);
158
+ }
159
+ }
160
+ }
161
+ function patchUnclaimedNodes(desired, mountedSet, unclaimed) {
162
+ let claim = 0;
163
+ for (let i = 0; i < desired.length; i++) {
164
+ const node = desired[i];
165
+ if (node.nodeType !== ELEMENT_NODE || mountedSet.has(node)) {
166
+ continue;
167
+ }
168
+ if (claim < unclaimed.length) {
169
+ if (unclaimed[claim].nodeName === node.nodeName) {
170
+ patchNode(unclaimed[claim], node);
171
+ desired[i] = unclaimed[claim];
172
+ }
173
+ claim++;
174
+ }
175
+ }
176
+ }
177
+ function findUnclaimedNodes(desired, element) {
178
+ const unclaimed = [];
179
+ const desiredSet = new Set(desired);
180
+ const mountedSet = new Set(element.childNodes);
181
+ for (const mounted of Array.from(element.childNodes)) {
182
+ if (mounted.nodeType === ELEMENT_NODE && !desiredSet.has(mounted)) {
183
+ unclaimed.push(mounted);
184
+ }
185
+ }
186
+ return { mountedSet, unclaimed };
187
+ }
188
+ export function patchNode(kept, fresh) {
189
+ assert(kept.nodeName === fresh.nodeName, "patching nodes of different types");
190
+ // Remove `kept` attributes that aren't on `fresh`, then add `fresh` attributes not on `kept`.
191
+ for (const { name } of Array.from(kept.attributes)) {
192
+ if (!fresh.hasAttribute(name)) {
193
+ kept.removeAttribute(name);
194
+ }
195
+ }
196
+ for (const { name, value } of Array.from(fresh.attributes)) {
197
+ if (kept.getAttribute(name) !== value) {
198
+ kept.setAttribute(name, value);
199
+ }
200
+ }
201
+ // Similar to attributes, but operating in a map on the side rather than the node itself.
202
+ kept[Events] ??= new Map();
203
+ const keptEvents = kept[Events];
204
+ const freshEvents = fresh[Events] ?? new Map();
205
+ for (const [type] of keptEvents) {
206
+ if (!freshEvents.has(type)) {
207
+ clearListener(kept, keptEvents, type);
208
+ }
209
+ }
210
+ for (const [type, handler] of freshEvents) {
211
+ setListener(kept, keptEvents, type, handler);
212
+ }
213
+ // Custom elements rebuild their own subtrees
214
+ if (customElements.get(kept.localName))
215
+ return;
216
+ reconcileChildren(kept, Array.from(fresh.childNodes));
217
+ }
@@ -0,0 +1,10 @@
1
+ import { type DenormChildren, type DomAttrs } from "./dom.ts";
2
+ export type Attrs<S> = S & Partial<DomAttrs>;
3
+ export declare const State: unique symbol;
4
+ export interface FCComponent<Props extends object, State extends object> extends Element {
5
+ [State]?: Partial<State>;
6
+ update(attrs?: Partial<Attrs<Props> & DomAttrs> | DenormChildren, ...children: DenormChildren[]): this;
7
+ }
8
+ export type RenderFn<Props extends object, State extends object> = (el: FCComponent<Props, State>, attrs: Attrs<Props>, children: DenormChildren[]) => Element | Element[];
9
+ export type FCComponentCtor<Props extends object, State extends object> = (attrs?: Attrs<Props> | DenormChildren, ...children: DenormChildren[]) => FCComponent<Props, State>;
10
+ export declare function FC<Props extends object, State extends object = object>(name: string, component: RenderFn<Props, State>): FCComponentCtor<Props, State>;