@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.
- package/README.md +0 -3
- package/lib/esm/assert.d.ts +26 -0
- package/lib/esm/assert.js +38 -0
- package/lib/esm/awaitable.js +1 -0
- package/lib/esm/case.d.ts +1 -0
- package/lib/esm/case.js +5 -0
- package/lib/esm/components/accordion.d.ts +5 -0
- package/lib/esm/components/accordion.js +9 -0
- package/lib/esm/components/alert.d.ts +7 -0
- package/lib/esm/components/alert.js +31 -0
- package/lib/esm/components/button_bar.d.ts +8 -0
- package/lib/esm/components/button_bar.js +25 -0
- package/lib/esm/components/card.d.ts +8 -0
- package/lib/esm/components/card.js +31 -0
- package/lib/esm/components/children.d.ts +2 -0
- package/lib/esm/components/children.js +7 -0
- package/lib/esm/components/form.d.ts +5 -0
- package/lib/esm/components/form.js +13 -0
- package/lib/esm/components/index.d.ts +9 -0
- package/lib/esm/components/index.js +10 -0
- package/lib/esm/components/inline_edit.d.ts +12 -0
- package/lib/esm/components/inline_edit.js +48 -0
- package/lib/esm/components/link.d.ts +5 -0
- package/lib/esm/components/link.js +11 -0
- package/lib/esm/components/logger.d.ts +6 -0
- package/lib/esm/components/logger.js +22 -0
- package/lib/esm/components/modal.d.ts +2 -0
- package/lib/esm/components/modal.js +10 -0
- package/lib/esm/components/nav.d.ts +11 -0
- package/lib/esm/components/nav.js +27 -0
- package/lib/esm/components/property.d.ts +9 -0
- package/lib/esm/components/property.js +16 -0
- package/lib/esm/components/select.d.ts +10 -0
- package/lib/esm/components/select.js +3 -0
- package/lib/esm/components/tabs.d.ts +20 -0
- package/lib/esm/components/tabs.js +45 -0
- package/lib/esm/components/virtual_scroll.d.ts +42 -0
- package/lib/esm/components/virtual_scroll.js +94 -0
- package/lib/esm/debounce.d.ts +1 -0
- package/lib/esm/debounce.js +11 -0
- package/lib/esm/diff.d.ts +15 -0
- package/lib/esm/diff.js +50 -0
- package/lib/esm/display.d.ts +5 -0
- package/lib/esm/display.js +11 -0
- package/lib/esm/dom/css/border.d.ts +11 -0
- package/lib/esm/dom/css/border.js +27 -0
- package/lib/esm/dom/css/constants.d.ts +31 -0
- package/lib/esm/dom/css/constants.js +28 -0
- package/lib/esm/dom/css/core.d.ts +5 -0
- package/lib/esm/dom/css/core.js +24 -0
- package/lib/esm/dom/css/fstyle.d.ts +5 -0
- package/lib/esm/dom/css/fstyle.js +32 -0
- package/lib/esm/dom/css/sizing.d.ts +5 -0
- package/lib/esm/dom/css/sizing.js +10 -0
- package/lib/esm/dom/dom.d.ts +36 -0
- package/lib/esm/dom/dom.js +217 -0
- package/lib/esm/dom/fc.d.ts +10 -0
- package/lib/esm/dom/fc.js +32 -0
- package/lib/esm/dom/form/form.app.d.ts +1 -0
- package/lib/esm/dom/form/form.app.js +19 -0
- package/lib/esm/dom/form/form.d.ts +27 -0
- package/lib/esm/dom/form/form.js +65 -0
- package/lib/esm/dom/html.d.ts +112 -0
- package/{src/dom/html.ts → lib/esm/dom/html.js} +2 -14
- package/lib/esm/dom/hydrate.d.ts +39 -0
- package/lib/esm/dom/hydrate.js +187 -0
- package/lib/esm/dom/index.js +2 -0
- package/lib/esm/dom/navigation/index.d.ts +76 -0
- package/lib/esm/dom/navigation/index.js +292 -0
- package/lib/esm/dom/observable.d.ts +2 -0
- package/lib/esm/dom/observable.js +6 -0
- package/lib/esm/dom/provide.d.ts +3 -0
- package/lib/esm/dom/provide.js +7 -0
- package/lib/esm/dom/render.d.ts +8 -0
- package/lib/esm/dom/render.js +28 -0
- package/lib/esm/dom/router/link.d.ts +6 -0
- package/lib/esm/dom/router/link.js +3 -0
- package/lib/esm/dom/router/router.d.ts +13 -0
- package/lib/esm/dom/router/router.js +52 -0
- package/lib/esm/dom/svg.d.ts +64 -0
- package/{src/dom/svg.ts → lib/esm/dom/svg.js} +2 -15
- package/lib/esm/dom/types/css.d.ts +6590 -0
- package/lib/esm/dom/types/css.js +1 -0
- package/lib/esm/dom/types/dom.js +1 -0
- package/lib/esm/dom/types/html.d.ts +614 -0
- package/lib/esm/dom/types/html.js +1 -0
- package/lib/esm/dom/xml.d.ts +1 -0
- package/lib/esm/dom/xml.js +4 -0
- package/lib/esm/equal.d.ts +11 -0
- package/lib/esm/equal.js +43 -0
- package/lib/esm/fs.d.ts +72 -0
- package/lib/esm/fs.js +227 -0
- package/lib/esm/fs_node.d.ts +15 -0
- package/lib/esm/fs_node.js +45 -0
- package/lib/esm/generator.d.ts +1 -0
- package/lib/esm/generator.js +10 -0
- package/lib/esm/lock.d.ts +1 -0
- package/lib/esm/lock.js +23 -0
- package/lib/esm/log.d.ts +69 -0
- package/lib/esm/log.js +211 -0
- package/lib/esm/observable/event.d.ts +35 -0
- package/lib/esm/observable/event.js +46 -0
- package/lib/esm/observable/observable.d.ts +134 -0
- package/lib/esm/observable/observable.js +349 -0
- package/lib/esm/range.d.ts +1 -0
- package/lib/esm/range.js +7 -0
- package/lib/esm/result.d.ts +31 -0
- package/lib/esm/result.js +66 -0
- package/lib/esm/safe.d.ts +1 -0
- package/lib/esm/safe.js +10 -0
- package/lib/esm/server/http/apps.d.ts +5 -0
- package/lib/esm/server/http/apps.js +23 -0
- package/lib/esm/server/http/css.d.ts +5 -0
- package/lib/esm/server/http/css.js +43 -0
- package/lib/esm/server/http/index.d.ts +16 -0
- package/lib/esm/server/http/index.js +78 -0
- package/lib/esm/server/http/response.d.ts +4 -0
- package/lib/esm/server/http/response.js +43 -0
- package/lib/esm/server/http/sitemap.d.ts +2 -0
- package/lib/esm/server/http/sitemap.js +22 -0
- package/lib/esm/server/http/static.d.ts +2 -0
- package/lib/esm/server/http/static.js +22 -0
- package/lib/esm/server/http/typescript.d.ts +5 -0
- package/lib/esm/server/http/typescript.js +40 -0
- package/lib/esm/server/live-reload.d.ts +46 -0
- package/lib/esm/server/live-reload.js +161 -0
- package/lib/esm/server/main.d.ts +2 -0
- package/lib/esm/server/main.js +23 -0
- package/lib/esm/server/ws/frame.d.ts +2 -0
- package/lib/esm/server/ws/frame.js +35 -0
- package/lib/esm/server/ws/handshake.d.ts +4 -0
- package/lib/esm/server/ws/handshake.js +32 -0
- package/lib/esm/server/ws/index.d.ts +14 -0
- package/lib/esm/server/ws/index.js +68 -0
- package/lib/esm/ssg/bundle.d.ts +14 -0
- package/lib/esm/ssg/bundle.js +73 -0
- package/lib/esm/ssg/copy-public.d.ts +6 -0
- package/lib/esm/ssg/copy-public.js +34 -0
- package/lib/esm/ssg/discover.d.ts +15 -0
- package/lib/esm/ssg/discover.js +117 -0
- package/lib/esm/ssg/main.d.ts +2 -0
- package/lib/esm/ssg/main.js +122 -0
- package/lib/esm/ssg/rewrite.d.ts +9 -0
- package/lib/esm/ssg/rewrite.js +15 -0
- package/lib/esm/ssg/ssg.d.ts +26 -0
- package/lib/esm/ssg/ssg.js +84 -0
- package/lib/esm/transpile.d.mts +3 -0
- package/lib/esm/transpile.mjs +12 -0
- package/package.json +19 -10
- package/src/404.html +0 -14
- package/src/assert.ts +0 -56
- package/src/case.ts +0 -5
- package/src/components/_notes +0 -33
- package/src/components/button_bar.ts +0 -42
- package/src/components/inline_edit.ts +0 -78
- package/src/components/logger.ts +0 -35
- package/src/components/select.ts +0 -22
- package/src/components/test.ts +0 -5
- package/src/components/virtual_scroll.test.ts +0 -30
- package/src/components/virtual_scroll.ts +0 -199
- package/src/context.test.ts +0 -58
- package/src/context.ts +0 -67
- package/src/debounce.ts +0 -14
- package/src/diff.test.ts +0 -48
- package/src/diff.ts +0 -82
- package/src/display.ts +0 -18
- package/src/dom/README.md +0 -102
- package/src/dom/css/border.ts +0 -47
- package/src/dom/css/constants.ts +0 -34
- package/src/dom/css/core.ts +0 -28
- package/src/dom/css/fstyle.ts +0 -42
- package/src/dom/css/sizing.ts +0 -11
- package/src/dom/dom.ts +0 -183
- package/src/dom/fc.test.ts +0 -43
- package/src/dom/fc.ts +0 -80
- package/src/dom/form/form.app.ts +0 -50
- package/src/dom/form/form.ts +0 -82
- package/src/dom/form/index.html +0 -15
- package/src/dom/html.test.ts +0 -74
- package/src/dom/observable.test.ts +0 -43
- package/src/dom/observable.ts +0 -11
- package/src/dom/provide.ts +0 -11
- package/src/dom/router/link.ts +0 -14
- package/src/dom/router/router.ts +0 -72
- package/src/dom/test.ts +0 -11
- package/src/dom/types/css.ts +0 -10088
- package/src/dom/types/dom.ts +0 -0
- package/src/dom/types/html.ts +0 -629
- package/src/dom/xml.ts +0 -11
- package/src/equal.test.ts +0 -23
- package/src/equal.ts +0 -66
- package/src/favicon.ico +0 -0
- package/src/flags.test.ts +0 -43
- package/src/flags.ts +0 -53
- package/src/fs.test.ts +0 -106
- package/src/fs.ts +0 -300
- package/src/fs_node.ts +0 -57
- package/src/fs_win.test.ts +0 -11
- package/src/generator.test.ts +0 -27
- package/src/generator.ts +0 -12
- package/src/hooks/_notes +0 -6
- package/src/index.html +0 -82
- package/src/is_browser.js +0 -1
- package/src/lock.test.ts +0 -17
- package/src/lock.ts +0 -23
- package/src/log.ts +0 -155
- package/src/observable/_notes +0 -26
- package/src/observable/event.ts +0 -93
- package/src/observable/observable.test.ts +0 -73
- package/src/observable/observable.ts +0 -484
- package/src/pico/_variables.scss +0 -66
- package/src/pico/components/_accordion.scss +0 -112
- package/src/pico/components/_button-group.scss +0 -51
- package/src/pico/components/_card.scss +0 -47
- package/src/pico/components/_dropdown.scss +0 -203
- package/src/pico/components/_modal.scss +0 -181
- package/src/pico/components/_nav.scss +0 -79
- package/src/pico/components/_progress.scss +0 -70
- package/src/pico/components/_property.scss +0 -34
- package/src/pico/content/_button.scss +0 -152
- package/src/pico/content/_code.scss +0 -63
- package/src/pico/content/_embedded.scss +0 -0
- package/src/pico/content/_form-alt.scss +0 -276
- package/src/pico/content/_form.scss +0 -259
- package/src/pico/content/_misc.scss +0 -0
- package/src/pico/content/_table.scss +0 -28
- package/src/pico/content/_toggle.scss +0 -132
- package/src/pico/content/_typography.scss +0 -232
- package/src/pico/layout/_container.scss +0 -40
- package/src/pico/layout/_document.scss +0 -0
- package/src/pico/layout/_flex.scss +0 -46
- package/src/pico/layout/_grid.scss +0 -24
- package/src/pico/layout/_scroller.scss +0 -16
- package/src/pico/layout/_section.scss +0 -8
- package/src/pico/layout/_sectioning.scss +0 -55
- package/src/pico/pico.scss +0 -60
- package/src/pico/reset/_accessibility.scss +0 -34
- package/src/pico/reset/_button.scss +0 -17
- package/src/pico/reset/_code.scss +0 -15
- package/src/pico/reset/_document.scss +0 -48
- package/src/pico/reset/_embedded.scss +0 -39
- package/src/pico/reset/_form.scss +0 -97
- package/src/pico/reset/_misc.scss +0 -23
- package/src/pico/reset/_nav.scss +0 -5
- package/src/pico/reset/_progress.scss +0 -4
- package/src/pico/reset/_table.scss +0 -8
- package/src/pico/reset/_typography.scss +0 -25
- package/src/pico/themes/default/_colors.scss +0 -65
- package/src/pico/themes/default/_dark.scss +0 -148
- package/src/pico/themes/default/_light.scss +0 -149
- package/src/pico/themes/default/_styles.scss +0 -272
- package/src/pico/themes/default.scss +0 -34
- package/src/pico/utilities/_accessibility.scss +0 -3
- package/src/pico/utilities/_loading.scss +0 -52
- package/src/pico/utilities/_reduce-motion.scss +0 -27
- package/src/pico/utilities/_tooltip.scss +0 -101
- package/src/range.ts +0 -7
- package/src/result.test.ts +0 -101
- package/src/result.ts +0 -107
- package/src/safe.ts +0 -12
- package/src/scope/describe.ts +0 -81
- package/src/scope/display/console.ts +0 -26
- package/src/scope/display/dom.ts +0 -36
- package/src/scope/display/junit.ts +0 -64
- package/src/scope/execute.ts +0 -110
- package/src/scope/expect.ts +0 -169
- package/src/scope/fix.ts +0 -30
- package/src/scope/index.ts +0 -11
- package/src/scope/scope.ts +0 -21
- package/src/scope/state.ts +0 -13
- package/src/server/http/apps.ts +0 -26
- package/src/server/http/css.ts +0 -49
- package/src/server/http/index.ts +0 -127
- package/src/server/http/response.ts +0 -57
- package/src/server/http/sitemap.ts +0 -48
- package/src/server/http/static.ts +0 -30
- package/src/server/http/typescript.ts +0 -46
- package/src/server/main.ts +0 -23
- package/src/test.mjs +0 -33
- package/src/test_all.ts +0 -35
- package/src/transpile.mjs +0 -16
- package/src/zip/spec.txt +0 -3260
- package/tsconfig.json +0 -34
- /package/{src/awaitable.ts → lib/esm/awaitable.d.ts} +0 -0
- /package/{src/dom/index.ts → lib/esm/dom/index.d.ts} +0 -0
- /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,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 {};
|
package/lib/esm/diff.js
ADDED
|
@@ -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,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,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,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>;
|