@geektech/tsone 0.0.1 → 0.1.0
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-zh.md +405 -0
- package/README.md +288 -32
- package/dist/core/animation/TransitionGroup.d.ts +7 -0
- package/dist/core/animation/index.d.ts +2 -0
- package/dist/core/animation/list-animation-controller.d.ts +16 -0
- package/dist/core/animation/transition-group-strategy.d.ts +15 -0
- package/dist/core/animation/types.d.ts +25 -0
- package/dist/core/app.d.ts +30 -8
- package/dist/core/component/base.d.ts +18 -1
- package/dist/core/component/index.d.ts +2 -2
- package/dist/core/document.d.ts +32 -0
- package/dist/core/form.d.ts +24 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/model.d.ts +18 -0
- package/dist/core/reactive/types.d.ts +5 -0
- package/dist/core/reactive.d.ts +5 -2
- package/dist/core/renderer/element-strategy.d.ts +25 -0
- package/dist/core/renderer/types.d.ts +6 -1
- package/dist/core/renderer.d.ts +9 -23
- package/dist/core/vnode.d.ts +56 -16
- package/dist/{index-dgv88dz4.js → index-8wjswsye.js} +6 -2
- package/dist/index-8wjswsye.js.map +10 -0
- package/dist/index-vpx80nq5.js +32 -0
- package/dist/index-vpx80nq5.js.map +10 -0
- package/dist/{index-3j2jsdpc.js → index-wv9gyjqt.js} +858 -244
- package/dist/index-wv9gyjqt.js.map +25 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +380 -20
- package/dist/index.js.map +8 -5
- package/dist/router/index.d.ts +1 -1
- package/dist/router/index.js +6 -4
- package/dist/router/index.js.map +1 -1
- package/dist/style/StyleManager.d.ts +1 -0
- package/dist/style/index.d.ts +1 -0
- package/dist/style/index.js +6 -2
- package/dist/style/index.js.map +1 -1
- package/dist/style/sheet.d.ts +13 -0
- package/package.json +4 -14
- package/dist/index-3j2jsdpc.js.map +0 -20
- package/dist/index-dgv88dz4.js.map +0 -10
package/README.md
CHANGED
|
@@ -1,28 +1,44 @@
|
|
|
1
1
|
# TSone
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [简体中文](./README-zh.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A lightweight frontend framework written entirely in TypeScript, with
|
|
6
|
+
reactivity, class-based components, strategy-driven rendering, and routing.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
- Bun 原生工具链:安装、测试、构建、示例服务和文档服务均由 Bun 驱动
|
|
9
|
-
- 响应式系统:`reactive`、`effect`、`computed`
|
|
10
|
-
- 面向对象组件模型:`Component<Props, State>`、生命周期、事件、插槽
|
|
11
|
-
- 策略模式渲染层:文本、元素、组件、插槽按 VNode 类型分发
|
|
12
|
-
- 内置路由:`createRouter`、`RouterView`、`RouterLink`
|
|
13
|
-
- 轻量级运行时,生产包无外部运行时依赖
|
|
8
|
+
## Features
|
|
14
9
|
|
|
15
|
-
|
|
10
|
+
- TypeScript-first implementation with typed public APIs
|
|
11
|
+
- Bun-native tooling for installation, testing, builds, playgrounds, and docs
|
|
12
|
+
- Documentation backed by a TypeScript typed-content registry
|
|
13
|
+
- Reactive primitives: `reactive`, `effect`, and `computed`
|
|
14
|
+
- Object-oriented components with `Component<Props, State>`, lifecycle hooks,
|
|
15
|
+
events, and slots
|
|
16
|
+
- Strategy-based rendering that dispatches text, elements, components, and
|
|
17
|
+
slots by VNode type
|
|
18
|
+
- Built-in routing with `createRouter`, `RouterView`, and `RouterLink`
|
|
19
|
+
- Lightweight runtime with no external production dependencies
|
|
20
|
+
|
|
21
|
+
## Repository Structure
|
|
22
|
+
|
|
23
|
+
This repository is a Bun workspace monorepo with two published packages:
|
|
24
|
+
`packages/tsone/` contains the browser framework `@geektech/tsone`, and
|
|
25
|
+
`packages/tsone-cli/` contains the Bun-native development tooling
|
|
26
|
+
`@geektech/tsone-cli`. Standalone example projects live in the root
|
|
27
|
+
`playground/` directory.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
16
30
|
|
|
17
31
|
```bash
|
|
18
|
-
bun add @geektech/tsone
|
|
32
|
+
bun add @geektech/tsone @geektech/tsone-cli
|
|
19
33
|
```
|
|
20
34
|
|
|
21
35
|
```bash
|
|
22
|
-
pnpm add @geektech/tsone
|
|
36
|
+
pnpm add @geektech/tsone @geektech/tsone-cli
|
|
23
37
|
```
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
The framework and CLI require Bun `>=1.3.0` for the documented workflow.
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
26
42
|
|
|
27
43
|
```typescript
|
|
28
44
|
import {
|
|
@@ -42,7 +58,7 @@ class App extends Component<Record<string, never>, AppState> {
|
|
|
42
58
|
protected initState(): AppState {
|
|
43
59
|
return {
|
|
44
60
|
count: 0,
|
|
45
|
-
version: '0.0.
|
|
61
|
+
version: '0.0.2',
|
|
46
62
|
};
|
|
47
63
|
}
|
|
48
64
|
|
|
@@ -72,13 +88,73 @@ class App extends Component<Record<string, never>, AppState> {
|
|
|
72
88
|
const state = reactive({ ready: true });
|
|
73
89
|
const status = computed(() => (state.ready ? 'ready' : 'pending'));
|
|
74
90
|
|
|
75
|
-
const app = createApp({ root: App,
|
|
91
|
+
const app = createApp({ root: App, state });
|
|
76
92
|
app.mount();
|
|
77
93
|
|
|
78
94
|
console.log(status.value);
|
|
79
95
|
```
|
|
80
96
|
|
|
81
|
-
|
|
97
|
+
`createApp` uses `#app` as its default mount target, so an application can call
|
|
98
|
+
`app.mount()` immediately after creation. If the target is not currently in the
|
|
99
|
+
document, `mount()` safely skips that attempt. Pass `rootElement` only when you
|
|
100
|
+
need to override the default target.
|
|
101
|
+
|
|
102
|
+
## Development Tooling
|
|
103
|
+
|
|
104
|
+
The separate `@geektech/tsone-cli` package provides `tsone dev` and
|
|
105
|
+
`tsone build`. Its default entry is `src/main.ts`; that module must expose the
|
|
106
|
+
application as `export const app`, and the value must provide
|
|
107
|
+
`renderHtmlDocument()`.
|
|
108
|
+
|
|
109
|
+
Create an optional `tsone.config.ts` at the project root:
|
|
110
|
+
|
|
111
|
+
The config file supports only a plain-object default export; functional or
|
|
112
|
+
function-valued config is not supported.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { defineConfig } from '@geektech/tsone-cli';
|
|
116
|
+
|
|
117
|
+
export default defineConfig({
|
|
118
|
+
server: {
|
|
119
|
+
proxy: {
|
|
120
|
+
'/api': {
|
|
121
|
+
target: 'http://localhost:3000',
|
|
122
|
+
changeOrigin: true,
|
|
123
|
+
rewrite: (path) => path.replace(/^\/api/, ''),
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
build: { outDir: 'dist' },
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The string proxy shorthand is also supported, for example
|
|
132
|
+
`{ '/backend': 'http://localhost:4000' }`. Defaults are `src/main.ts`,
|
|
133
|
+
`127.0.0.1`, port `52211`, an empty `server.proxy`, and `dist`.
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
tsone dev [--host <host>] [--port <port>]
|
|
137
|
+
tsone build [--out-dir <path>]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`dev` accepts host/port overrides and `build` accepts the output-directory
|
|
141
|
+
override; both `--port 3000` and `--port=3000` forms are valid. Build output
|
|
142
|
+
must remain a safe child directory inside the project root.
|
|
143
|
+
|
|
144
|
+
Programmatic tooling is imported from `@geektech/tsone-cli`, not from the
|
|
145
|
+
framework root. It exports `defineConfig`, `resolveConfig`, `startDevServer`,
|
|
146
|
+
and `build`. The dev server is owned by the caller, which must call
|
|
147
|
+
`server.stop()`; `build()` returns absolute `root` and `outDir` values plus
|
|
148
|
+
`assetsBuilt`.
|
|
149
|
+
|
|
150
|
+
The development server serves HTTP only. Proxy targets may use HTTP or HTTPS.
|
|
151
|
+
Rules use literal prefix matching with the longest match first, retain
|
|
152
|
+
query/body/end-to-end headers, optionally apply `changeOrigin` and `rewrite`,
|
|
153
|
+
and return `502 Bad Gateway` when the upstream is unreachable. CLI v1 has no
|
|
154
|
+
config plugins, WebSocket, HMR, SSR, functional config, `public/` copying, or
|
|
155
|
+
public minify/sourcemap settings.
|
|
156
|
+
|
|
157
|
+
## Routing
|
|
82
158
|
|
|
83
159
|
```typescript
|
|
84
160
|
import { Component, VNode, createApp } from '@geektech/tsone';
|
|
@@ -97,11 +173,11 @@ class Layout extends Component {
|
|
|
97
173
|
children: [
|
|
98
174
|
{
|
|
99
175
|
component: RouterLink,
|
|
100
|
-
props: { to: '/', children: ['
|
|
176
|
+
props: { to: '/', children: ['Home'] },
|
|
101
177
|
},
|
|
102
178
|
{
|
|
103
179
|
component: RouterLink,
|
|
104
|
-
props: { to: '/users/42', children: ['
|
|
180
|
+
props: { to: '/users/42', children: ['User'] },
|
|
105
181
|
},
|
|
106
182
|
{ component: RouterView },
|
|
107
183
|
],
|
|
@@ -113,14 +189,18 @@ const router = createRouter({
|
|
|
113
189
|
mode: 'history',
|
|
114
190
|
routes: [
|
|
115
191
|
{ path: '/', component: HomePage },
|
|
116
|
-
{
|
|
192
|
+
{
|
|
193
|
+
path: '/users/:id',
|
|
194
|
+
component: UserPage,
|
|
195
|
+
meta: { title: 'User Details' },
|
|
196
|
+
},
|
|
117
197
|
],
|
|
118
198
|
});
|
|
119
199
|
|
|
120
|
-
createApp({ root: Layout
|
|
200
|
+
createApp({ root: Layout }).use(router).mount();
|
|
121
201
|
```
|
|
122
202
|
|
|
123
|
-
##
|
|
203
|
+
## Development Commands
|
|
124
204
|
|
|
125
205
|
```bash
|
|
126
206
|
bun install
|
|
@@ -128,23 +208,197 @@ bun test
|
|
|
128
208
|
bun run build
|
|
129
209
|
bun run dev
|
|
130
210
|
bun run docs
|
|
211
|
+
bun run docs:build
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The root `playground/` directory contains two standalone projects:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
bun run dev
|
|
218
|
+
bun run dev:site
|
|
219
|
+
bun run dev:admin
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- `playground/official-site`: product website example
|
|
223
|
+
- `playground/admin-dashboard`: admin dashboard example
|
|
224
|
+
|
|
225
|
+
Documentation content is maintained in the typed-content registry. Chinese
|
|
226
|
+
content lives in `packages/tsone/docs/app/content/zh/`, while English content
|
|
227
|
+
lives in `packages/tsone/docs/app/content/en/`. Every logical route must exist
|
|
228
|
+
in both directories.
|
|
229
|
+
|
|
230
|
+
Chinese and English catalogs each contain exactly 14 logical routes. Add or
|
|
231
|
+
remove a route in both catalogs in the same change.
|
|
232
|
+
|
|
233
|
+
Content links stay locale-neutral: never write `/en/` manually. Chinese public
|
|
234
|
+
routes are unprefixed, while English routes use `/en/`. Browser-language
|
|
235
|
+
detection runs only at `/`; manual selection takes precedence and persists for
|
|
236
|
+
later visits.
|
|
237
|
+
|
|
238
|
+
Build the static documentation site with:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
bun run docs:build
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The build fails strictly for missing, extra, duplicate, empty, or
|
|
245
|
+
mixed-language pages.
|
|
246
|
+
|
|
247
|
+
The base HTML document shell can also be generated from `createApp`. It emits a
|
|
248
|
+
`#app` mount node by default; pass `rootElement` only to use a different target.
|
|
249
|
+
When a custom `body` is needed, pass a component or VNode rather than an HTML
|
|
250
|
+
string. This API renders the mount node through TSone's renderer. In a Bun or
|
|
251
|
+
Node static-generation environment, provide a DOM-like document first:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import { createApp, type StyleSheet } from '@geektech/tsone';
|
|
255
|
+
|
|
256
|
+
const styles: StyleSheet = [
|
|
257
|
+
{
|
|
258
|
+
selector: '.app',
|
|
259
|
+
properties: { maxWidth: '72rem' },
|
|
260
|
+
},
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
const app = createApp({
|
|
264
|
+
root: App,
|
|
265
|
+
document: {
|
|
266
|
+
lang: 'en',
|
|
267
|
+
title: 'TSone App',
|
|
268
|
+
styles,
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const html = app.renderHtmlDocument({
|
|
273
|
+
scripts: [{ type: 'module', src: '/assets/app.js' }],
|
|
274
|
+
});
|
|
131
275
|
```
|
|
132
276
|
|
|
133
|
-
##
|
|
277
|
+
## Public API
|
|
134
278
|
|
|
135
|
-
|
|
279
|
+
The main `@geektech/tsone` entry point exports:
|
|
136
280
|
|
|
137
281
|
- `createApp(options)`
|
|
282
|
+
- `createApp({ root, rootProps })`
|
|
138
283
|
- `Component<Props, State>`
|
|
284
|
+
- `TransitionGroup` / `TransitionGroupProps` / `TransitionAnimationType`
|
|
139
285
|
- `VNode`
|
|
140
|
-
- `h()` / `createComponent()` / `slot()`
|
|
286
|
+
- `h()` / `createComponent()` / `slot()` / `each()`
|
|
287
|
+
- `Tag(tag, options)` for arbitrary HTML elements
|
|
288
|
+
- `Div()` / `Span()` / `P()` / `Button()` / `Input()`
|
|
289
|
+
- `Section()` / `Main()` / `Header()` / `Footer()` / `Nav()` / `Article()` /
|
|
290
|
+
`Aside()`
|
|
291
|
+
- `H1()` through `H6()` / `Strong()` / `Em()` / `Small()` / `Pre()` /
|
|
292
|
+
`Code()` / `Blockquote()`
|
|
293
|
+
- `Ul()` / `Ol()` / `Li()` / `A()` / `Img()`
|
|
294
|
+
- `Form()` / `Label()` / `Textarea()` / `Select()` / `Option()`
|
|
295
|
+
- `Table()` / `Thead()` / `Tbody()` / `Tr()` / `Th()` / `Td()`
|
|
296
|
+
- `Directions` / `ModelBinding`
|
|
297
|
+
- `InjectionKey` and component/application `provide()` / `inject()`
|
|
298
|
+
- `createForm()` / `required()` / `minLength()` / `validate()`
|
|
299
|
+
- `createApp(options).renderHtmlDocument(options)`
|
|
300
|
+
- `renderHtmlDocument(options)`
|
|
301
|
+
- `StyleSheet` / `renderStyleSheet(styles)`
|
|
141
302
|
- `reactive()` / `readonly()`
|
|
142
303
|
- `effect()` / `stop()`
|
|
143
304
|
- `computed()`
|
|
144
305
|
- `ref()` / `isRef()` / `unref()`
|
|
145
|
-
- `version
|
|
306
|
+
- `version`, currently `0.0.2`
|
|
307
|
+
|
|
308
|
+
## Rendering, Communication, and Forms
|
|
309
|
+
|
|
310
|
+
`directions.if` controls whether an element, component, or slot is mounted. The
|
|
311
|
+
node is unmounted when the condition is false:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
{
|
|
315
|
+
component: ProfilePanel,
|
|
316
|
+
directions: { if: this.state.visible },
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`each()` creates stable keys for list items so the renderer can reuse nodes
|
|
321
|
+
during reordering, insertion, and removal:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
const items = each(
|
|
325
|
+
this.state.users,
|
|
326
|
+
(user) => ({ tag: 'li', children: [user.name] }),
|
|
327
|
+
(user) => user.id
|
|
328
|
+
);
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
`TransitionGroup` animates the enter and exit of direct keyed children:
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
{
|
|
335
|
+
component: TransitionGroup,
|
|
336
|
+
props: { tag: 'ul', type: 'fade', duration: 300 },
|
|
337
|
+
children: each(
|
|
338
|
+
this.state.items,
|
|
339
|
+
(item) => Li({ children: [item.label] }),
|
|
340
|
+
(item) => item.id
|
|
341
|
+
),
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The animation types are `fade`, `slide-up`, `slide-down`, `slide-left`,
|
|
346
|
+
`slide-right`, and `scale`. The defaults are a `div` wrapper, `fade`, and
|
|
347
|
+
`300` ms with `ease` easing. Every direct child must have a unique key. Initial
|
|
348
|
+
children animate in, and removed children stay mounted until their exit ends.
|
|
349
|
+
TSone automatically disables these animations for
|
|
350
|
+
`prefers-reduced-motion: reduce` or when Web Animations is unavailable.
|
|
351
|
+
Reordering reuses and moves existing nodes without a reorder or FLIP animation.
|
|
352
|
+
|
|
353
|
+
Component events return an unsubscribe function, while component VNodes can
|
|
354
|
+
declare parent listeners through `emitters`:
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
const stopListening = child.on('saved', (payload) => console.log(payload));
|
|
358
|
+
stopListening();
|
|
359
|
+
// { component: Editor, emitters: { saved: (payload) => this.save(payload) } }
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Dependency injection resolves values from the current component, then its
|
|
363
|
+
parents, and finally the application instance:
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
const THEME: InjectionKey<{ mode: string }> = Symbol('theme');
|
|
367
|
+
app.provide(THEME, { mode: 'dark' });
|
|
368
|
+
const theme = this.inject(THEME, { mode: 'light' });
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
`directions.model` supports dot-separated paths and conversion functions.
|
|
372
|
+
Native input, textarea, checkbox, radio, and select controls stay synchronized:
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
Input({
|
|
376
|
+
props: { type: 'number' },
|
|
377
|
+
directions: {
|
|
378
|
+
model: {
|
|
379
|
+
path: 'profile.age',
|
|
380
|
+
parse: (value) => Number(value),
|
|
381
|
+
format: (value) => String(value ?? ''),
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Validation uses pure functions and does not own error UI or submission:
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
const form = createForm(this.state, {
|
|
391
|
+
'profile.name': [required('Name is required'), minLength(2)],
|
|
392
|
+
'profile.age': [
|
|
393
|
+
validate((value) => Number(value) >= 18 || 'Age must be at least 18'),
|
|
394
|
+
],
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const result = form.validate();
|
|
398
|
+
form.resetErrors();
|
|
399
|
+
```
|
|
146
400
|
|
|
147
|
-
|
|
401
|
+
The `@geektech/tsone/router` entry point exports:
|
|
148
402
|
|
|
149
403
|
- `createRouter({ routes, mode, base })`
|
|
150
404
|
- `Router`
|
|
@@ -154,23 +408,25 @@ bun run docs
|
|
|
154
408
|
- `RouteRecord`
|
|
155
409
|
- `RouteLocation`
|
|
156
410
|
|
|
157
|
-
|
|
411
|
+
The `@geektech/tsone/style` entry point exports:
|
|
158
412
|
|
|
159
413
|
- `StyleManager`
|
|
414
|
+
- `StyleSheet` / `renderStyleSheet(styles)`
|
|
160
415
|
|
|
161
|
-
##
|
|
416
|
+
## Pre-Publish Checklist
|
|
162
417
|
|
|
163
418
|
```bash
|
|
164
419
|
bun test
|
|
165
420
|
bunx tsc --noEmit
|
|
166
421
|
bun run build
|
|
167
|
-
bun pm pack --dry-run
|
|
422
|
+
bun pm pack --cwd packages/tsone --dry-run
|
|
168
423
|
```
|
|
169
424
|
|
|
170
|
-
##
|
|
425
|
+
## Contributing
|
|
171
426
|
|
|
172
|
-
|
|
427
|
+
Issues and pull requests are welcome. Before publishing an open-source release,
|
|
428
|
+
make sure the tests, type checks, and build all pass.
|
|
173
429
|
|
|
174
|
-
##
|
|
430
|
+
## License
|
|
175
431
|
|
|
176
432
|
[MIT](LICENSE)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Component } from '../component';
|
|
2
|
+
import { type TransitionGroupNode, type TransitionGroupProps } from './types';
|
|
3
|
+
export declare class TransitionGroup extends Component<TransitionGroupProps> {
|
|
4
|
+
protected initState(): object;
|
|
5
|
+
protected initStyles(): void;
|
|
6
|
+
protected render(): TransitionGroupNode;
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TransitionGroupOptions } from './types';
|
|
2
|
+
export interface ListAnimationRun {
|
|
3
|
+
animation: Animation;
|
|
4
|
+
token: symbol;
|
|
5
|
+
keyframes: Keyframe[];
|
|
6
|
+
options: KeyframeAnimationOptions;
|
|
7
|
+
finished: Promise<'finished' | 'cancelled'>;
|
|
8
|
+
}
|
|
9
|
+
export declare class ListAnimationController {
|
|
10
|
+
private readonly runs;
|
|
11
|
+
playEnter(element: HTMLElement, options: TransitionGroupOptions): ListAnimationRun | null;
|
|
12
|
+
playExit(element: HTMLElement, options: TransitionGroupOptions): ListAnimationRun | null;
|
|
13
|
+
cancel(element: HTMLElement): void;
|
|
14
|
+
private play;
|
|
15
|
+
private canAnimate;
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ElementRenderStrategy } from '../renderer/element-strategy';
|
|
2
|
+
import type { RenderRuntimeContext, Renderable } from '../renderer/types';
|
|
3
|
+
import { type TransitionGroupNode } from './types';
|
|
4
|
+
export declare class TransitionGroupRenderStrategy extends ElementRenderStrategy<TransitionGroupNode> {
|
|
5
|
+
private readonly entries;
|
|
6
|
+
private readonly animations;
|
|
7
|
+
matches(vnode: Renderable): vnode is TransitionGroupNode;
|
|
8
|
+
protected mountChildren(element: HTMLElement, groupVNode: TransitionGroupNode, context: RenderRuntimeContext): void;
|
|
9
|
+
protected updateChildren(element: HTMLElement, _oldVNode: TransitionGroupNode, newVNode: TransitionGroupNode, context: RenderRuntimeContext): void;
|
|
10
|
+
protected unmountChildren(element: HTMLElement, vnode: TransitionGroupNode, context: RenderRuntimeContext): void;
|
|
11
|
+
private playEnter;
|
|
12
|
+
private startExit;
|
|
13
|
+
private finishExit;
|
|
14
|
+
private placeActiveEntries;
|
|
15
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type EventListeners, type HTMLNode, type HTMLProps, type VNode } from '../vnode';
|
|
2
|
+
export declare const TRANSITION_ANIMATION_TYPES: readonly ["fade", "slide-up", "slide-down", "slide-left", "slide-right", "scale"];
|
|
3
|
+
export type TransitionAnimationType = (typeof TRANSITION_ANIMATION_TYPES)[number];
|
|
4
|
+
export interface TransitionGroupOptions {
|
|
5
|
+
type: TransitionAnimationType;
|
|
6
|
+
duration: number;
|
|
7
|
+
}
|
|
8
|
+
export interface TransitionGroupProps {
|
|
9
|
+
tag?: string;
|
|
10
|
+
type?: TransitionAnimationType;
|
|
11
|
+
duration?: number;
|
|
12
|
+
elementProps?: HTMLProps;
|
|
13
|
+
listeners?: EventListeners;
|
|
14
|
+
children?: VNode[];
|
|
15
|
+
}
|
|
16
|
+
export interface TransitionGroupNode extends HTMLNode {
|
|
17
|
+
transitionGroup: TransitionGroupOptions;
|
|
18
|
+
children?: VNode[];
|
|
19
|
+
}
|
|
20
|
+
export type KeyedTransitionChild = VNode & {
|
|
21
|
+
key: string | number;
|
|
22
|
+
};
|
|
23
|
+
export declare function normalizeTransitionGroupProps(props: TransitionGroupProps): Required<Pick<TransitionGroupProps, 'tag' | 'type' | 'duration'>>;
|
|
24
|
+
export declare function validateTransitionGroupChildren(children: Array<VNode | string>): KeyedTransitionChild[];
|
|
25
|
+
export declare function isTransitionGroupNode(vnode: unknown): vnode is TransitionGroupNode;
|
package/dist/core/app.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ComponentConstructor } from './component';
|
|
1
|
+
import { ComponentConstructor, ComponentProps, InjectionKey, InjectionResult } from './component';
|
|
2
|
+
import { type HtmlDocumentBody, type HtmlDocumentOptions } from './document';
|
|
2
3
|
import type { Router } from '../router';
|
|
3
4
|
export interface Plugin {
|
|
4
5
|
install: (app: OneApp, ...args: unknown[]) => void;
|
|
@@ -6,15 +7,23 @@ export interface Plugin {
|
|
|
6
7
|
onUpdated?: (app: OneApp) => void;
|
|
7
8
|
onBeforeUnmount?: (app: OneApp) => void;
|
|
8
9
|
}
|
|
9
|
-
export
|
|
10
|
+
export type AppDocumentOptions = Partial<Omit<HtmlDocumentOptions, 'body'>> & {
|
|
11
|
+
body?: HtmlDocumentBody;
|
|
12
|
+
};
|
|
13
|
+
export type AppDocumentRenderOptions = AppDocumentOptions;
|
|
14
|
+
export interface AppOptions<TState = Record<string, unknown>, TConfig = Record<string, unknown>, TRootProps extends ComponentProps = ComponentProps> {
|
|
10
15
|
/** 根组件构造函数 */
|
|
11
|
-
root?: ComponentConstructor
|
|
12
|
-
/**
|
|
16
|
+
root?: ComponentConstructor<TRootProps>;
|
|
17
|
+
/** 根组件 props */
|
|
18
|
+
rootProps?: TRootProps;
|
|
19
|
+
/** 应用挂载点,默认 #app */
|
|
13
20
|
rootElement?: string | Element;
|
|
14
21
|
/** 全局状态 */
|
|
15
22
|
state?: TState;
|
|
16
23
|
/** 全局配置 */
|
|
17
24
|
config?: TConfig;
|
|
25
|
+
/** HTML 文档壳配置,用于 dev/build 生成入口页面 */
|
|
26
|
+
document?: AppDocumentOptions;
|
|
18
27
|
}
|
|
19
28
|
export interface AppContext<TConfig = Record<string, unknown>> {
|
|
20
29
|
app: OneApp;
|
|
@@ -22,17 +31,18 @@ export interface AppContext<TConfig = Record<string, unknown>> {
|
|
|
22
31
|
config: TConfig;
|
|
23
32
|
router?: Router;
|
|
24
33
|
}
|
|
25
|
-
export declare class OneApp<TState extends object = Record<string, unknown>, TConfig extends object = Record<string, unknown
|
|
34
|
+
export declare class OneApp<TState extends object = Record<string, unknown>, TConfig extends object = Record<string, unknown>, TRootProps extends ComponentProps = ComponentProps> {
|
|
26
35
|
private options;
|
|
27
36
|
private container;
|
|
28
37
|
private rootInstance;
|
|
29
38
|
private mounted;
|
|
30
39
|
private templateEngine;
|
|
31
40
|
private readonly appContext;
|
|
41
|
+
private readonly providers;
|
|
32
42
|
private plugins;
|
|
33
43
|
private unmountedCallback?;
|
|
34
44
|
router?: Router;
|
|
35
|
-
constructor(options?: AppOptions<TState, TConfig>);
|
|
45
|
+
constructor(options?: AppOptions<TState, TConfig, TRootProps>);
|
|
36
46
|
private handleError;
|
|
37
47
|
/**
|
|
38
48
|
* 渲染错误UI
|
|
@@ -57,7 +67,7 @@ export declare class OneApp<TState extends object = Record<string, unknown>, TCo
|
|
|
57
67
|
/**
|
|
58
68
|
* 更新根组件
|
|
59
69
|
*/
|
|
60
|
-
updateRootComponent(component: ComponentConstructor): void;
|
|
70
|
+
updateRootComponent(component: ComponentConstructor<TRootProps>): void;
|
|
61
71
|
/**
|
|
62
72
|
* 更新应用状态
|
|
63
73
|
*/
|
|
@@ -66,6 +76,10 @@ export declare class OneApp<TState extends object = Record<string, unknown>, TCo
|
|
|
66
76
|
* 获取应用上下文
|
|
67
77
|
*/
|
|
68
78
|
getContext(): AppContext<TConfig>;
|
|
79
|
+
provide<T>(key: InjectionKey<T>, value: T): this;
|
|
80
|
+
inject<T>(key: InjectionKey<T>): T | undefined;
|
|
81
|
+
inject<T>(key: InjectionKey<T>, fallback: T): T;
|
|
82
|
+
resolveInjection<T>(key: InjectionKey<T>): InjectionResult<T>;
|
|
69
83
|
/**
|
|
70
84
|
* 获取应用状态
|
|
71
85
|
*/
|
|
@@ -78,10 +92,18 @@ export declare class OneApp<TState extends object = Record<string, unknown>, TCo
|
|
|
78
92
|
* 监听应用卸载
|
|
79
93
|
*/
|
|
80
94
|
onUnmounted(callback: () => void): this;
|
|
95
|
+
/**
|
|
96
|
+
* 生成应用入口 HTML 文档
|
|
97
|
+
*/
|
|
98
|
+
renderHtmlDocument(options?: AppDocumentRenderOptions): string;
|
|
81
99
|
/**
|
|
82
100
|
* 解析根元素
|
|
83
101
|
*/
|
|
84
102
|
private resolveRootElement;
|
|
103
|
+
private resolveMountContainer;
|
|
104
|
+
private createMountDocumentBody;
|
|
105
|
+
private createMountElementFromSelector;
|
|
106
|
+
private mergeDocumentScripts;
|
|
85
107
|
private onMounted;
|
|
86
108
|
private onUpdated;
|
|
87
109
|
private onBeforeUnmount;
|
|
@@ -89,4 +111,4 @@ export declare class OneApp<TState extends object = Record<string, unknown>, TCo
|
|
|
89
111
|
/**
|
|
90
112
|
* 创建应用实例
|
|
91
113
|
*/
|
|
92
|
-
export declare function createApp<TState extends object = Record<string, unknown>, TConfig extends object = Record<string, unknown
|
|
114
|
+
export declare function createApp<TState extends object = Record<string, unknown>, TConfig extends object = Record<string, unknown>, TRootProps extends ComponentProps = ComponentProps>(options?: AppOptions<TState, TConfig, TRootProps>): OneApp<TState, TConfig, TRootProps>;
|
|
@@ -4,6 +4,13 @@ import type { VNode } from '../vnode';
|
|
|
4
4
|
export type ComponentProps = object;
|
|
5
5
|
export type ComponentState = object;
|
|
6
6
|
export type ComponentEventListener = (...args: unknown[]) => void;
|
|
7
|
+
export type InjectionKey<T> = (string | symbol) & {
|
|
8
|
+
readonly __injectionType?: T;
|
|
9
|
+
};
|
|
10
|
+
export interface InjectionResult<T> {
|
|
11
|
+
found: boolean;
|
|
12
|
+
value: T | undefined;
|
|
13
|
+
}
|
|
7
14
|
export type ComponentConstructor<TProps extends ComponentProps = ComponentProps, TState extends ComponentState = ComponentState> = new (props?: TProps) => Component<TProps, TState>;
|
|
8
15
|
export type AnyComponentConstructor = new (props?: never) => Component<ComponentProps, ComponentState>;
|
|
9
16
|
export declare abstract class Component<TProps extends ComponentProps = ComponentProps, TState extends ComponentState = ComponentState> implements ComponentInstance {
|
|
@@ -14,8 +21,11 @@ export declare abstract class Component<TProps extends ComponentProps = Componen
|
|
|
14
21
|
private readonly templateEngine;
|
|
15
22
|
private readonly childComponents;
|
|
16
23
|
private readonly eventListeners;
|
|
24
|
+
private readonly providers;
|
|
17
25
|
private readonly updateEffect;
|
|
18
26
|
private appContext;
|
|
27
|
+
private parentComponent;
|
|
28
|
+
private elementChangeListener;
|
|
19
29
|
protected styleManager: StyleManager;
|
|
20
30
|
state: TState;
|
|
21
31
|
mounted: boolean;
|
|
@@ -30,6 +40,12 @@ export declare abstract class Component<TProps extends ComponentProps = Componen
|
|
|
30
40
|
setProps(props: Partial<TProps>): void;
|
|
31
41
|
setState(state: Partial<TState>): void;
|
|
32
42
|
setAppContext(context: unknown): void;
|
|
43
|
+
setParentComponent(parent: ComponentInstance | null): void;
|
|
44
|
+
setElementChangeListener(listener: (previousElement: Node, nextElement: Node) => void): void;
|
|
45
|
+
provide<T>(key: InjectionKey<T>, value: T): void;
|
|
46
|
+
inject<T>(key: InjectionKey<T>): T | undefined;
|
|
47
|
+
inject<T>(key: InjectionKey<T>, fallback: T): T;
|
|
48
|
+
resolveInjection<T>(key: InjectionKey<T>): InjectionResult<T>;
|
|
33
49
|
getElement(): Node | null;
|
|
34
50
|
protected beforeMount(): void;
|
|
35
51
|
protected onMounted(): void;
|
|
@@ -40,7 +56,7 @@ export declare abstract class Component<TProps extends ComponentProps = Componen
|
|
|
40
56
|
protected getContext(): unknown;
|
|
41
57
|
protected get router(): unknown;
|
|
42
58
|
protected emit(eventName: string, ...args: unknown[]): void;
|
|
43
|
-
on(eventName: string, listener: ComponentEventListener): void;
|
|
59
|
+
on(eventName: string, listener: ComponentEventListener): () => void;
|
|
44
60
|
off(eventName: string, listener: ComponentEventListener): void;
|
|
45
61
|
private createRenderContext;
|
|
46
62
|
private collectSlots;
|
|
@@ -49,5 +65,6 @@ export declare abstract class Component<TProps extends ComponentProps = Componen
|
|
|
49
65
|
private trackStateProperties;
|
|
50
66
|
private getRouterFrom;
|
|
51
67
|
private getRouterFromGlobalApp;
|
|
68
|
+
private resolveAppInjection;
|
|
52
69
|
private trackReactiveValue;
|
|
53
70
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Component } from './base';
|
|
2
|
-
import type { AnyComponentConstructor, ComponentConstructor, ComponentEventListener, ComponentProps, ComponentState } from './base';
|
|
2
|
+
import type { AnyComponentConstructor, ComponentConstructor, ComponentEventListener, InjectionKey, InjectionResult, ComponentProps, ComponentState } from './base';
|
|
3
3
|
export { Component };
|
|
4
|
-
export type { AnyComponentConstructor, ComponentConstructor, ComponentEventListener, ComponentProps, ComponentState, };
|
|
4
|
+
export type { AnyComponentConstructor, ComponentConstructor, ComponentEventListener, InjectionKey, InjectionResult, ComponentProps, ComponentState, };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Renderable } from './renderer/types';
|
|
2
|
+
import { type StyleSheet } from '../style/sheet';
|
|
3
|
+
export { renderStyleSheet, type StyleAtRule, type StyleProperties, type StyleRule, type StyleSheet, type StyleSheetEntry, type StyleValue, } from '../style/sheet';
|
|
4
|
+
export type HtmlAttributeValue = string | number | boolean | null | undefined;
|
|
5
|
+
export type HtmlAttributes = Record<string, HtmlAttributeValue>;
|
|
6
|
+
export type HtmlDocumentBody = Renderable | Renderable[];
|
|
7
|
+
export interface HtmlHeadElement {
|
|
8
|
+
tag: string;
|
|
9
|
+
attributes?: HtmlAttributes;
|
|
10
|
+
text?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface HtmlScript {
|
|
13
|
+
src: string;
|
|
14
|
+
type?: string;
|
|
15
|
+
async?: boolean;
|
|
16
|
+
defer?: boolean;
|
|
17
|
+
attributes?: HtmlAttributes;
|
|
18
|
+
}
|
|
19
|
+
export interface HtmlDocumentOptions {
|
|
20
|
+
title: string;
|
|
21
|
+
body: HtmlDocumentBody;
|
|
22
|
+
lang?: string;
|
|
23
|
+
charset?: string;
|
|
24
|
+
viewport?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
htmlAttributes?: HtmlAttributes;
|
|
27
|
+
bodyAttributes?: HtmlAttributes;
|
|
28
|
+
head?: HtmlHeadElement[];
|
|
29
|
+
styles?: StyleSheet;
|
|
30
|
+
scripts?: HtmlScript[];
|
|
31
|
+
}
|
|
32
|
+
export declare function renderHtmlDocument(options: HtmlDocumentOptions): string;
|