ctrl-fx 0.0.1 → 0.1.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 +13 -0
- package/dist/chunk-DZAR6PTR.js +3966 -0
- package/dist/chunk-KNHJPAIU.js +1800 -0
- package/dist/chunk-NSWOTCDU.js +916 -0
- package/dist/chunk-PKBMQBKP.js +7 -0
- package/dist/chunk-XICUXW4T.js +252 -0
- package/dist/db/index.d.ts +234 -0
- package/dist/db/index.js +53 -0
- package/dist/dom/index.d.ts +2 -0
- package/dist/dom/index.js +30 -0
- package/dist/effects.d.ts +2 -0
- package/dist/effects.js +111 -0
- package/dist/index-fpCmXVEu.d.ts +1391 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/router.d.ts +39 -0
- package/dist/router.js +66 -0
- package/dist/testing.d.ts +254 -0
- package/dist/testing.js +40 -0
- package/dist/webcomponent.d.ts +18 -0
- package/dist/webcomponent.js +51 -0
- package/package.json +4 -3
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isState,
|
|
3
|
+
manageApplication,
|
|
4
|
+
permission,
|
|
5
|
+
toComponent,
|
|
6
|
+
uuidFromString
|
|
7
|
+
} from "./chunk-DZAR6PTR.js";
|
|
8
|
+
import "./chunk-NSWOTCDU.js";
|
|
9
|
+
import "./chunk-KNHJPAIU.js";
|
|
10
|
+
import "./chunk-XICUXW4T.js";
|
|
11
|
+
import "./chunk-PKBMQBKP.js";
|
|
12
|
+
export {
|
|
13
|
+
isState,
|
|
14
|
+
manageApplication,
|
|
15
|
+
permission,
|
|
16
|
+
toComponent,
|
|
17
|
+
uuidFromString
|
|
18
|
+
};
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { I as InternalLocation, E as Effect, a as EventActions } from './index-fpCmXVEu.js';
|
|
2
|
+
import './db/index.js';
|
|
3
|
+
|
|
4
|
+
/** Named path-segment parameters extracted from a matched route. */
|
|
5
|
+
type Params = Record<string, string>;
|
|
6
|
+
/** A map of URL patterns to handler functions. Pattern keys use `:param` syntax for named path segments. */
|
|
7
|
+
type RouteMap<Route> = {
|
|
8
|
+
[pattern: string]: (params: Params) => Route;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Creates a route-matching function from a pattern map. Pattern keys use `:param` syntax for
|
|
12
|
+
* named path segments. Returns `null` when no pattern matches.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* const match = router({ '/': () => 'home', '/users/:id': ({ id }) => `user-${id}` })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare function router<Route>(routes: RouteMap<Route>): (location: InternalLocation) => Route | null;
|
|
19
|
+
/**
|
|
20
|
+
* An effect describing a `history.pushState` to `path`, suitable for use as an `onClick` handler on an `<a>` element.
|
|
21
|
+
* Produces `{ preventDefault: true }` to suppress the default browser navigation.
|
|
22
|
+
*/
|
|
23
|
+
declare function link<State, Event>(path: string): Effect<State, Event, EventActions>;
|
|
24
|
+
/** An effect describing a `history.pushState` to `path`. Use in effect chains or button handlers. */
|
|
25
|
+
declare function navigate<State, Event>(path: string): Effect<State, Event, void>;
|
|
26
|
+
/**
|
|
27
|
+
* Bundled router that strips `options.basePath` before matching and prepends it before navigating.
|
|
28
|
+
* Use when your app is mounted at a sub-path (e.g. a GitHub Pages subdirectory).
|
|
29
|
+
* Returns a `{ match, link, navigate }` object.
|
|
30
|
+
*/
|
|
31
|
+
declare function createRouter<Route>(routes: RouteMap<Route>, options?: {
|
|
32
|
+
basePath?: string;
|
|
33
|
+
}): {
|
|
34
|
+
match(location: InternalLocation): Route | null;
|
|
35
|
+
link<State, Event>(path: string): Effect<State, Event, EventActions>;
|
|
36
|
+
navigate<State, Event>(path: string): Effect<State, Event, void>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export { EventActions, InternalLocation, type Params, type RouteMap, createRouter, link, navigate, router };
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
async,
|
|
3
|
+
parseInternalLocation,
|
|
4
|
+
pushState
|
|
5
|
+
} from "./chunk-KNHJPAIU.js";
|
|
6
|
+
import "./chunk-PKBMQBKP.js";
|
|
7
|
+
|
|
8
|
+
// src/router.ts
|
|
9
|
+
function compile(routes) {
|
|
10
|
+
return Object.entries(routes).map(([pattern, handler]) => ({
|
|
11
|
+
segments: pattern.split("/"),
|
|
12
|
+
handler
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
function matchRoute(compiled, location) {
|
|
16
|
+
const pathSegs = location.path.elems;
|
|
17
|
+
for (const { segments, handler } of compiled) {
|
|
18
|
+
if (segments.length !== pathSegs.length) continue;
|
|
19
|
+
const params = {};
|
|
20
|
+
let matched = true;
|
|
21
|
+
for (let i = 0; i < segments.length; i++) {
|
|
22
|
+
const seg = segments[i];
|
|
23
|
+
if (seg.startsWith(":")) {
|
|
24
|
+
params[seg.slice(1)] = pathSegs[i];
|
|
25
|
+
} else if (seg.toLowerCase() !== pathSegs[i].toLowerCase()) {
|
|
26
|
+
matched = false;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (matched) return handler(params);
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function router(routes) {
|
|
35
|
+
const compiled = compile(routes);
|
|
36
|
+
return (location) => matchRoute(compiled, location);
|
|
37
|
+
}
|
|
38
|
+
function link(path) {
|
|
39
|
+
return async(pushState(parseInternalLocation(path)), 0).as({
|
|
40
|
+
preventDefault: true
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function navigate(path) {
|
|
44
|
+
return pushState(parseInternalLocation(path));
|
|
45
|
+
}
|
|
46
|
+
function createRouter(routes, options) {
|
|
47
|
+
const compiled = compile(routes);
|
|
48
|
+
const base = options?.basePath ?? "";
|
|
49
|
+
return {
|
|
50
|
+
match(location) {
|
|
51
|
+
if (!base) return matchRoute(compiled, location);
|
|
52
|
+
const fullPath = location.path.elems.join("/");
|
|
53
|
+
if (!fullPath.startsWith(base)) return null;
|
|
54
|
+
const relative = fullPath.slice(base.length) || "/";
|
|
55
|
+
return matchRoute(compiled, parseInternalLocation(relative));
|
|
56
|
+
},
|
|
57
|
+
link: (path) => link(base + path),
|
|
58
|
+
navigate: (path) => navigate(base + path)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
createRouter,
|
|
63
|
+
link,
|
|
64
|
+
navigate,
|
|
65
|
+
router
|
|
66
|
+
};
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { A as Attr, H as HttpRequest, R as Result, b as HttpResponse, c as RequestError, d as HttpError, I as InternalLocation, S as ScrollOptions, N as NodeGroup, e as Node, E as Effect, C as Component } from './index-fpCmXVEu.js';
|
|
2
|
+
import { KeyPath, JsonValue, DbName, ObjectStore } from './db/index.js';
|
|
3
|
+
|
|
4
|
+
type NonEmptyArray<A> = [A, ...A[]];
|
|
5
|
+
|
|
6
|
+
type DbStoreSnapshot = {
|
|
7
|
+
readonly keyPath: KeyPath;
|
|
8
|
+
readonly records: ReadonlyMap<string, JsonValue>;
|
|
9
|
+
readonly autoIncrementCounter: number;
|
|
10
|
+
};
|
|
11
|
+
declare class TestDatabaseData {
|
|
12
|
+
readonly _databases: ReadonlyMap<string, ReadonlyMap<string, DbStoreSnapshot>>;
|
|
13
|
+
constructor(databases: ReadonlyMap<string, ReadonlyMap<string, DbStoreSnapshot>>);
|
|
14
|
+
records(db: DbName, store: ObjectStore): readonly JsonValue[];
|
|
15
|
+
getByKey(db: DbName, store: ObjectStore, key: IDBValidKey): JsonValue | undefined;
|
|
16
|
+
size(db: DbName, store: ObjectStore): number;
|
|
17
|
+
hasDatabase(db: DbName): boolean;
|
|
18
|
+
hasStore(db: DbName, store: ObjectStore): boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type DownloadRecord = {
|
|
22
|
+
readonly filename: string;
|
|
23
|
+
readonly content: string;
|
|
24
|
+
readonly contentType: string;
|
|
25
|
+
};
|
|
26
|
+
type BroadcastSentRecord = {
|
|
27
|
+
readonly channel: string;
|
|
28
|
+
readonly message: unknown;
|
|
29
|
+
};
|
|
30
|
+
type SetAppBadge = {
|
|
31
|
+
_type: 'SetAppBadge';
|
|
32
|
+
count: number;
|
|
33
|
+
};
|
|
34
|
+
type ClearAppBadge = {
|
|
35
|
+
_type: 'ClearAppBadge';
|
|
36
|
+
};
|
|
37
|
+
type AppBadgeOperation = SetAppBadge | ClearAppBadge;
|
|
38
|
+
declare const clearAppBadge: ClearAppBadge;
|
|
39
|
+
declare function setAppBadge(count: number): SetAppBadge;
|
|
40
|
+
declare function appBadgeCount(operations: readonly AppBadgeOperation[]): number;
|
|
41
|
+
type HttpInteraction = {
|
|
42
|
+
readonly request: HttpRequest;
|
|
43
|
+
readonly response: Result<HttpResponse, RequestError | HttpError>;
|
|
44
|
+
};
|
|
45
|
+
type HttpHandler = (request: HttpRequest, prior: readonly HttpInteraction[]) => Result<HttpResponse, RequestError | HttpError>;
|
|
46
|
+
/** Configuration passed to `testApplication`/`testComponent` to control how side effects behave during tests. */
|
|
47
|
+
type TestConfig = {
|
|
48
|
+
readonly http: {
|
|
49
|
+
/** Called for each HTTP request. Throw or return a failure Result to simulate errors. */
|
|
50
|
+
readonly handler: HttpHandler;
|
|
51
|
+
};
|
|
52
|
+
readonly clock: {
|
|
53
|
+
/** Scripted return values for `getTime()`, consumed in order. When exhausted, returns `new Date(0)`. */
|
|
54
|
+
readonly values: readonly Date[];
|
|
55
|
+
};
|
|
56
|
+
readonly random: {
|
|
57
|
+
/** Scripted return values for `getRandom()`, consumed in order. When exhausted, uses a deterministic seeded PRNG. */
|
|
58
|
+
readonly values: readonly number[];
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/** Creates a TestConfig that throws on any HTTP request. */
|
|
62
|
+
declare function defaultTestConfig(): TestConfig;
|
|
63
|
+
type ConfirmInteraction = {
|
|
64
|
+
message: string;
|
|
65
|
+
response: boolean;
|
|
66
|
+
};
|
|
67
|
+
type PromptInteraction = {
|
|
68
|
+
message: string;
|
|
69
|
+
default: string | undefined;
|
|
70
|
+
response: string | null;
|
|
71
|
+
};
|
|
72
|
+
/** Snapshot of every observable side effect that occurred during a test run. */
|
|
73
|
+
type TestData<Custom = {}> = {
|
|
74
|
+
readonly window: {
|
|
75
|
+
readonly alerts: readonly any[];
|
|
76
|
+
readonly confirms: readonly ConfirmInteraction[];
|
|
77
|
+
readonly confirmResponses: readonly boolean[];
|
|
78
|
+
readonly prompts: readonly PromptInteraction[];
|
|
79
|
+
readonly promptResponses: readonly (string | null)[];
|
|
80
|
+
};
|
|
81
|
+
readonly navigator: {
|
|
82
|
+
readonly appBadgeOperations: readonly AppBadgeOperation[];
|
|
83
|
+
};
|
|
84
|
+
readonly clipboard: {
|
|
85
|
+
readonly content: string;
|
|
86
|
+
readonly writes: readonly string[];
|
|
87
|
+
};
|
|
88
|
+
readonly document: {
|
|
89
|
+
readonly title: string | null;
|
|
90
|
+
};
|
|
91
|
+
readonly http: {
|
|
92
|
+
readonly interactions: readonly HttpInteraction[];
|
|
93
|
+
};
|
|
94
|
+
readonly clock: {
|
|
95
|
+
/** Values returned by each `getTime()` call, in order. */
|
|
96
|
+
readonly calls: readonly Date[];
|
|
97
|
+
};
|
|
98
|
+
readonly random: {
|
|
99
|
+
/** Values returned by each `getRandom()` call, in order. */
|
|
100
|
+
readonly calls: readonly number[];
|
|
101
|
+
};
|
|
102
|
+
readonly console: {
|
|
103
|
+
readonly logs: readonly any[];
|
|
104
|
+
};
|
|
105
|
+
readonly db: TestDatabaseData;
|
|
106
|
+
readonly storage: {
|
|
107
|
+
readonly local: ReadonlyMap<string, string>;
|
|
108
|
+
readonly session: ReadonlyMap<string, string>;
|
|
109
|
+
};
|
|
110
|
+
readonly navigation: {
|
|
111
|
+
readonly locationHistory: readonly InternalLocation[];
|
|
112
|
+
};
|
|
113
|
+
readonly scroll: {
|
|
114
|
+
readonly windowScrolls: readonly ScrollOptions[];
|
|
115
|
+
readonly elementScrolls: readonly {
|
|
116
|
+
selector: string;
|
|
117
|
+
options: ScrollOptions;
|
|
118
|
+
}[];
|
|
119
|
+
};
|
|
120
|
+
readonly downloads: readonly DownloadRecord[];
|
|
121
|
+
readonly broadcasts: {
|
|
122
|
+
readonly sent: readonly BroadcastSentRecord[];
|
|
123
|
+
};
|
|
124
|
+
readonly custom: Custom;
|
|
125
|
+
};
|
|
126
|
+
/** Creates an empty TestData, optionally initialising the `custom` field. */
|
|
127
|
+
declare function newTestData<Custom = {}>(custom?: Custom): TestData<Custom>;
|
|
128
|
+
type TestInput<State, Params, Custom = {}> = {
|
|
129
|
+
state?: State;
|
|
130
|
+
params?: Params;
|
|
131
|
+
testData?: TestData<Custom>;
|
|
132
|
+
};
|
|
133
|
+
type Click = {
|
|
134
|
+
type: 'Click';
|
|
135
|
+
selector: QuerySelector;
|
|
136
|
+
};
|
|
137
|
+
/** Simulates a click on the element(s) matching `selector`. */
|
|
138
|
+
declare function click(selector: QuerySelector): Click;
|
|
139
|
+
type AdvanceTime = {
|
|
140
|
+
_type: 'AdvanceTime';
|
|
141
|
+
milliseconds: number;
|
|
142
|
+
};
|
|
143
|
+
/** Advances the test scheduler clock by `milliseconds`, firing any scheduled tasks that come due. */
|
|
144
|
+
declare function advanceTime(milliseconds: number): AdvanceTime;
|
|
145
|
+
type ReceiveBroadcast = {
|
|
146
|
+
_type: 'ReceiveBroadcast';
|
|
147
|
+
channel: string;
|
|
148
|
+
message: unknown;
|
|
149
|
+
};
|
|
150
|
+
/** Delivers a BroadcastChannel message to any active subscriber on `channel`. */
|
|
151
|
+
declare function receiveBroadcast(channel: string, message: unknown): ReceiveBroadcast;
|
|
152
|
+
type FireCustomEvent = {
|
|
153
|
+
_type: 'FireCustomEvent';
|
|
154
|
+
selector: QuerySelector;
|
|
155
|
+
eventName: string;
|
|
156
|
+
detail: unknown;
|
|
157
|
+
};
|
|
158
|
+
/** Dispatches a custom DOM event on the element matching `selector`, invoking any `onEvent` handler. */
|
|
159
|
+
declare function fireCustomEvent(selector: QuerySelector, eventName: string, detail?: unknown): FireCustomEvent;
|
|
160
|
+
type Submit = {
|
|
161
|
+
_type: 'Submit';
|
|
162
|
+
selector: QuerySelector;
|
|
163
|
+
};
|
|
164
|
+
/** Simulates a form submit event on the element(s) matching `selector`. */
|
|
165
|
+
declare function submit(selector: QuerySelector): Submit;
|
|
166
|
+
type TextInput = {
|
|
167
|
+
_type: 'TextInput';
|
|
168
|
+
selector: QuerySelector;
|
|
169
|
+
value: string;
|
|
170
|
+
};
|
|
171
|
+
/** Simulates an `input` event on the element(s) matching `selector`. */
|
|
172
|
+
declare function textInput(selector: QuerySelector, value: string): TextInput;
|
|
173
|
+
/** Creates an attribute object `{ name, value }` for use with `findOne(...).attrs.toContainEqual(attr(...))`. */
|
|
174
|
+
declare function attr(name: string, value: string): {
|
|
175
|
+
name: string;
|
|
176
|
+
value: string;
|
|
177
|
+
};
|
|
178
|
+
type Interaction = Click | AdvanceTime | ReceiveBroadcast | FireCustomEvent | Submit | TextInput;
|
|
179
|
+
|
|
180
|
+
type DomElement = {
|
|
181
|
+
readonly tag: string;
|
|
182
|
+
readonly attrs: readonly Attr[];
|
|
183
|
+
readonly childNodes: readonly DomNode[];
|
|
184
|
+
};
|
|
185
|
+
type DomNode = DomElement | string;
|
|
186
|
+
type TestableDom = {
|
|
187
|
+
head: DomElement;
|
|
188
|
+
body: DomElement;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* The result of mounting a component for testing. Supports running interactions
|
|
192
|
+
* and querying the virtual DOM. Each call to `run` returns a new
|
|
193
|
+
* `TestableComponent` reflecting the state after those interactions, so tests
|
|
194
|
+
* can chain calls to build up complex scenarios step by step.
|
|
195
|
+
*/
|
|
196
|
+
type TestableComponent<State, Params, Event, Custom = {}> = {
|
|
197
|
+
readonly config: TestConfig;
|
|
198
|
+
readonly state: State;
|
|
199
|
+
readonly data: TestData<Custom>;
|
|
200
|
+
/** Returns a copy with the given TestConfig. */
|
|
201
|
+
withConfig(config: TestConfig): TestableComponent<State, Params, Event, Custom>;
|
|
202
|
+
/** Returns a copy with the given TestData as the starting data. */
|
|
203
|
+
withData(data: TestData<Custom>): TestableComponent<State, Params, Event, Custom>;
|
|
204
|
+
readonly dom: TestableDom;
|
|
205
|
+
/**
|
|
206
|
+
* Applies one or more interactions in order and returns a new
|
|
207
|
+
* `TestableComponent` reflecting the resulting state and DOM. The original
|
|
208
|
+
* instance is unchanged, so you can branch from the same starting point to
|
|
209
|
+
* test different sequences.
|
|
210
|
+
*/
|
|
211
|
+
run(...interactions: Interaction[]): TestableComponent<State, Params, Event, Custom>;
|
|
212
|
+
/** Returns all elements matching `selector`. */
|
|
213
|
+
find(selector: string): readonly DomElement[];
|
|
214
|
+
/** Returns the single element matching `selector`. Throws if not exactly one match. */
|
|
215
|
+
findOne(selector: string): DomElement;
|
|
216
|
+
/** Returns all elements matching `selector`. Throws if no match. */
|
|
217
|
+
findOneOrMore(selector: string): NonEmptyArray<DomElement>;
|
|
218
|
+
/** Returns the element matching `selector`, or undefined. */
|
|
219
|
+
findMaybeOne(selector: string): DomElement | undefined;
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Creates a testable wrapper around a `Component`. Use this when testing a
|
|
223
|
+
* component that was created with `component()`. For testing a top-level view
|
|
224
|
+
* function directly, use `testApplication` instead.
|
|
225
|
+
*/
|
|
226
|
+
declare function testComponent<State, Params, Event, Custom = {}>(component: Component<State, Params, Event>, config?: TestConfig, data?: TestData<Custom>): TestableComponent<State, Params, Event, Custom>;
|
|
227
|
+
/**
|
|
228
|
+
* The main entry point for testing a ctrl-fx application. Wraps a view
|
|
229
|
+
* function and initial state into a `TestableComponent`. Call `.run(...)`
|
|
230
|
+
* with interactions to simulate user actions, then inspect `.state`, `.data`,
|
|
231
|
+
* and the `findOne`/`find` DOM query methods on the result.
|
|
232
|
+
*/
|
|
233
|
+
declare function testApplication<State, Custom = {}>(app: (state: State) => NodeGroup<State, never> | Node<State, never>, initialState: State | Effect<State, never, State>, config?: TestConfig, data?: TestData<Custom>): TestableComponent<State, void, never, Custom>;
|
|
234
|
+
type ExactlyOne = {
|
|
235
|
+
_type: 'ExactlyOne';
|
|
236
|
+
selector: string;
|
|
237
|
+
};
|
|
238
|
+
type ZeroOrMore = {
|
|
239
|
+
_type: 'ZeroOrMore';
|
|
240
|
+
selector: string;
|
|
241
|
+
};
|
|
242
|
+
type ZeroOrOne = {
|
|
243
|
+
_type: 'ZeroOrOne';
|
|
244
|
+
selector: string;
|
|
245
|
+
};
|
|
246
|
+
type OneOrMore = {
|
|
247
|
+
_type: 'OneOrMore';
|
|
248
|
+
selector: string;
|
|
249
|
+
};
|
|
250
|
+
/** Selects elements matching `selector`; throws during `run` if none are found. */
|
|
251
|
+
declare function one(selector: string): OneOrMore;
|
|
252
|
+
type QuerySelector = ExactlyOne | OneOrMore | ZeroOrMore | ZeroOrOne;
|
|
253
|
+
|
|
254
|
+
export { type AdvanceTime, type AppBadgeOperation, type BroadcastSentRecord, type ClearAppBadge, type Click, type ConfirmInteraction, type DbStoreSnapshot, type DomElement, type DomNode, type DownloadRecord, type ExactlyOne, type FireCustomEvent, HttpError, type HttpHandler, type HttpInteraction, HttpRequest, HttpResponse, type Interaction, InternalLocation, type NonEmptyArray, type OneOrMore, type PromptInteraction, type QuerySelector, type ReceiveBroadcast, RequestError, type SetAppBadge, type Submit, type TestConfig, type TestData, TestDatabaseData, type TestInput, type TestableComponent, type TestableDom, type TextInput, type ZeroOrMore, type ZeroOrOne, advanceTime, appBadgeCount, attr, clearAppBadge, click, defaultTestConfig, fireCustomEvent, newTestData, one, receiveBroadcast, setAppBadge, submit, testApplication, testComponent, textInput };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TestDatabaseData,
|
|
3
|
+
advanceTime,
|
|
4
|
+
appBadgeCount,
|
|
5
|
+
attr,
|
|
6
|
+
clearAppBadge,
|
|
7
|
+
click,
|
|
8
|
+
defaultTestConfig,
|
|
9
|
+
fireCustomEvent,
|
|
10
|
+
newTestData,
|
|
11
|
+
one,
|
|
12
|
+
receiveBroadcast,
|
|
13
|
+
setAppBadge,
|
|
14
|
+
submit,
|
|
15
|
+
testApplication,
|
|
16
|
+
testComponent,
|
|
17
|
+
textInput
|
|
18
|
+
} from "./chunk-DZAR6PTR.js";
|
|
19
|
+
import "./chunk-NSWOTCDU.js";
|
|
20
|
+
import "./chunk-KNHJPAIU.js";
|
|
21
|
+
import "./chunk-XICUXW4T.js";
|
|
22
|
+
import "./chunk-PKBMQBKP.js";
|
|
23
|
+
export {
|
|
24
|
+
TestDatabaseData,
|
|
25
|
+
advanceTime,
|
|
26
|
+
appBadgeCount,
|
|
27
|
+
attr,
|
|
28
|
+
clearAppBadge,
|
|
29
|
+
click,
|
|
30
|
+
defaultTestConfig,
|
|
31
|
+
fireCustomEvent,
|
|
32
|
+
newTestData,
|
|
33
|
+
one,
|
|
34
|
+
receiveBroadcast,
|
|
35
|
+
setAppBadge,
|
|
36
|
+
submit,
|
|
37
|
+
testApplication,
|
|
38
|
+
testComponent,
|
|
39
|
+
textInput
|
|
40
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { C as Component } from './index-fpCmXVEu.js';
|
|
2
|
+
import './db/index.js';
|
|
3
|
+
|
|
4
|
+
/** Options for `defineWebComponent`. */
|
|
5
|
+
type WebComponentOptions<Event> = {
|
|
6
|
+
/** Maps a typed component event to a DOM custom-event name. */
|
|
7
|
+
events?: (event: Event) => string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Registers a ctrl-fx Component as a native custom element. Component events are dispatched as
|
|
11
|
+
* CustomEvents using the name returned by `options.events`.
|
|
12
|
+
* @param tagName the custom element tag name (must contain a hyphen)
|
|
13
|
+
* @param component the ctrl-fx component to mount
|
|
14
|
+
* @param options optional configuration
|
|
15
|
+
*/
|
|
16
|
+
declare function defineWebComponent<State, Event>(tagName: string, component: Component<State, void, Event>, options?: WebComponentOptions<Event>): void;
|
|
17
|
+
|
|
18
|
+
export { type WebComponentOptions, defineWebComponent };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ComponentManager,
|
|
3
|
+
domInterpreter,
|
|
4
|
+
realStyleRegistry
|
|
5
|
+
} from "./chunk-DZAR6PTR.js";
|
|
6
|
+
import "./chunk-NSWOTCDU.js";
|
|
7
|
+
import "./chunk-KNHJPAIU.js";
|
|
8
|
+
import "./chunk-XICUXW4T.js";
|
|
9
|
+
import {
|
|
10
|
+
__publicField
|
|
11
|
+
} from "./chunk-PKBMQBKP.js";
|
|
12
|
+
|
|
13
|
+
// src/webcomponent.ts
|
|
14
|
+
function defineWebComponent(tagName, component, options) {
|
|
15
|
+
class CtrlFxElement extends HTMLElement {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
__publicField(this, "_mgr", null);
|
|
19
|
+
}
|
|
20
|
+
connectedCallback() {
|
|
21
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
22
|
+
const mount = document.createElement("div");
|
|
23
|
+
shadow.appendChild(mount);
|
|
24
|
+
this._mgr = new ComponentManager(
|
|
25
|
+
component,
|
|
26
|
+
mount,
|
|
27
|
+
globalThis.window,
|
|
28
|
+
domInterpreter,
|
|
29
|
+
realStyleRegistry(),
|
|
30
|
+
(event) => {
|
|
31
|
+
const eventName = options?.events ? options.events(event) : "event";
|
|
32
|
+
this.dispatchEvent(
|
|
33
|
+
new CustomEvent(eventName, {
|
|
34
|
+
detail: event,
|
|
35
|
+
bubbles: true,
|
|
36
|
+
composed: true
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
disconnectedCallback() {
|
|
43
|
+
this._mgr?.destroy();
|
|
44
|
+
this._mgr = null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
customElements.define(tagName, CtrlFxElement);
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
defineWebComponent
|
|
51
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ctrl-fx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A no-dependency, purely functional TypeScript framework for building web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"dev": "vite --host",
|
|
28
28
|
"clean": "rm -rf dist",
|
|
29
29
|
"build": "tsc && vite build --config doc/examples/vite.config.ts doc/examples",
|
|
30
|
-
"build:lib": "
|
|
30
|
+
"build:lib": "tsup",
|
|
31
31
|
"preview": "vite preview",
|
|
32
32
|
"test": "vitest run",
|
|
33
33
|
"test:watch": "vitest --watch",
|
|
@@ -74,12 +74,13 @@
|
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@eslint/js": "^10.0.1",
|
|
77
|
-
"typedoc": "^0.28.19",
|
|
78
77
|
"eslint": "^10.4.1",
|
|
79
78
|
"highlight.js": "^11.11.1",
|
|
80
79
|
"prettier": "^3.8.3",
|
|
81
80
|
"ts-node": "^10.9.2",
|
|
81
|
+
"tsup": "^8.5.1",
|
|
82
82
|
"tsx": "^4.20.6",
|
|
83
|
+
"typedoc": "^0.28.19",
|
|
83
84
|
"typescript": "~5.8.3",
|
|
84
85
|
"typescript-eslint": "^8.60.0",
|
|
85
86
|
"vitest": "^3.2.4"
|