@adukiorg/anza 0.2.0 → 0.2.3
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/CHANGELOG.md +90 -4
- package/README.md +97 -133
- package/bin/anza/anza-linux-arm64 +0 -0
- package/bin/anza/anza-linux-x64 +0 -0
- package/bin/anza/anza-macos-arm64 +0 -0
- package/bin/anza/anza-macos-x64 +0 -0
- package/bin/anza/anza-windows-x64.exe +0 -0
- package/bin/anza/find.js +35 -0
- package/bin/anza/index.js +34 -0
- package/bin/anza/launch.js +19 -0
- package/bin/common/index.js +7 -0
- package/bin/common/logs.js +62 -0
- package/bin/create/copy.js +18 -0
- package/bin/create/index.js +45 -0
- package/bin/create/run.js +210 -0
- package/bin/create/write.js +19 -0
- package/importmap.json +4 -0
- package/package.json +16 -10
- package/src/core/offline/{usage.md → notes/usage.md} +11 -1
- package/src/core/router/boot.js +82 -0
- package/src/core/router/cascade.js +76 -0
- package/src/core/router/container.js +63 -72
- package/src/core/router/graph.js +144 -0
- package/src/core/router/index.js +12 -2
- package/src/core/router/intercept.js +26 -7
- package/src/core/router/lca.js +58 -0
- package/src/core/router/match.js +49 -36
- package/src/core/router/notes/audit-old.md +887 -0
- package/src/core/router/notes/audti.md +773 -0
- package/src/core/router/notes/tasks.md +473 -0
- package/src/core/router/{usage.md → notes/usage.md} +57 -35
- package/src/core/router/sync/tab.js +6 -4
- package/src/core/router/transitions.js +35 -8
- package/src/core/router/trie.js +130 -0
- package/src/core/security/{usage.md → notes/usage.md} +1 -2
- package/src/core/storage/{usage.md → notes/usage.md} +6 -6
- package/src/core/theme/index.js +78 -0
- package/src/core/ui/define/index.js +2 -1
- package/src/core/ui/define/orchestrator.js +10 -4
- package/src/core/ui/defs/dock.js +134 -0
- package/src/core/ui/defs/index.js +20 -0
- package/src/core/ui/defs/page.js +89 -0
- package/src/core/ui/defs/part.js +28 -0
- package/src/core/ui/defs/spec.js +96 -0
- package/src/core/ui/defs/view.js +23 -0
- package/src/core/ui/index.js +16 -3
- package/src/core/ui/notes/definations.md +979 -0
- package/src/tokens/index.css +1 -0
- package/src/tokens/semantic/contrast.css +18 -0
- package/src/tokens/semantic/transitions.css +32 -0
- package/types/core/platform/index.d.ts +39 -10
- package/types/core/router/index.d.ts +9 -0
- package/types/core/theme/index.d.ts +18 -0
- package/types/core/ui/index.d.ts +11 -0
- package/types/index.d.ts +1 -0
- package/bin/anza.js +0 -63
- package/bin/create.js +0 -150
- package/src/core/api/plan.md +0 -209
- package/src/core/events/missing.md +0 -103
- package/src/core/events/plan.md +0 -177
- package/src/core/offline/missing.md +0 -89
- package/src/core/offline/plan.md +0 -143
- package/src/core/platform/missing.md +0 -119
- package/src/core/platform/platform.d.ts +0 -88
- package/src/core/router/missing.md +0 -716
- package/src/core/router/outlet.js +0 -139
- package/src/core/router/plan.md +0 -370
- package/src/core/security/missing.md +0 -97
- package/src/core/state/missing.md +0 -165
- package/src/core/storage/missing.md +0 -165
- package/src/core/storage/plan.md +0 -69
- package/src/core/ui/implementation.md +0 -170
- package/src/core/ui/plan.md +0 -510
- package/src/core/ui/ui.types.md +0 -890
- /package/src/core/animations/{usage.md → notes/usage.md} +0 -0
- /package/src/core/api/{usage.md → notes/usage.md} +0 -0
- /package/src/core/events/{usage.md → notes/usage.md} +0 -0
- /package/src/core/platform/{usage.md → notes/usage.md} +0 -0
- /package/src/core/state/{usage.md → notes/usage.md} +0 -0
- /package/src/core/ui/{usage.md → notes/usage.md} +0 -0
- /package/src/core/ui/{watch.md → notes/watch.md} +0 -0
- /package/src/core/workers/{plan.md → notes/plan.md} +0 -0
- /package/src/core/workers/{usage.md → notes/usage.md} +0 -0
package/src/core/ui/ui.types.md
DELETED
|
@@ -1,890 +0,0 @@
|
|
|
1
|
-
# Native UI Type System Plan
|
|
2
|
-
|
|
3
|
-
This document specifies the TypeScript declaration surface for `@adukiorg/anza/ui`. The goal is strict IDE enforcement for component authors using plain JavaScript with JSDoc or TypeScript consumers importing the package declarations.
|
|
4
|
-
|
|
5
|
-
The current runtime source of truth is:
|
|
6
|
-
|
|
7
|
-
- `src/core/ui/index.js`
|
|
8
|
-
- `src/core/ui/base.js`
|
|
9
|
-
- `src/core/ui/schedule.js`
|
|
10
|
-
- `src/core/ui/template.js`
|
|
11
|
-
- `src/core/ui/transitions.js`
|
|
12
|
-
- `src/core/ui/observe.js`
|
|
13
|
-
- `src/core/ui/define/define.js`
|
|
14
|
-
- `src/core/ui/define/element.js`
|
|
15
|
-
- `src/core/ui/define/container.js`
|
|
16
|
-
- `src/core/ui/define/proxy.js`
|
|
17
|
-
|
|
18
|
-
The implemented declaration file is:
|
|
19
|
-
|
|
20
|
-
- `types/core/ui/index.d.ts`
|
|
21
|
-
|
|
22
|
-
Optional generated component-specific files:
|
|
23
|
-
|
|
24
|
-
- `index.tags.d.ts`
|
|
25
|
-
- generated `dist/types/**/*.d.ts`
|
|
26
|
-
|
|
27
|
-
## 1. Goals
|
|
28
|
-
|
|
29
|
-
- Make `ui.element(...)` and `ui.container(...)` strongly typed.
|
|
30
|
-
- Infer component property types from `props`.
|
|
31
|
-
- Type `mount`, `update`, and `unmount` lifecycle contexts.
|
|
32
|
-
- Type injected `refs`, `tags`, `on`, and `watch`.
|
|
33
|
-
- Type `ElementInternals` availability for form-associated elements.
|
|
34
|
-
- Type scheduler, observer, template, and transition utilities according to the actual runtime.
|
|
35
|
-
- Type generated refs from `.tags.json` or optional `index.tags.d.ts`.
|
|
36
|
-
- Catch misspelled prop names, invalid update value types, invalid ref names where possible, and incorrect handler signatures.
|
|
37
|
-
- Preserve ergonomic JavaScript usage through JSDoc-compatible exported types.
|
|
38
|
-
|
|
39
|
-
## 2. Non-Goals
|
|
40
|
-
|
|
41
|
-
- No full CSS selector parser in TypeScript. Selector strings remain `string`.
|
|
42
|
-
- No compile-time verification that a selector exists in the HTML template unless generated template typings are imported.
|
|
43
|
-
- No static guarantee that an arbitrary runtime `tags.one(selector)` is non-null.
|
|
44
|
-
- No framework JSX runtime. JSX custom element typing can be added separately.
|
|
45
|
-
- No type-level HTML parser inside declarations.
|
|
46
|
-
|
|
47
|
-
## 3. Public Exports
|
|
48
|
-
|
|
49
|
-
`@adukiorg/anza/ui` must export:
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
export class BaseElement extends HTMLElement {}
|
|
53
|
-
|
|
54
|
-
export function define<TagName extends string>(
|
|
55
|
-
tagName: TagName,
|
|
56
|
-
elementClass: CustomElementConstructor
|
|
57
|
-
): void;
|
|
58
|
-
|
|
59
|
-
export function element<
|
|
60
|
-
TagName extends string,
|
|
61
|
-
Props extends PropsDefinition = {},
|
|
62
|
-
Refs extends RefsMap = RefsMap,
|
|
63
|
-
Form extends boolean = false
|
|
64
|
-
>(
|
|
65
|
-
tagName: TagName,
|
|
66
|
-
spec: ElementSpec<Props, Refs, Form>,
|
|
67
|
-
base?: string | URL
|
|
68
|
-
): void;
|
|
69
|
-
|
|
70
|
-
export function container<
|
|
71
|
-
TagName extends string,
|
|
72
|
-
Props extends PropsDefinition = {},
|
|
73
|
-
Refs extends RefsMap = RefsMap
|
|
74
|
-
>(
|
|
75
|
-
tagName: TagName,
|
|
76
|
-
spec: ContainerSpec<Props, Refs>,
|
|
77
|
-
base?: string | URL
|
|
78
|
-
): void;
|
|
79
|
-
|
|
80
|
-
export function schedule<T>(
|
|
81
|
-
fn: () => T | Promise<T>,
|
|
82
|
-
priority?: TaskPriority
|
|
83
|
-
): Promise<T>;
|
|
84
|
-
|
|
85
|
-
export function scheduleFrame<T>(fn: () => T): Promise<T>;
|
|
86
|
-
|
|
87
|
-
export function yieldTask(): Promise<void>;
|
|
88
|
-
|
|
89
|
-
export function transition<T>(fn: () => T): Promise<ViewTransitionLike<T>>;
|
|
90
|
-
|
|
91
|
-
export function template(
|
|
92
|
-
strings: TemplateStringsArray,
|
|
93
|
-
...values: unknown[]
|
|
94
|
-
): DocumentFragment;
|
|
95
|
-
|
|
96
|
-
export const observe: ObserveApi;
|
|
97
|
-
|
|
98
|
-
export const ui: UiApi;
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## 4. Primitive Type Aliases
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
export type TaskPriority = 'user-blocking' | 'user-visible' | 'background';
|
|
105
|
-
|
|
106
|
-
export type Constructor<T = HTMLElement> = new (...args: any[]) => T;
|
|
107
|
-
|
|
108
|
-
export type MaybePromise<T> = T | Promise<T>;
|
|
109
|
-
|
|
110
|
-
export type Disposer = () => void;
|
|
111
|
-
|
|
112
|
-
export type RefsMap = Record<string, Element>;
|
|
113
|
-
|
|
114
|
-
export type ElementRefs<T> = {
|
|
115
|
-
[K in keyof T]: Element;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export type RefElement = Element;
|
|
119
|
-
|
|
120
|
-
export type SignalOrOptions<T extends object = {}> =
|
|
121
|
-
| AbortSignal
|
|
122
|
-
| (T & { signal?: AbortSignal; once?: boolean });
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Rationale:
|
|
126
|
-
|
|
127
|
-
- `Disposer` is returned by `on.*` and `watch.*`.
|
|
128
|
-
- `SignalOrOptions` lets the runtime support both `ctrl.signal` and `{ signal, once }`.
|
|
129
|
-
- `RefsMap` defaults to permissive `Record<string, Element>`, but generated template typings can replace it with exact refs.
|
|
130
|
-
|
|
131
|
-
## 5. Prop Definitions
|
|
132
|
-
|
|
133
|
-
Runtime props use constructors in config objects:
|
|
134
|
-
|
|
135
|
-
```javascript
|
|
136
|
-
props: {
|
|
137
|
-
disabled: { type: Boolean, default: false, state: true },
|
|
138
|
-
count: { type: Number, default: 0 },
|
|
139
|
-
label: { type: String, default: 'Untitled' }
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Declaration:
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
export type PropConstructor = BooleanConstructor | NumberConstructor | StringConstructor;
|
|
147
|
-
|
|
148
|
-
export interface PropConfig<T extends PropConstructor = PropConstructor> {
|
|
149
|
-
type: T;
|
|
150
|
-
default?: PropValueFromConstructor<T>;
|
|
151
|
-
state?: boolean;
|
|
152
|
-
reflect?: boolean;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export type AnyPropConfig =
|
|
156
|
-
| PropConfig<BooleanConstructor>
|
|
157
|
-
| PropConfig<NumberConstructor>
|
|
158
|
-
| PropConfig<StringConstructor>;
|
|
159
|
-
|
|
160
|
-
export type PropsDefinition = Record<string, AnyPropConfig>;
|
|
161
|
-
|
|
162
|
-
export type PropValueFromConstructor<T> =
|
|
163
|
-
T extends BooleanConstructor ? boolean :
|
|
164
|
-
T extends NumberConstructor ? number :
|
|
165
|
-
T extends StringConstructor ? string :
|
|
166
|
-
never;
|
|
167
|
-
|
|
168
|
-
export type InferProps<Props extends PropsDefinition> = {
|
|
169
|
-
[K in keyof Props]: PropValueFromConstructor<Props[K]['type']>;
|
|
170
|
-
};
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Strictness requirements:
|
|
174
|
-
|
|
175
|
-
- `default` must match `type`.
|
|
176
|
-
- `update({ name, val, prev })` must narrow by prop key.
|
|
177
|
-
- `el` in lifecycle methods must expose declared props.
|
|
178
|
-
|
|
179
|
-
Example expected IDE behavior:
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
ui.element('ui-count', {
|
|
183
|
-
props: {
|
|
184
|
-
count: { type: Number, default: 0 },
|
|
185
|
-
open: { type: Boolean, default: false }
|
|
186
|
-
},
|
|
187
|
-
update(ctx) {
|
|
188
|
-
if (ctx.name === 'count') {
|
|
189
|
-
ctx.val.toFixed(); // ok, number
|
|
190
|
-
ctx.val.trim(); // error
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## 6. Component Host Type
|
|
197
|
-
|
|
198
|
-
The lifecycle `el` should be the custom element host plus inferred props.
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
export type ComponentHost<Props extends PropsDefinition> =
|
|
202
|
-
HTMLElement & InferProps<Props>;
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
For form-associated elements:
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
export type InternalsFor<Form extends boolean> =
|
|
209
|
-
Form extends true ? ElementInternals : ElementInternals | undefined;
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
This means `internals` is strongly present when `form: true` is statically declared.
|
|
213
|
-
|
|
214
|
-
## 7. Lifecycle Contexts
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
export interface BaseLifecycleContext<
|
|
218
|
-
Props extends PropsDefinition,
|
|
219
|
-
Refs extends ElementRefs<Refs>,
|
|
220
|
-
Form extends boolean = false
|
|
221
|
-
> {
|
|
222
|
-
el: ComponentHost<Props>;
|
|
223
|
-
ctrl: AbortController;
|
|
224
|
-
tags: TagsApi;
|
|
225
|
-
on: EventDelegator;
|
|
226
|
-
refs: Readonly<Refs>;
|
|
227
|
-
watch: WatchApi;
|
|
228
|
-
internals: InternalsFor<Form>;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export type MountContext<
|
|
232
|
-
Props extends PropsDefinition,
|
|
233
|
-
Refs extends RefsMap,
|
|
234
|
-
Form extends boolean = false
|
|
235
|
-
> = BaseLifecycleContext<Props, Refs, Form>;
|
|
236
|
-
|
|
237
|
-
export type UnmountContext<
|
|
238
|
-
Props extends PropsDefinition,
|
|
239
|
-
Refs extends RefsMap,
|
|
240
|
-
Form extends boolean = false
|
|
241
|
-
> = Pick<
|
|
242
|
-
BaseLifecycleContext<Props, Refs, Form>,
|
|
243
|
-
'el' | 'tags' | 'refs' | 'watch' | 'internals'
|
|
244
|
-
>;
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
## 8. Strict Update Context
|
|
248
|
-
|
|
249
|
-
Update must be a discriminated union by prop key:
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
export type UpdateContext<
|
|
253
|
-
Props extends PropsDefinition,
|
|
254
|
-
Refs extends RefsMap,
|
|
255
|
-
Form extends boolean = false
|
|
256
|
-
> = {
|
|
257
|
-
[K in keyof Props & string]:
|
|
258
|
-
BaseLifecycleContext<Props, Refs, Form> & {
|
|
259
|
-
name: K;
|
|
260
|
-
val: PropValueFromConstructor<Props[K]['type']>;
|
|
261
|
-
prev: PropValueFromConstructor<Props[K]['type']>;
|
|
262
|
-
old: PropValueFromConstructor<Props[K]['type']>;
|
|
263
|
-
}
|
|
264
|
-
}[keyof Props & string];
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
This gives IDE narrowing:
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
update(ctx) {
|
|
271
|
-
switch (ctx.name) {
|
|
272
|
-
case 'disabled':
|
|
273
|
-
ctx.val.valueOf(); // boolean
|
|
274
|
-
break;
|
|
275
|
-
case 'count':
|
|
276
|
-
ctx.val.toFixed(); // number
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
## 9. Element Spec
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
export interface ElementSpec<
|
|
286
|
-
Props extends PropsDefinition = {},
|
|
287
|
-
Refs extends RefsMap = RefsMap,
|
|
288
|
-
Form extends boolean = false
|
|
289
|
-
> {
|
|
290
|
-
template?: string;
|
|
291
|
-
style?: string;
|
|
292
|
-
mode?: ShadowRootMode;
|
|
293
|
-
props?: Props;
|
|
294
|
-
form?: Form;
|
|
295
|
-
url?: string;
|
|
296
|
-
container?: string;
|
|
297
|
-
meta?: Record<string, unknown>;
|
|
298
|
-
|
|
299
|
-
mount?: (
|
|
300
|
-
context: MountContext<Props, Refs, Form>
|
|
301
|
-
) => void | Promise<void>;
|
|
302
|
-
|
|
303
|
-
update?: (
|
|
304
|
-
context: UpdateContext<Props, Refs, Form>
|
|
305
|
-
) => void | Promise<void>;
|
|
306
|
-
|
|
307
|
-
unmount?: (
|
|
308
|
-
context: UnmountContext<Props, Refs, Form>
|
|
309
|
-
) => void;
|
|
310
|
-
|
|
311
|
-
methods?: Record<string, (...args: any[]) => any>;
|
|
312
|
-
}
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
Strictness details:
|
|
316
|
-
|
|
317
|
-
- `props` is optional and defaults to `{}`.
|
|
318
|
-
- `mount` and `update` may be async, but errors are not swallowed by the type system.
|
|
319
|
-
- `form: true` narrows `internals` to `ElementInternals`.
|
|
320
|
-
- `Refs` can be supplied manually or generated. Exact ref interfaces do not need a string index signature; every declared ref value only needs to extend `Element`.
|
|
321
|
-
|
|
322
|
-
Manual refs typing:
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
interface ButtonRefs {
|
|
326
|
-
button: HTMLButtonElement;
|
|
327
|
-
status: HTMLSpanElement;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
ui.element<'ui-button', typeof props, ButtonRefs>('ui-button', {
|
|
331
|
-
props,
|
|
332
|
-
mount({ refs }) {
|
|
333
|
-
refs.button.disabled = true; // ok
|
|
334
|
-
refs.missing; // error
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
## 10. Container Spec
|
|
340
|
-
|
|
341
|
-
Containers share the element spec but have router layout behavior.
|
|
342
|
-
|
|
343
|
-
```typescript
|
|
344
|
-
export type ContainerHost<Props extends PropsDefinition> =
|
|
345
|
-
ComponentHost<Props> & {
|
|
346
|
-
swapView(newElement: Element, options?: SwapViewOptions): Promise<void>;
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
export interface SwapViewOptions {
|
|
350
|
-
direction?: 'push' | 'pop' | 'replace' | string;
|
|
351
|
-
params?: Record<string, string>;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
export type ContainerMountContext<
|
|
355
|
-
Props extends PropsDefinition,
|
|
356
|
-
Refs extends RefsMap
|
|
357
|
-
> = Omit<MountContext<Props, Refs, false>, 'el'> & {
|
|
358
|
-
el: ContainerHost<Props>;
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
export interface ContainerSpec<
|
|
362
|
-
Props extends PropsDefinition = {},
|
|
363
|
-
Refs extends RefsMap = RefsMap
|
|
364
|
-
> extends Omit<ElementSpec<Props, Refs, false>, 'mount' | 'update' | 'unmount' | 'form'> {
|
|
365
|
-
mount?: (context: ContainerMountContext<Props, Refs>) => void | Promise<void>;
|
|
366
|
-
update?: (context: UpdateContext<Props, Refs, false>) => void | Promise<void>;
|
|
367
|
-
unmount?: (context: UnmountContext<Props, Refs, false>) => void;
|
|
368
|
-
}
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
## 11. `tags` API
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
export interface TagsApi {
|
|
375
|
-
one<E extends Element = Element>(selector: string): E | null;
|
|
376
|
-
all<E extends Element = Element>(selector: string): E[];
|
|
377
|
-
each<E extends Element = Element>(
|
|
378
|
-
selector: string,
|
|
379
|
-
fn: (element: E, index: number) => void
|
|
380
|
-
): void;
|
|
381
|
-
has(selector: string): boolean;
|
|
382
|
-
clear(): void;
|
|
383
|
-
}
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
Usage:
|
|
387
|
-
|
|
388
|
-
```typescript
|
|
389
|
-
const button = tags.one<HTMLButtonElement>('button');
|
|
390
|
-
button?.disabled = true;
|
|
391
|
-
|
|
392
|
-
const inputs = tags.all<HTMLInputElement>('input');
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
Why generic selectors:
|
|
396
|
-
|
|
397
|
-
- TypeScript cannot reliably infer element type from arbitrary CSS selectors.
|
|
398
|
-
- Explicit generic arguments are simple and honest.
|
|
399
|
-
- Generated refs should be preferred for strongly typed stable elements.
|
|
400
|
-
|
|
401
|
-
## 12. `refs` Typing
|
|
402
|
-
|
|
403
|
-
Default:
|
|
404
|
-
|
|
405
|
-
```typescript
|
|
406
|
-
refs: Readonly<Record<string, Element>>;
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
Strict generated form:
|
|
410
|
-
|
|
411
|
-
```typescript
|
|
412
|
-
export interface TemplateRefs {
|
|
413
|
-
button: HTMLButtonElement;
|
|
414
|
-
status: HTMLSpanElement;
|
|
415
|
-
}
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
Generated `index.tags.d.ts` should export:
|
|
419
|
-
|
|
420
|
-
```typescript
|
|
421
|
-
export interface TemplateRefs {
|
|
422
|
-
readonly button: HTMLButtonElement;
|
|
423
|
-
readonly status: HTMLSpanElement;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
export type TemplateRefName = keyof TemplateRefs;
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
Component author usage:
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
import type { TemplateRefs } from './index.tags';
|
|
433
|
-
|
|
434
|
-
ui.element<'ui-button', typeof props, TemplateRefs>('ui-button', {
|
|
435
|
-
props,
|
|
436
|
-
mount({ refs }) {
|
|
437
|
-
refs.button.disabled = false;
|
|
438
|
-
}
|
|
439
|
-
}, import.meta.url);
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
## 13. `on` Event Delegator
|
|
443
|
-
|
|
444
|
-
Runtime usage:
|
|
445
|
-
|
|
446
|
-
```javascript
|
|
447
|
-
on.click('button', handler)
|
|
448
|
-
on.click('button', handler, ctrl.signal)
|
|
449
|
-
on.click('button', handler, { signal: ctrl.signal, once: true })
|
|
450
|
-
on.click.once('button', handler)
|
|
451
|
-
on['nav:change']('[data-tab]', handler)
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
Types:
|
|
455
|
-
|
|
456
|
-
```typescript
|
|
457
|
-
export interface DelegatedEventOptions extends AddEventListenerOptions {
|
|
458
|
-
signal?: AbortSignal;
|
|
459
|
-
once?: boolean;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
export type DelegatedEventHandler<
|
|
463
|
-
EventType extends Event = Event,
|
|
464
|
-
Target extends Element = Element
|
|
465
|
-
> = (event: EventType, target: Target) => void;
|
|
466
|
-
|
|
467
|
-
export interface DelegatedEventBinder<EventType extends Event = Event> {
|
|
468
|
-
<Target extends Element = Element>(
|
|
469
|
-
selector: string,
|
|
470
|
-
handler: DelegatedEventHandler<EventType, Target>,
|
|
471
|
-
options?: AbortSignal | DelegatedEventOptions
|
|
472
|
-
): Disposer;
|
|
473
|
-
|
|
474
|
-
once<Target extends Element = Element>(
|
|
475
|
-
selector: string,
|
|
476
|
-
handler: DelegatedEventHandler<EventType, Target>,
|
|
477
|
-
options?: AbortSignal | Omit<DelegatedEventOptions, 'once'>
|
|
478
|
-
): Disposer;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
export type EventDelegator = {
|
|
482
|
-
[K in keyof GlobalEventHandlersEventMap]: DelegatedEventBinder<GlobalEventHandlersEventMap[K]>;
|
|
483
|
-
} & {
|
|
484
|
-
[customEvent: string]: DelegatedEventBinder<Event>;
|
|
485
|
-
};
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
Usage:
|
|
489
|
-
|
|
490
|
-
```typescript
|
|
491
|
-
on.click<HTMLButtonElement>('button', (event, button) => {
|
|
492
|
-
event.clientX; // MouseEvent
|
|
493
|
-
button.disabled; // HTMLButtonElement
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
on.input<HTMLInputElement>('input', (event, input) => {
|
|
497
|
-
input.value;
|
|
498
|
-
});
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
## 14. `watch` API
|
|
502
|
-
|
|
503
|
-
```typescript
|
|
504
|
-
export interface WatchOptions {
|
|
505
|
-
signal?: AbortSignal;
|
|
506
|
-
once?: boolean;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
export type WatchTarget<T extends Element = Element> = string | T;
|
|
510
|
-
|
|
511
|
-
export type AttrWatchHandler<T extends Element = Element> = (
|
|
512
|
-
attrName: string,
|
|
513
|
-
newValue: string | null,
|
|
514
|
-
oldValue: string | null,
|
|
515
|
-
element: T
|
|
516
|
-
) => void;
|
|
517
|
-
|
|
518
|
-
export type KidsWatchHandler<T extends Element = Element> = (
|
|
519
|
-
change: {
|
|
520
|
-
added: Node[];
|
|
521
|
-
removed: Node[];
|
|
522
|
-
},
|
|
523
|
-
element: T
|
|
524
|
-
) => void;
|
|
525
|
-
|
|
526
|
-
export type TextWatchHandler<T extends Element = Element> = (
|
|
527
|
-
newText: string,
|
|
528
|
-
oldText: string | null,
|
|
529
|
-
element: T | null
|
|
530
|
-
) => void;
|
|
531
|
-
|
|
532
|
-
export type TreeWatchHandler<T extends Element = Element> = (
|
|
533
|
-
records: MutationRecord[],
|
|
534
|
-
element: T | null
|
|
535
|
-
) => void;
|
|
536
|
-
|
|
537
|
-
export interface WatchMethod<Args extends any[]> {
|
|
538
|
-
(...args: Args): Disposer;
|
|
539
|
-
once(...args: Args): Disposer;
|
|
540
|
-
}
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
Concrete API:
|
|
544
|
-
|
|
545
|
-
```typescript
|
|
546
|
-
export interface WatchApi {
|
|
547
|
-
attr: {
|
|
548
|
-
<T extends Element = Element>(
|
|
549
|
-
target: WatchTarget<T>,
|
|
550
|
-
attr: string | readonly string[] | '*',
|
|
551
|
-
handler: AttrWatchHandler<T>,
|
|
552
|
-
options?: AbortSignal | WatchOptions
|
|
553
|
-
): Disposer;
|
|
554
|
-
|
|
555
|
-
once<T extends Element = Element>(
|
|
556
|
-
target: WatchTarget<T>,
|
|
557
|
-
attr: string | readonly string[] | '*',
|
|
558
|
-
handler: AttrWatchHandler<T>,
|
|
559
|
-
options?: AbortSignal | Omit<WatchOptions, 'once'>
|
|
560
|
-
): Disposer;
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
kids: {
|
|
564
|
-
<T extends Element = Element>(
|
|
565
|
-
target: WatchTarget<T>,
|
|
566
|
-
handler: KidsWatchHandler<T>,
|
|
567
|
-
options?: AbortSignal | WatchOptions
|
|
568
|
-
): Disposer;
|
|
569
|
-
|
|
570
|
-
<T extends Element = Element>(
|
|
571
|
-
target: WatchTarget<T>,
|
|
572
|
-
config: { deep?: boolean },
|
|
573
|
-
handler: KidsWatchHandler<T>,
|
|
574
|
-
options?: AbortSignal | WatchOptions
|
|
575
|
-
): Disposer;
|
|
576
|
-
|
|
577
|
-
once<T extends Element = Element>(
|
|
578
|
-
target: WatchTarget<T>,
|
|
579
|
-
handler: KidsWatchHandler<T>,
|
|
580
|
-
options?: AbortSignal | Omit<WatchOptions, 'once'>
|
|
581
|
-
): Disposer;
|
|
582
|
-
|
|
583
|
-
once<T extends Element = Element>(
|
|
584
|
-
target: WatchTarget<T>,
|
|
585
|
-
config: { deep?: boolean },
|
|
586
|
-
handler: KidsWatchHandler<T>,
|
|
587
|
-
options?: AbortSignal | Omit<WatchOptions, 'once'>
|
|
588
|
-
): Disposer;
|
|
589
|
-
};
|
|
590
|
-
|
|
591
|
-
text: {
|
|
592
|
-
<T extends Element = Element>(
|
|
593
|
-
target: WatchTarget<T>,
|
|
594
|
-
handler: TextWatchHandler<T>,
|
|
595
|
-
options?: AbortSignal | WatchOptions
|
|
596
|
-
): Disposer;
|
|
597
|
-
|
|
598
|
-
once<T extends Element = Element>(
|
|
599
|
-
target: WatchTarget<T>,
|
|
600
|
-
handler: TextWatchHandler<T>,
|
|
601
|
-
options?: AbortSignal | Omit<WatchOptions, 'once'>
|
|
602
|
-
): Disposer;
|
|
603
|
-
};
|
|
604
|
-
|
|
605
|
-
tree: {
|
|
606
|
-
<T extends Element = Element>(
|
|
607
|
-
target: WatchTarget<T>,
|
|
608
|
-
handler: TreeWatchHandler<T>,
|
|
609
|
-
options?: AbortSignal | WatchOptions
|
|
610
|
-
): Disposer;
|
|
611
|
-
|
|
612
|
-
once<T extends Element = Element>(
|
|
613
|
-
target: WatchTarget<T>,
|
|
614
|
-
handler: TreeWatchHandler<T>,
|
|
615
|
-
options?: AbortSignal | Omit<WatchOptions, 'once'>
|
|
616
|
-
): Disposer;
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
## 15. Observer API
|
|
622
|
-
|
|
623
|
-
Runtime signatures from `observe.js`:
|
|
624
|
-
|
|
625
|
-
```typescript
|
|
626
|
-
export interface ObserveApi {
|
|
627
|
-
resize(
|
|
628
|
-
el: Element,
|
|
629
|
-
fn: (entries: ResizeObserverEntry[]) => void,
|
|
630
|
-
signal?: AbortSignal
|
|
631
|
-
): Disposer;
|
|
632
|
-
|
|
633
|
-
intersection(
|
|
634
|
-
el: Element,
|
|
635
|
-
fn: (entries: IntersectionObserverEntry[]) => void,
|
|
636
|
-
signal?: AbortSignal,
|
|
637
|
-
options?: IntersectionObserverInit
|
|
638
|
-
): Disposer;
|
|
639
|
-
|
|
640
|
-
mutation(
|
|
641
|
-
el: Node,
|
|
642
|
-
fn: (mutations: MutationRecord[]) => void,
|
|
643
|
-
signal?: AbortSignal,
|
|
644
|
-
options?: MutationObserverInit
|
|
645
|
-
): Disposer;
|
|
646
|
-
|
|
647
|
-
performance(
|
|
648
|
-
types: string[],
|
|
649
|
-
fn: (list: PerformanceObserverEntryList) => void,
|
|
650
|
-
signal?: AbortSignal,
|
|
651
|
-
options?: PerformanceObserverInit
|
|
652
|
-
): Disposer;
|
|
653
|
-
}
|
|
654
|
-
```
|
|
655
|
-
|
|
656
|
-
Important correction from old declarations:
|
|
657
|
-
|
|
658
|
-
- `resize` callback receives `ResizeObserverEntry[]`, not one entry.
|
|
659
|
-
- The exported method is `intersection`, not `intersect`.
|
|
660
|
-
- `mutation` accepts `signal` before `options`.
|
|
661
|
-
|
|
662
|
-
## 16. Scheduler API
|
|
663
|
-
|
|
664
|
-
Runtime signatures:
|
|
665
|
-
|
|
666
|
-
```typescript
|
|
667
|
-
export function schedule<T>(
|
|
668
|
-
fn: () => T | Promise<T>,
|
|
669
|
-
priority?: TaskPriority
|
|
670
|
-
): Promise<T>;
|
|
671
|
-
|
|
672
|
-
export function scheduleFrame<T>(fn: () => T): Promise<T>;
|
|
673
|
-
|
|
674
|
-
export function yieldTask(): Promise<void>;
|
|
675
|
-
```
|
|
676
|
-
|
|
677
|
-
`ui.yield` should be typed as `typeof yieldTask`.
|
|
678
|
-
|
|
679
|
-
## 17. Transition API
|
|
680
|
-
|
|
681
|
-
Runtime `transition` accepts a callback, not keyframes:
|
|
682
|
-
|
|
683
|
-
```typescript
|
|
684
|
-
export interface ViewTransitionLike<T = unknown> {
|
|
685
|
-
finished: Promise<T>;
|
|
686
|
-
updateCallbackDone: Promise<T>;
|
|
687
|
-
ready: Promise<unknown>;
|
|
688
|
-
skipTransition(): void;
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
export function transition<T>(
|
|
692
|
-
fn: () => T
|
|
693
|
-
): Promise<ViewTransitionLike<T>>;
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
Why not use the built-in `ViewTransition` type only:
|
|
697
|
-
|
|
698
|
-
- Some environments may not include the newest DOM lib.
|
|
699
|
-
- The runtime returns a compatible fallback object when unsupported or reduced-motion is active.
|
|
700
|
-
|
|
701
|
-
## 18. Template API
|
|
702
|
-
|
|
703
|
-
Runtime `template` returns a cloned `DocumentFragment`:
|
|
704
|
-
|
|
705
|
-
```typescript
|
|
706
|
-
export function template(
|
|
707
|
-
strings: TemplateStringsArray,
|
|
708
|
-
...values: unknown[]
|
|
709
|
-
): DocumentFragment;
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
Important correction from old declarations:
|
|
713
|
-
|
|
714
|
-
- It does not return `HTMLTemplateElement`.
|
|
715
|
-
- Interpolated values are currently ignored by runtime implementation, so `unknown[]` is safer than implying string interpolation.
|
|
716
|
-
|
|
717
|
-
## 19. `ui` Object Type
|
|
718
|
-
|
|
719
|
-
```typescript
|
|
720
|
-
export interface UiApi {
|
|
721
|
-
define: typeof define;
|
|
722
|
-
element: typeof element;
|
|
723
|
-
container: typeof container;
|
|
724
|
-
schedule: typeof schedule;
|
|
725
|
-
scheduleFrame: typeof scheduleFrame;
|
|
726
|
-
yield: typeof yieldTask;
|
|
727
|
-
transition: typeof transition;
|
|
728
|
-
template: typeof template;
|
|
729
|
-
observe: ObserveApi;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
export const ui: UiApi;
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
## 20. Generated Type Strategy
|
|
736
|
-
|
|
737
|
-
The Rust scanner should eventually generate `index.tags.d.ts` alongside `index.tags.json`.
|
|
738
|
-
|
|
739
|
-
Input:
|
|
740
|
-
|
|
741
|
-
```html
|
|
742
|
-
<button ref="button" id="button"></button>
|
|
743
|
-
<span ref="status"></span>
|
|
744
|
-
<input ref="email" type="email" />
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
Output:
|
|
748
|
-
|
|
749
|
-
```typescript
|
|
750
|
-
/* Auto-generated by anza. Do not edit. */
|
|
751
|
-
|
|
752
|
-
export interface TemplateRefs {
|
|
753
|
-
readonly button: HTMLButtonElement;
|
|
754
|
-
readonly email: HTMLInputElement;
|
|
755
|
-
readonly status: HTMLSpanElement;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
export type TemplateRefName = keyof TemplateRefs;
|
|
759
|
-
```
|
|
760
|
-
|
|
761
|
-
Type inference source:
|
|
762
|
-
|
|
763
|
-
- `button` -> `HTMLButtonElement`
|
|
764
|
-
- `input` -> `HTMLInputElement`
|
|
765
|
-
- `form` -> `HTMLFormElement`
|
|
766
|
-
- Unknown custom tags -> `HTMLElement`
|
|
767
|
-
|
|
768
|
-
## 21. JSDoc Consumer Pattern
|
|
769
|
-
|
|
770
|
-
Plain JavaScript component files can opt into strict IDE types:
|
|
771
|
-
|
|
772
|
-
```javascript
|
|
773
|
-
// @ts-check
|
|
774
|
-
import { ui } from '@adukiorg/anza/ui';
|
|
775
|
-
|
|
776
|
-
/** @type {const} */
|
|
777
|
-
const props = {
|
|
778
|
-
disabled: { type: Boolean, default: false },
|
|
779
|
-
count: { type: Number, default: 0 }
|
|
780
|
-
};
|
|
781
|
-
|
|
782
|
-
/** @typedef {import('./index.tags').TemplateRefs} TemplateRefs */
|
|
783
|
-
|
|
784
|
-
ui.element('ui-counter', {
|
|
785
|
-
props,
|
|
786
|
-
|
|
787
|
-
/** @param {import('@adukiorg/anza/ui').MountContext<typeof props, TemplateRefs>} ctx */
|
|
788
|
-
mount({ refs, on }) {
|
|
789
|
-
refs.button.disabled = false;
|
|
790
|
-
on.click('button', (_event, button) => {
|
|
791
|
-
button.disabled = true;
|
|
792
|
-
});
|
|
793
|
-
},
|
|
794
|
-
|
|
795
|
-
/** @param {import('@adukiorg/anza/ui').UpdateContext<typeof props, TemplateRefs>} ctx */
|
|
796
|
-
update(ctx) {
|
|
797
|
-
if (ctx.name === 'count') {
|
|
798
|
-
ctx.val.toFixed();
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
}, import.meta.url);
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
## 22. Type Test Cases
|
|
805
|
-
|
|
806
|
-
Use `tsd`, `vitest` with `expectTypeOf`, or a `tsc --noEmit` fixture.
|
|
807
|
-
|
|
808
|
-
Required pass cases:
|
|
809
|
-
|
|
810
|
-
```typescript
|
|
811
|
-
ui.element('ui-a', {
|
|
812
|
-
props: {
|
|
813
|
-
count: { type: Number, default: 0 },
|
|
814
|
-
open: { type: Boolean, default: false },
|
|
815
|
-
label: { type: String, default: '' }
|
|
816
|
-
},
|
|
817
|
-
mount({ el }) {
|
|
818
|
-
el.count.toFixed();
|
|
819
|
-
el.open.valueOf();
|
|
820
|
-
el.label.trim();
|
|
821
|
-
}
|
|
822
|
-
});
|
|
823
|
-
```
|
|
824
|
-
|
|
825
|
-
Required fail cases:
|
|
826
|
-
|
|
827
|
-
```typescript
|
|
828
|
-
ui.element('ui-bad', {
|
|
829
|
-
props: {
|
|
830
|
-
count: { type: Number, default: 'wrong' } // error
|
|
831
|
-
}
|
|
832
|
-
});
|
|
833
|
-
|
|
834
|
-
ui.element('ui-bad-update', {
|
|
835
|
-
props: {
|
|
836
|
-
count: { type: Number, default: 0 }
|
|
837
|
-
},
|
|
838
|
-
update(ctx) {
|
|
839
|
-
if (ctx.name === 'count') {
|
|
840
|
-
ctx.val.trim(); // error
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
});
|
|
844
|
-
```
|
|
845
|
-
|
|
846
|
-
## 23. Strict `tsconfig` Recommendation
|
|
847
|
-
|
|
848
|
-
For validating declarations:
|
|
849
|
-
|
|
850
|
-
```json
|
|
851
|
-
{
|
|
852
|
-
"compilerOptions": {
|
|
853
|
-
"target": "ES2022",
|
|
854
|
-
"module": "ESNext",
|
|
855
|
-
"moduleResolution": "Bundler",
|
|
856
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
857
|
-
"strict": true,
|
|
858
|
-
"noImplicitAny": true,
|
|
859
|
-
"noUncheckedIndexedAccess": true,
|
|
860
|
-
"exactOptionalPropertyTypes": true,
|
|
861
|
-
"checkJs": true,
|
|
862
|
-
"allowJs": true,
|
|
863
|
-
"noEmit": true,
|
|
864
|
-
"skipLibCheck": false
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
```
|
|
868
|
-
|
|
869
|
-
## 24. Implementation Order
|
|
870
|
-
|
|
871
|
-
1. Replace `types/core/ui/index.d.ts` with declarations matching this document.
|
|
872
|
-
2. Export all public utility types from that declaration file.
|
|
873
|
-
3. Add a `typesVersions` or package `types` entry if package consumers do not already resolve `types/index.d.ts`.
|
|
874
|
-
4. Add type tests for `ui.element`, props, refs, `on`, `watch`, observe, transition, and template.
|
|
875
|
-
5. Extend Rust scanner to emit `index.tags.d.ts`.
|
|
876
|
-
6. Update `usage.md` with a short "Typing Components" section linking this file.
|
|
877
|
-
|
|
878
|
-
## 25. Known Declaration Corrections Needed
|
|
879
|
-
|
|
880
|
-
The previous `types/core/ui/index.d.ts` was corrected:
|
|
881
|
-
|
|
882
|
-
- Remove stale `BaseElement.$`, `BaseElement.$$`, and `emit` unless the runtime implements them.
|
|
883
|
-
- Add `element` and `container`.
|
|
884
|
-
- Correct `transition` from animation keyframes to callback-based View Transition wrapper.
|
|
885
|
-
- Correct `template` return type to `DocumentFragment`.
|
|
886
|
-
- Correct `observe.resize` callback to entry arrays.
|
|
887
|
-
- Correct `observe.intersection` name.
|
|
888
|
-
- Add `observe.performance`.
|
|
889
|
-
- Add `TagsApi`, `EventDelegator`, `WatchApi`, lifecycle contexts, prop inference, and `ElementSpec`.
|
|
890
|
-
- Add `ui.observe.performance`, `ui.element`, and `ui.container`.
|