jops-core 1.0.2 → 1.0.4
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 +27 -3
- package/lib/jops-core.d.ts +291 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -33,18 +33,26 @@ The fastest way to get started. Add these two tags to any HTML page:
|
|
|
33
33
|
<script type="importmap">
|
|
34
34
|
{
|
|
35
35
|
"imports": {
|
|
36
|
-
"jops-core": "https://cdn.
|
|
36
|
+
"jops-core": "https://cdn.jsdelivr.net/npm/jops-core@latest/lib/jops-core.min.js"
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
</script>
|
|
40
|
-
<script src="https://cdn.
|
|
40
|
+
<script src="https://cdn.jsdelivr.net/npm/jops-core@latest/lib/jops-core.min.js" data-app="/src/App.js"></script>
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
The import map lets your app modules use `import { View } from "jops-core"`. The script tag bootstraps the app entry point specified in `data-app`.
|
|
44
44
|
|
|
45
45
|
`App.js` is your entry point — a file you provide that instantiates your root `View` and mounts it to the page. Point `data-app` to wherever you place it.
|
|
46
46
|
|
|
47
|
-
Replace `
|
|
47
|
+
Replace `@latest` with a specific version (e.g. `@1.0.3`) to pin. No build step, no npm, no bundler.
|
|
48
|
+
|
|
49
|
+
For editor autocomplete and inline docs without installing the full package, add jops-core as a dev dependency:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm i -D jops-core
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Your editor picks up `lib/jops-core.d.ts` automatically via the `"types"` field in `package.json`.
|
|
48
56
|
|
|
49
57
|
### npm
|
|
50
58
|
|
|
@@ -295,3 +303,19 @@ EventBus.unsubscribe("user:login", handler);
|
|
|
295
303
|
| --------------------- | -------------------------------------------------------------------------- |
|
|
296
304
|
| `npx jops-core init` | Vendor library, configure `index.html`, inject scripts into `package.json` |
|
|
297
305
|
| `npx jops-core build` | Generate production-ready `dist/` folder |
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Source & Contributing
|
|
310
|
+
|
|
311
|
+
JOPS is distributed as a single minified ES module (`lib/jops-core.min.js`) together with full TypeScript declarations (`lib/jops-core.d.ts`). The source repository is private.
|
|
312
|
+
|
|
313
|
+
This is a deliberate choice. JOPS is maintained by one developer at [UIDB](https://ui-db.com), and keeping the codebase in one pair of hands keeps it small, consistent, and fast to change.
|
|
314
|
+
|
|
315
|
+
What that means for you:
|
|
316
|
+
|
|
317
|
+
- **Free to use.** JOPS is MIT-licensed. Use it in personal, commercial, and client projects.
|
|
318
|
+
- **The API is the contract.** Every public class, method, and lifecycle hook is documented in this README and typed in `jops-core.d.ts`, so your editor gives you autocomplete and inline docs.
|
|
319
|
+
- **Readable stack traces.** The build keeps class and function names (`--keep-names`), so errors point to `View.loadLayout`, not `e.t`.
|
|
320
|
+
- **Backwards compatible within a major version.** Pin `jops-core@1` if you want to control major upgrades.
|
|
321
|
+
- **Feedback goes through issues.** Bug reports and feature requests are welcome in [GitHub Issues](https://github.com/uidb-dev/jops-core/issues), and questions in [Discussions](https://github.com/uidb-dev/jops-core/discussions). The repository doesn't accept pull requests; describe the change in an issue instead.
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
declare class RawHTML {
|
|
2
|
+
html: any;
|
|
3
|
+
constructor(html: any);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Wraps a trusted HTML string to bypass XSS escaping inside a {@link layout} template.
|
|
7
|
+
* @param {string} html - Trusted HTML string to pass through as-is.
|
|
8
|
+
* @returns {RawHTML}
|
|
9
|
+
* @example
|
|
10
|
+
* layout`<p>${raw("<b>trusted</b>")}</p>`
|
|
11
|
+
*/
|
|
12
|
+
export declare const raw: (html: string) => RawHTML;
|
|
13
|
+
/**
|
|
14
|
+
* XSS-safe tagged template literal for defining HTML layouts.
|
|
15
|
+
* Dynamic values are HTML-escaped automatically. Wrap with {@link raw} to opt out.
|
|
16
|
+
* @param {TemplateStringsArray} strings
|
|
17
|
+
* @param {...*} values
|
|
18
|
+
* @returns {string}
|
|
19
|
+
* @example
|
|
20
|
+
* const html = layout`<p>${userInput}</p>`; // escaped
|
|
21
|
+
* const html = layout`<p>${raw("<b>bold</b>")}</p>`; // raw HTML
|
|
22
|
+
*/
|
|
23
|
+
export declare function layout(strings: TemplateStringsArray, ...values: any[]): string;
|
|
24
|
+
/**
|
|
25
|
+
* DOM accessor returned by {@link View.get}.
|
|
26
|
+
* Wraps a single child element for reading and writing.
|
|
27
|
+
*/
|
|
28
|
+
declare class Element {
|
|
29
|
+
element: any;
|
|
30
|
+
constructor(domNode: any, id: any);
|
|
31
|
+
/**
|
|
32
|
+
* Sets the `innerHTML` of the element.
|
|
33
|
+
* @param {string} value - Plain text or HTML string.
|
|
34
|
+
*/
|
|
35
|
+
set(value: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Returns the current `innerHTML` of the element.
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
value(): string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Base class for every screen, component, and sub-component in a JOPS app.
|
|
44
|
+
*
|
|
45
|
+
* Lifecycle order (called by the framework):
|
|
46
|
+
* 1. {@link View#loadLayout} — load or assign `this.layout`
|
|
47
|
+
* 2. {@link View#inflate} — parse `this.layout` into `this.domNode`
|
|
48
|
+
* 3. {@link View#onLayout} — DOM is live; add subviews, read initial data
|
|
49
|
+
* 4. {@link View#onResume} — view becomes visible; called again on every return navigation
|
|
50
|
+
* 5. {@link View#bindEvents} — wire `on*` attributes to class methods
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* import { View, layout } from "jops-core";
|
|
54
|
+
*
|
|
55
|
+
* export default class MyView extends View {
|
|
56
|
+
* constructor() {
|
|
57
|
+
* super();
|
|
58
|
+
* this.layout = layout`<div><h1 id="title"></h1></div>`;
|
|
59
|
+
* this.inflate();
|
|
60
|
+
* }
|
|
61
|
+
* onLayout() { this.get("title").set("Hello!"); }
|
|
62
|
+
* }
|
|
63
|
+
*/
|
|
64
|
+
export declare class View {
|
|
65
|
+
#private;
|
|
66
|
+
subViews: any[];
|
|
67
|
+
layout: any;
|
|
68
|
+
style: string;
|
|
69
|
+
domNode: any;
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} [layout] - Optional layout path or inline HTML string.
|
|
72
|
+
*/
|
|
73
|
+
constructor(layout?: string);
|
|
74
|
+
/**
|
|
75
|
+
* Override to load a layout `.js` module or set `this.layout` inline.
|
|
76
|
+
* Called automatically by the framework before `inflate()`.
|
|
77
|
+
* @param {string} [layout] - Path to a `.js` layout module or an inline HTML string.
|
|
78
|
+
* @returns {Promise<void>}
|
|
79
|
+
*/
|
|
80
|
+
loadLayout(layout?: string): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Injects a `.css` file link or an inline `<style>` tag into `<head>`, deduped.
|
|
83
|
+
* @param {string} style - Path to a `.css` file, or a raw CSS string.
|
|
84
|
+
*/
|
|
85
|
+
setStyle(style: string): void;
|
|
86
|
+
/**
|
|
87
|
+
* Returns an {@link Element} wrapper for the descendant with the given `id`.
|
|
88
|
+
* @param {string} id - The DOM `id` of the target child element.
|
|
89
|
+
* @returns {Element}
|
|
90
|
+
* @example
|
|
91
|
+
* this.get("username").set("Alice");
|
|
92
|
+
*/
|
|
93
|
+
get(id: string): Element;
|
|
94
|
+
/**
|
|
95
|
+
* Wires `on*` HTML attributes on `[type="jops-event-bind"]` elements to
|
|
96
|
+
* same-named methods on this view instance.
|
|
97
|
+
* Called automatically after `onResume()`. Override to add custom wiring.
|
|
98
|
+
*/
|
|
99
|
+
bindEvents(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Parses `this.layout` into a DOM node and assigns it to `this.domNode`.
|
|
102
|
+
* Idempotent — safe to call multiple times.
|
|
103
|
+
*/
|
|
104
|
+
inflate(): void;
|
|
105
|
+
/**
|
|
106
|
+
* Appends `view` into the child element with `id="parentNode"` and runs its
|
|
107
|
+
* full lifecycle (`onLayout` → `onResume` → `bindEvents`).
|
|
108
|
+
* @param {string} parentNode - The `id` of the container element inside `this.domNode`.
|
|
109
|
+
* @param {View} view - The subview to mount.
|
|
110
|
+
* @returns {Promise<void>}
|
|
111
|
+
*/
|
|
112
|
+
addSubView(parentNode: string, view: View): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Sets `this.domNode` to `display: none`.
|
|
115
|
+
*/
|
|
116
|
+
hide(): void;
|
|
117
|
+
/**
|
|
118
|
+
* Removes the inline `display` override, restoring `this.domNode` visibility.
|
|
119
|
+
*/
|
|
120
|
+
show(): void;
|
|
121
|
+
/**
|
|
122
|
+
* Depth-first search across the full subview subtree.
|
|
123
|
+
* @param {function(View): boolean} condition - Predicate to match against each view.
|
|
124
|
+
* @returns {View[]} Flat array of all matching views.
|
|
125
|
+
* @example
|
|
126
|
+
* const forms = root.find(v => v instanceof FormView);
|
|
127
|
+
*/
|
|
128
|
+
find(condition: Function): View[];
|
|
129
|
+
/**
|
|
130
|
+
* Recursively destroys the subview subtree: calls `destroy()` on all
|
|
131
|
+
* subviews, removes `this.domNode` from the DOM, and nulls references.
|
|
132
|
+
*/
|
|
133
|
+
destroy(): void;
|
|
134
|
+
/**
|
|
135
|
+
* Removes `view` from `this.subViews` and calls `view.destroy()`.
|
|
136
|
+
* @param {View} view - The subview to remove.
|
|
137
|
+
*/
|
|
138
|
+
removeSubView(view: View): void;
|
|
139
|
+
/**
|
|
140
|
+
* Inserts `view` at `index` within `this.domNode` and runs its full lifecycle.
|
|
141
|
+
* @param {View} view - The subview to insert.
|
|
142
|
+
* @param {number} index - Zero-based insertion position.
|
|
143
|
+
* @returns {Promise<void>}
|
|
144
|
+
*/
|
|
145
|
+
insertSubView(view: View, index: number): Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* Mounts the view to the DOM, resolves all nested `[type="jops"]` view
|
|
148
|
+
* elements and `<Router>` declarations, and runs each one's full lifecycle.
|
|
149
|
+
* Call once on the root view; nested views are handled automatically.
|
|
150
|
+
* Idempotent — safe to call multiple times.
|
|
151
|
+
* @returns {Promise<void>}
|
|
152
|
+
*/
|
|
153
|
+
render(): Promise<void>;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Static pub/sub event bus for cross-view communication.
|
|
157
|
+
* All methods are static — no instance needed.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* EventBus.subscribe("user:login", data => console.log(data.name));
|
|
161
|
+
* EventBus.send("user:login", { name: "Alice" });
|
|
162
|
+
*/
|
|
163
|
+
export declare class EventBus {
|
|
164
|
+
#private;
|
|
165
|
+
/**
|
|
166
|
+
* Registers a listener for the named event.
|
|
167
|
+
* @param {string} event - Event name.
|
|
168
|
+
* @param {function(*): void} callback - Handler called with the event payload.
|
|
169
|
+
*/
|
|
170
|
+
static subscribe(event: string, callback: Function): void;
|
|
171
|
+
/**
|
|
172
|
+
* Fires the named event to all subscribers.
|
|
173
|
+
* @param {string} event - Event name.
|
|
174
|
+
* @param {*} [data] - Optional payload passed to every subscriber.
|
|
175
|
+
*/
|
|
176
|
+
static send(event: string, data?: any): void;
|
|
177
|
+
/**
|
|
178
|
+
* Removes a specific listener by reference.
|
|
179
|
+
* @param {string} event - Event name.
|
|
180
|
+
* @param {function(*): void} callback - The exact function reference passed to `subscribe`.
|
|
181
|
+
*/
|
|
182
|
+
static unsubscribe(event: string, callback: Function): void;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* App-wide runtime state container. Singleton. No reactivity.
|
|
186
|
+
* Views read in `onResume()` and write in event handlers.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* const store = Store.singleton();
|
|
190
|
+
* store.set("user.name", "Alice");
|
|
191
|
+
* store.get("user.name"); // "Alice"
|
|
192
|
+
*/
|
|
193
|
+
export declare class Store {
|
|
194
|
+
#private;
|
|
195
|
+
_state: {};
|
|
196
|
+
/**
|
|
197
|
+
* Returns the single Store instance, creating it on first call.
|
|
198
|
+
* @returns {Store}
|
|
199
|
+
*/
|
|
200
|
+
static singleton(): Store;
|
|
201
|
+
constructor();
|
|
202
|
+
/**
|
|
203
|
+
* Reads a value by dot-notation path.
|
|
204
|
+
* @param {string} path - e.g. `"user.name"`
|
|
205
|
+
* @returns {*}
|
|
206
|
+
*/
|
|
207
|
+
get(path: string): any;
|
|
208
|
+
/**
|
|
209
|
+
* Writes a value by dot-notation path. Auto-creates intermediate objects.
|
|
210
|
+
* @param {string} path - e.g. `"user.name"`
|
|
211
|
+
* @param {*} value
|
|
212
|
+
*/
|
|
213
|
+
set(path: string, value: any): void;
|
|
214
|
+
/**
|
|
215
|
+
* Resets the entire state to `{}`.
|
|
216
|
+
*/
|
|
217
|
+
clear(): void;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Hash-based SPA router. Singleton. Extends {@link View}.
|
|
221
|
+
* Declared in a layout string as `<Router type="jops" animation="slide">`.
|
|
222
|
+
*
|
|
223
|
+
* Supports two navigation paradigms:
|
|
224
|
+
* - **Flat** (`href="#path"`) — reuses pre-existing view instances (tab navigation).
|
|
225
|
+
* - **Stack** (`navigate()`) — creates a new instance and pushes onto a nav stack (Android `startActivity`).
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* // In a layout file:
|
|
229
|
+
* layout`
|
|
230
|
+
* <Router type="jops" animation="slide">
|
|
231
|
+
* <Home path="/home" type="jops" src="/src/Home.js"></Home>
|
|
232
|
+
* <Detail path="/detail" type="jops" src="/src/Detail.js"></Detail>
|
|
233
|
+
* </Router>
|
|
234
|
+
* `
|
|
235
|
+
*/
|
|
236
|
+
export declare class Router extends View {
|
|
237
|
+
#private;
|
|
238
|
+
rootNode: any;
|
|
239
|
+
routerDomNode: any;
|
|
240
|
+
animation: any;
|
|
241
|
+
routes: {};
|
|
242
|
+
_navStack: any[];
|
|
243
|
+
_baseView: any;
|
|
244
|
+
_pendingBack: boolean;
|
|
245
|
+
_currentView: any;
|
|
246
|
+
/**
|
|
247
|
+
* Returns the single Router instance after it has been rendered.
|
|
248
|
+
* @returns {Router}
|
|
249
|
+
*/
|
|
250
|
+
static singleton(): Router;
|
|
251
|
+
constructor(routerDomNode: any);
|
|
252
|
+
onLayout(): Promise<void>;
|
|
253
|
+
initRouter(): void;
|
|
254
|
+
_animateTransition(outgoing: any, incoming: any, direction: any): Promise<void>;
|
|
255
|
+
_slideTransition(outgoing: any, incoming: any, direction: any): Promise<void>;
|
|
256
|
+
_fadeTransition(outgoing: any, incoming: any): Promise<void>;
|
|
257
|
+
_switchTo(path: any): void;
|
|
258
|
+
/**
|
|
259
|
+
* Navigates back — pops the nav stack with animation if non-empty,
|
|
260
|
+
* otherwise calls `history.back()`.
|
|
261
|
+
*/
|
|
262
|
+
back(): void;
|
|
263
|
+
/**
|
|
264
|
+
* Returns the current URL query params as a plain object.
|
|
265
|
+
* Works for both flat (`href="#path?k=v"`) and stack (`navigate("/path?k=v")`) routes.
|
|
266
|
+
* @returns {Record<string, string>}
|
|
267
|
+
* @example
|
|
268
|
+
* const { id, mode } = Router.singleton().getParams();
|
|
269
|
+
*/
|
|
270
|
+
getParams(): Record<string, string>;
|
|
271
|
+
/**
|
|
272
|
+
* Creates a new view instance for `path`, pushes onto the nav stack,
|
|
273
|
+
* animates forward, and updates the address bar.
|
|
274
|
+
* @param {string} path - Route path, e.g. `"/detail?id=42"`.
|
|
275
|
+
* @param {typeof View} [ClassDef] - Required for dynamic routes not in the static route map.
|
|
276
|
+
* @returns {Promise<void>}
|
|
277
|
+
* @example
|
|
278
|
+
* Router.singleton().navigate("/detail?id=42", DetailView);
|
|
279
|
+
*/
|
|
280
|
+
navigate(path: string, ClassDef?: typeof View): Promise<void>;
|
|
281
|
+
/**
|
|
282
|
+
* Clears the nav stack and navigates to `path`. Uses the existing flat route
|
|
283
|
+
* instance if available, otherwise creates a new instance from `ClassDef`.
|
|
284
|
+
* Equivalent to Android's `startActivity(FLAG_CLEAR_TOP)`.
|
|
285
|
+
* @param {string} path - Route path.
|
|
286
|
+
* @param {typeof View} [ClassDef] - Required when the path is not in the static route map.
|
|
287
|
+
* @returns {Promise<void>}
|
|
288
|
+
*/
|
|
289
|
+
replace(path: string, ClassDef?: typeof View): Promise<void>;
|
|
290
|
+
}
|
|
291
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jops-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "A no-bundler, OOP-first SPA framework with Android-style view composition",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./lib/jops-core.min.js",
|
|
7
7
|
"browser": "./lib/jops-core.min.js",
|
|
8
|
+
"types": "./lib/jops-core.d.ts",
|
|
8
9
|
"bin": {
|
|
9
10
|
"jops-core": "bin/init.js"
|
|
10
11
|
},
|
|
11
12
|
"files": [
|
|
12
13
|
"lib/jops-core.min.js",
|
|
14
|
+
"lib/jops-core.d.ts",
|
|
13
15
|
"bin/init.js",
|
|
14
16
|
"templates/"
|
|
15
17
|
],
|
|
16
18
|
"scripts": {
|
|
17
|
-
"build": "esbuild src/jops-core.js --bundle --format=esm --minify --keep-names --outfile=lib/jops-core.min.js && npm pack"
|
|
19
|
+
"build": "esbuild src/jops-core.js --bundle --format=esm --minify --keep-names --outfile=lib/jops-core.min.js && tsc -p tsconfig.types.json && npm pack"
|
|
18
20
|
},
|
|
19
21
|
"homepage": "https://cdn.ui-db.com",
|
|
20
22
|
"repository": {
|
|
@@ -46,7 +48,8 @@
|
|
|
46
48
|
"author": "arik@ui-db.com",
|
|
47
49
|
"license": "MIT",
|
|
48
50
|
"devDependencies": {
|
|
49
|
-
"esbuild": "^0.25.12"
|
|
51
|
+
"esbuild": "^0.25.12",
|
|
52
|
+
"typescript": "^7.0.2"
|
|
50
53
|
},
|
|
51
54
|
"packageManager": "npm@12.0.2+sha512.b885e890b9418fa1693544d05f53e64f9a73ec194837d4258b15fecdd692347b1dd2a517b1b0cbaf9d31cd8e92c3b70956bd2ecc72833a57b4b3098f5bfa7943"
|
|
52
55
|
}
|