@ydant/base 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/LICENSE +21 -0
- package/README.md +214 -0
- package/dist/elements/factory.d.ts +16 -0
- package/dist/elements/html.d.ts +32 -0
- package/dist/elements/svg.d.ts +20 -0
- package/dist/global.d.ts +85 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.es.js +256 -0
- package/dist/index.umd.js +1 -0
- package/dist/plugin/element.d.ts +6 -0
- package/dist/plugin/index.d.ts +8 -0
- package/dist/plugin/primitives.d.ts +10 -0
- package/dist/primitives.d.ts +71 -0
- package/dist/slot-ref.d.ts +39 -0
- package/dist/types.d.ts +60 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 cwd-k2
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# @ydant/base
|
|
2
|
+
|
|
3
|
+
Element factories, primitives, and base plugin for Ydant.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
**@ydant/base is the user-facing API that knows "how" to render.**
|
|
8
|
+
|
|
9
|
+
While `@ydant/core` is a pure engine, base provides everything users need:
|
|
10
|
+
|
|
11
|
+
- Element factories (`div`, `span`, etc.)
|
|
12
|
+
- Primitives (`text`, `attr`, `on`, `style`, etc.)
|
|
13
|
+
- DOM operations (`appendChild`, `setCurrentElement`)
|
|
14
|
+
- Lifecycle management (`onMount`, `onUnmount`)
|
|
15
|
+
- Keyed element diffing
|
|
16
|
+
|
|
17
|
+
This separation means core remains stable, while base can evolve. Other plugins stand on equal footing with base—they extend core the same way.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @ydant/base
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { mount } from "@ydant/core";
|
|
29
|
+
import { createBasePlugin, div, p, text, classes, type Component } from "@ydant/base";
|
|
30
|
+
|
|
31
|
+
const Greeting: Component = () =>
|
|
32
|
+
div(function* () {
|
|
33
|
+
yield* classes("greeting");
|
|
34
|
+
yield* p(() => [text("Hello World!")]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
mount(Greeting, document.getElementById("app")!, {
|
|
38
|
+
plugins: [createBasePlugin()],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
### Plugin
|
|
45
|
+
|
|
46
|
+
| Function | Description |
|
|
47
|
+
| -------------------- | ------------------------------------------ |
|
|
48
|
+
| `createBasePlugin()` | Create plugin to process base DSL elements |
|
|
49
|
+
|
|
50
|
+
The base plugin extends `RenderContext` and `PluginAPI`:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// RenderContext extensions
|
|
54
|
+
interface RenderContextExtensions {
|
|
55
|
+
isCurrentElementReused: boolean;
|
|
56
|
+
pendingKey: string | number | null;
|
|
57
|
+
keyedNodes: Map<string | number, KeyedNode>;
|
|
58
|
+
mountCallbacks: Array<() => void | (() => void)>;
|
|
59
|
+
unmountCallbacks: Array<() => void>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// PluginAPI extensions (in addition to core's parent, currentElement, processChildren, createChildAPI)
|
|
63
|
+
interface PluginAPIExtensions {
|
|
64
|
+
// DOM operations
|
|
65
|
+
readonly isCurrentElementReused: boolean;
|
|
66
|
+
setCurrentElementReused(reused: boolean): void;
|
|
67
|
+
appendChild(node: Node): void;
|
|
68
|
+
setCurrentElement(element: Element | null): void;
|
|
69
|
+
setParent(parent: Node): void;
|
|
70
|
+
|
|
71
|
+
// Lifecycle
|
|
72
|
+
onMount(callback: () => void | (() => void)): void;
|
|
73
|
+
onUnmount(callback: () => void): void;
|
|
74
|
+
addUnmountCallbacks(...callbacks: Array<() => void>): void;
|
|
75
|
+
executeMount(): void;
|
|
76
|
+
|
|
77
|
+
// Keyed elements
|
|
78
|
+
readonly pendingKey: string | number | null;
|
|
79
|
+
setPendingKey(key: string | number | null): void;
|
|
80
|
+
getKeyedNode(key: string | number): KeyedNode | undefined;
|
|
81
|
+
setKeyedNode(key: string | number, node: KeyedNode): void;
|
|
82
|
+
deleteKeyedNode(key: string | number): void;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Element Factories
|
|
87
|
+
|
|
88
|
+
HTML elements: `div`, `span`, `p`, `button`, `input`, `h1`-`h3`, `ul`, `li`, `a`, `form`, `table`, `thead`, `tbody`, `tr`, `th`, `td`, `label`, `textarea`, `select`, `option`, `nav`, `header`, `footer`, `section`, `article`, `aside`, `main`, `img`
|
|
89
|
+
|
|
90
|
+
SVG elements: `svg`, `circle`, `ellipse`, `line`, `path`, `polygon`, `polyline`, `rect`, `g`, `defs`, `use`, `clipPath`, `mask`, `linearGradient`, `radialGradient`, `stop`, `svgText`, `tspan`
|
|
91
|
+
|
|
92
|
+
### Primitives
|
|
93
|
+
|
|
94
|
+
| Function | Description |
|
|
95
|
+
| --------------------- | -------------------------- |
|
|
96
|
+
| `text(content)` | Create a text node |
|
|
97
|
+
| `attr(key, value)` | Set an HTML attribute |
|
|
98
|
+
| `classes(...names)` | Set class attribute |
|
|
99
|
+
| `on(event, handler)` | Add event listener |
|
|
100
|
+
| `style(styles)` | Set inline styles |
|
|
101
|
+
| `key(value)` | Set key for list diffing |
|
|
102
|
+
| `onMount(callback)` | Lifecycle hook for mount |
|
|
103
|
+
| `onUnmount(callback)` | Lifecycle hook for unmount |
|
|
104
|
+
|
|
105
|
+
#### `on()` Type Overloads
|
|
106
|
+
|
|
107
|
+
`on()` provides type-safe overloads for known DOM event types:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Type-safe: handler receives MouseEvent
|
|
111
|
+
yield *
|
|
112
|
+
on("click", (e) => {
|
|
113
|
+
/* e: MouseEvent */
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Type-safe: handler receives KeyboardEvent
|
|
117
|
+
yield *
|
|
118
|
+
on("keydown", (e) => {
|
|
119
|
+
/* e: KeyboardEvent */
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Generic fallback for custom events
|
|
123
|
+
yield *
|
|
124
|
+
on("my-event", (e) => {
|
|
125
|
+
/* e: Event */
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Types
|
|
130
|
+
|
|
131
|
+
| Type | Description |
|
|
132
|
+
| --------------- | ------------------------------------------------------------------- |
|
|
133
|
+
| `Slot` | `{ node: HTMLElement, refresh: (fn) => void }` |
|
|
134
|
+
| `SlotRef` | Mutable reference holder for a `Slot`, created by `createSlotRef()` |
|
|
135
|
+
| `Render` | `Generator<Child, ChildReturn, ChildNext>` - Rendering generator |
|
|
136
|
+
| `Component` | `() => Render` - Root component type |
|
|
137
|
+
| `ElementRender` | `Generator<Element, Slot, ChildNext>` - Element factory return |
|
|
138
|
+
| `Element` | Tagged type for HTML/SVG elements |
|
|
139
|
+
| `Attribute` | Tagged type for attributes |
|
|
140
|
+
| `Listener` | Tagged type for event listeners |
|
|
141
|
+
| `Text` | Tagged type for text nodes |
|
|
142
|
+
| `Lifecycle` | Tagged type for lifecycle hooks |
|
|
143
|
+
| `Key` | Tagged type for list keys |
|
|
144
|
+
|
|
145
|
+
### createSlotRef
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
function createSlotRef(): SlotRef;
|
|
149
|
+
|
|
150
|
+
interface SlotRef {
|
|
151
|
+
current: Slot | null;
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Creates a mutable reference to a `Slot`. Useful for capturing a Slot reference within array syntax where `yield*` is not available:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
const ref = createSlotRef();
|
|
159
|
+
|
|
160
|
+
yield *
|
|
161
|
+
div(function* () {
|
|
162
|
+
ref.current = yield* div(() => [text("Content")]);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Later: update via ref
|
|
166
|
+
ref.current?.refresh(() => [text("Updated!")]);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Key and Element Reuse
|
|
170
|
+
|
|
171
|
+
When using `key()` for list items, the same key will reuse the existing DOM element:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
yield *
|
|
175
|
+
ul(function* () {
|
|
176
|
+
for (const item of items) {
|
|
177
|
+
yield* key(item.id);
|
|
178
|
+
yield* li(() => [text(item.name)]);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Constraints when reusing keyed elements:**
|
|
184
|
+
|
|
185
|
+
- **Listeners are not re-registered**: Event handlers remain from the original element. If you need to change handlers, use a different key.
|
|
186
|
+
- **Lifecycle callbacks are not re-registered**: `onMount`/`onUnmount` from the original registration are kept.
|
|
187
|
+
- **Attributes are updated**: Attribute values are refreshed on each render.
|
|
188
|
+
|
|
189
|
+
This design assumes that components with the same key have the same structure. If you need different behavior, change the key to force a new element.
|
|
190
|
+
|
|
191
|
+
## Syntax
|
|
192
|
+
|
|
193
|
+
### Generator Syntax
|
|
194
|
+
|
|
195
|
+
Use when you need the `Slot` return value:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
const slot =
|
|
199
|
+
yield *
|
|
200
|
+
div(function* () {
|
|
201
|
+
yield* text("Content");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Later: update the content
|
|
205
|
+
slot.refresh(() => [text("Updated!")]);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Array Syntax
|
|
209
|
+
|
|
210
|
+
Use for static structures:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
yield * div(() => [classes("container"), text("Static content")]);
|
|
214
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Builder } from '@ydant/core';
|
|
2
|
+
import { ElementRender } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* HTML 要素ファクトリを作成
|
|
5
|
+
*
|
|
6
|
+
* @returns ElementRender を返すジェネレーター関数
|
|
7
|
+
* yield* で使用すると Slot を返す
|
|
8
|
+
*/
|
|
9
|
+
export declare function createHTMLElement(tag: string): (builder: Builder) => ElementRender;
|
|
10
|
+
/**
|
|
11
|
+
* SVG 要素ファクトリを作成
|
|
12
|
+
*
|
|
13
|
+
* @returns ElementRender を返すジェネレーター関数
|
|
14
|
+
* yield* で使用すると Slot を返す
|
|
15
|
+
*/
|
|
16
|
+
export declare function createSVGElement(tag: string): (builder: Builder) => ElementRender;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Builder } from '@ydant/core';
|
|
2
|
+
import { ElementRender } from '../types';
|
|
3
|
+
export declare const div: (builder: Builder) => ElementRender;
|
|
4
|
+
export declare const span: (builder: Builder) => ElementRender;
|
|
5
|
+
export declare const p: (builder: Builder) => ElementRender;
|
|
6
|
+
export declare const h1: (builder: Builder) => ElementRender;
|
|
7
|
+
export declare const h2: (builder: Builder) => ElementRender;
|
|
8
|
+
export declare const h3: (builder: Builder) => ElementRender;
|
|
9
|
+
export declare const img: (builder: Builder) => ElementRender;
|
|
10
|
+
export declare const button: (builder: Builder) => ElementRender;
|
|
11
|
+
export declare const input: (builder: Builder) => ElementRender;
|
|
12
|
+
export declare const ul: (builder: Builder) => ElementRender;
|
|
13
|
+
export declare const li: (builder: Builder) => ElementRender;
|
|
14
|
+
export declare const a: (builder: Builder) => ElementRender;
|
|
15
|
+
export declare const section: (builder: Builder) => ElementRender;
|
|
16
|
+
export declare const header: (builder: Builder) => ElementRender;
|
|
17
|
+
export declare const footer: (builder: Builder) => ElementRender;
|
|
18
|
+
export declare const nav: (builder: Builder) => ElementRender;
|
|
19
|
+
export declare const main: (builder: Builder) => ElementRender;
|
|
20
|
+
export declare const article: (builder: Builder) => ElementRender;
|
|
21
|
+
export declare const aside: (builder: Builder) => ElementRender;
|
|
22
|
+
export declare const form: (builder: Builder) => ElementRender;
|
|
23
|
+
export declare const label: (builder: Builder) => ElementRender;
|
|
24
|
+
export declare const textarea: (builder: Builder) => ElementRender;
|
|
25
|
+
export declare const select: (builder: Builder) => ElementRender;
|
|
26
|
+
export declare const option: (builder: Builder) => ElementRender;
|
|
27
|
+
export declare const table: (builder: Builder) => ElementRender;
|
|
28
|
+
export declare const thead: (builder: Builder) => ElementRender;
|
|
29
|
+
export declare const tbody: (builder: Builder) => ElementRender;
|
|
30
|
+
export declare const tr: (builder: Builder) => ElementRender;
|
|
31
|
+
export declare const th: (builder: Builder) => ElementRender;
|
|
32
|
+
export declare const td: (builder: Builder) => ElementRender;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Builder } from '@ydant/core';
|
|
2
|
+
import { ElementRender } from '../types';
|
|
3
|
+
export declare const svg: (builder: Builder) => ElementRender;
|
|
4
|
+
export declare const circle: (builder: Builder) => ElementRender;
|
|
5
|
+
export declare const ellipse: (builder: Builder) => ElementRender;
|
|
6
|
+
export declare const line: (builder: Builder) => ElementRender;
|
|
7
|
+
export declare const path: (builder: Builder) => ElementRender;
|
|
8
|
+
export declare const polygon: (builder: Builder) => ElementRender;
|
|
9
|
+
export declare const polyline: (builder: Builder) => ElementRender;
|
|
10
|
+
export declare const rect: (builder: Builder) => ElementRender;
|
|
11
|
+
export declare const g: (builder: Builder) => ElementRender;
|
|
12
|
+
export declare const defs: (builder: Builder) => ElementRender;
|
|
13
|
+
export declare const use: (builder: Builder) => ElementRender;
|
|
14
|
+
export declare const clipPath: (builder: Builder) => ElementRender;
|
|
15
|
+
export declare const mask: (builder: Builder) => ElementRender;
|
|
16
|
+
export declare const linearGradient: (builder: Builder) => ElementRender;
|
|
17
|
+
export declare const radialGradient: (builder: Builder) => ElementRender;
|
|
18
|
+
export declare const stop: (builder: Builder) => ElementRender;
|
|
19
|
+
export declare const svgText: (builder: Builder) => ElementRender;
|
|
20
|
+
export declare const tspan: (builder: Builder) => ElementRender;
|
package/dist/global.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Builder, PluginAPI } from '@ydant/core';
|
|
2
|
+
import { Element, Attribute, Listener, Text, Lifecycle, Slot, KeyedNode } from './types';
|
|
3
|
+
declare module "@ydant/core" {
|
|
4
|
+
// RenderContext に base プラグイン用のプロパティを追加
|
|
5
|
+
interface RenderContextExtensions {
|
|
6
|
+
/** 現在の要素が再利用されたかどうか(リスナー・ライフサイクルの重複登録を防ぐ) */
|
|
7
|
+
isCurrentElementReused: boolean;
|
|
8
|
+
/** キー付き要素のマップ */
|
|
9
|
+
keyedNodes: Map<string | number, KeyedNode>;
|
|
10
|
+
/** マウント時に実行するコールバック */
|
|
11
|
+
mountCallbacks: Array<() => void | (() => void)>;
|
|
12
|
+
/** アンマウント時に実行するコールバック */
|
|
13
|
+
unmountCallbacks: Array<() => void>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// PluginAPI に base プラグインのメソッドを追加
|
|
17
|
+
interface PluginAPIExtensions {
|
|
18
|
+
// === DOM 操作 ===
|
|
19
|
+
/** 現在の親ノード */
|
|
20
|
+
readonly parent: Node;
|
|
21
|
+
/** 現在処理中の要素 */
|
|
22
|
+
readonly currentElement: globalThis.Element | null;
|
|
23
|
+
/** 現在の要素を設定 */
|
|
24
|
+
setCurrentElement(element: globalThis.Element | null): void;
|
|
25
|
+
/** 親を設定 */
|
|
26
|
+
setParent(parent: Node): void;
|
|
27
|
+
/** 親ノードに子ノードを追加 */
|
|
28
|
+
appendChild(node: Node): void;
|
|
29
|
+
|
|
30
|
+
// === ライフサイクル ===
|
|
31
|
+
/** マウント時のコールバックを登録 */
|
|
32
|
+
onMount(callback: () => void | (() => void)): void;
|
|
33
|
+
/** アンマウント時のコールバックを登録 */
|
|
34
|
+
onUnmount(callback: () => void): void;
|
|
35
|
+
/** unmount コールバックを追加 */
|
|
36
|
+
addUnmountCallbacks(...callbacks: Array<() => void>): void;
|
|
37
|
+
/** mount コールバックを実行 */
|
|
38
|
+
executeMount(): void;
|
|
39
|
+
/** 現在のコンテキストの unmount コールバックを取得 */
|
|
40
|
+
getUnmountCallbacks(): Array<() => void>;
|
|
41
|
+
|
|
42
|
+
// === 子要素処理 ===
|
|
43
|
+
/** 子要素を処理する */
|
|
44
|
+
processChildren(
|
|
45
|
+
builder: Builder,
|
|
46
|
+
options?: {
|
|
47
|
+
parent?: Node;
|
|
48
|
+
inheritContext?: boolean;
|
|
49
|
+
},
|
|
50
|
+
): void;
|
|
51
|
+
/** 新しい子コンテキストの API を作成 */
|
|
52
|
+
createChildAPI(parent: Node): PluginAPI;
|
|
53
|
+
|
|
54
|
+
// === keyed 要素 ===
|
|
55
|
+
/** 現在の要素が再利用されたかどうか */
|
|
56
|
+
readonly isCurrentElementReused: boolean;
|
|
57
|
+
/** 要素再利用フラグを設定 */
|
|
58
|
+
setCurrentElementReused(reused: boolean): void;
|
|
59
|
+
/** keyed node を取得 */
|
|
60
|
+
getKeyedNode(key: string | number): KeyedNode | undefined;
|
|
61
|
+
/** keyed node を設定 */
|
|
62
|
+
setKeyedNode(key: string | number, node: KeyedNode): void;
|
|
63
|
+
/** keyed node を削除 */
|
|
64
|
+
deleteKeyedNode(key: string | number): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// base の DSL 型を Child に追加
|
|
68
|
+
interface PluginChildExtensions {
|
|
69
|
+
Element: Element;
|
|
70
|
+
Attribute: Attribute;
|
|
71
|
+
Listener: Listener;
|
|
72
|
+
Text: Text;
|
|
73
|
+
Lifecycle: Lifecycle;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Slot を ChildNext に追加(Element の yield* で受け取る値)
|
|
77
|
+
interface PluginNextExtensions {
|
|
78
|
+
Slot: Slot;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Slot を ChildReturn に追加(Component の戻り値)
|
|
82
|
+
interface PluginReturnExtensions {
|
|
83
|
+
Slot: Slot;
|
|
84
|
+
}
|
|
85
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference path="./global.d.ts" />
|
|
2
|
+
/**
|
|
3
|
+
* @ydant/base
|
|
4
|
+
*
|
|
5
|
+
* 要素ファクトリ・プリミティブ DSL とそれらを処理するベースプラグイン
|
|
6
|
+
*/
|
|
7
|
+
export type { Slot, ElementRender, Element, Attribute, Listener, Text, Lifecycle, Decoration, } from './types';
|
|
8
|
+
export { attr, classes, on, text, onMount, onUnmount, style, keyed } from './primitives';
|
|
9
|
+
export { div, span, p, h1, h2, h3, img, button, input, ul, li, a, section, header, footer, nav, main, article, aside, form, label, textarea, select, option, table, thead, tbody, tr, th, td, } from './elements/html';
|
|
10
|
+
export { svg, circle, ellipse, line, path, polygon, polyline, rect, g, defs, use, clipPath, mask, linearGradient, radialGradient, stop, svgText, tspan, } from './elements/svg';
|
|
11
|
+
export { createSlotRef } from './slot-ref';
|
|
12
|
+
export type { SlotRef } from './slot-ref';
|
|
13
|
+
export { createHTMLElement, createSVGElement } from './elements/factory';
|
|
14
|
+
export { createBasePlugin } from './plugin';
|
package/dist/index.es.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { toChildren as f, isTagged as a } from "@ydant/core";
|
|
2
|
+
function m(e) {
|
|
3
|
+
return function* (...t) {
|
|
4
|
+
yield e(...t);
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
const M = m(
|
|
8
|
+
(e, t) => ({
|
|
9
|
+
type: "attribute",
|
|
10
|
+
key: e,
|
|
11
|
+
value: t
|
|
12
|
+
})
|
|
13
|
+
);
|
|
14
|
+
function w(...e) {
|
|
15
|
+
return (function* () {
|
|
16
|
+
yield { type: "attribute", key: "class", value: e.flat().join(" ") };
|
|
17
|
+
})();
|
|
18
|
+
}
|
|
19
|
+
function K(e, t) {
|
|
20
|
+
return (function* () {
|
|
21
|
+
yield { type: "listener", key: e, value: t };
|
|
22
|
+
})();
|
|
23
|
+
}
|
|
24
|
+
const P = m((e) => ({ type: "text", content: e }));
|
|
25
|
+
function* R(e) {
|
|
26
|
+
yield { type: "lifecycle", event: "mount", callback: e };
|
|
27
|
+
}
|
|
28
|
+
function* x(e) {
|
|
29
|
+
yield { type: "lifecycle", event: "unmount", callback: e };
|
|
30
|
+
}
|
|
31
|
+
function* L(e) {
|
|
32
|
+
yield { type: "attribute", key: "style", value: Object.entries(e).map(([n, o]) => `${n.startsWith("--") ? n : n.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${o}`).join("; ") };
|
|
33
|
+
}
|
|
34
|
+
function S(e, t) {
|
|
35
|
+
return (...n) => (function* () {
|
|
36
|
+
const o = t(...n), u = o.next();
|
|
37
|
+
if (u.done) return u.value;
|
|
38
|
+
const c = yield { ...u.value, key: e };
|
|
39
|
+
return o.next(c), c;
|
|
40
|
+
})();
|
|
41
|
+
}
|
|
42
|
+
const b = "http://www.w3.org/2000/svg";
|
|
43
|
+
function r(e) {
|
|
44
|
+
return function* (t) {
|
|
45
|
+
const n = f(t());
|
|
46
|
+
return yield { type: "element", tag: e, children: n };
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function l(e) {
|
|
50
|
+
return function* (t) {
|
|
51
|
+
const n = f(t());
|
|
52
|
+
return yield {
|
|
53
|
+
type: "element",
|
|
54
|
+
tag: e,
|
|
55
|
+
children: n,
|
|
56
|
+
ns: b
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const G = r("div"), T = r("span"), A = r("p"), j = r("h1"), V = r("h2"), $ = r("h3"), H = r("img"), I = r("button"), O = r("input"), _ = r("ul"), q = r("li"), B = r("a"), D = r("section"), F = r("header"), W = r("footer"), Z = r("nav"), z = r("main"), J = r("article"), Q = r("aside"), X = r("form"), Y = r("label"), ee = r("textarea"), te = r("select"), ne = r("option"), oe = r("table"), se = r("thead"), re = r("tbody"), ue = r("tr"), le = r("th"), ce = r("td"), ae = l("svg"), ie = l("circle"), de = l("ellipse"), fe = l("line"), me = l("path"), ye = l("polygon"), be = l("polyline"), pe = l("rect"), Ce = l("g"), ke = l("defs"), he = l("use"), ge = l("clipPath"), ve = l("mask"), Ee = l("linearGradient"), Ue = l("radialGradient"), Ne = l("stop"), Me = l("text"), we = l("tspan");
|
|
61
|
+
function Ke() {
|
|
62
|
+
let e = null;
|
|
63
|
+
return {
|
|
64
|
+
get current() {
|
|
65
|
+
return e;
|
|
66
|
+
},
|
|
67
|
+
bind(t) {
|
|
68
|
+
e = t;
|
|
69
|
+
},
|
|
70
|
+
refresh(t) {
|
|
71
|
+
e && e.refresh(t);
|
|
72
|
+
},
|
|
73
|
+
get node() {
|
|
74
|
+
return e?.node ?? null;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function p(e, t) {
|
|
79
|
+
const n = e.key ?? null;
|
|
80
|
+
let o, u = !1;
|
|
81
|
+
if (n !== null && t.getKeyedNode(n)) {
|
|
82
|
+
const d = t.getKeyedNode(n);
|
|
83
|
+
o = d.node, u = !0, t.addUnmountCallbacks(...d.unmountCallbacks), t.deleteKeyedNode(n);
|
|
84
|
+
} else
|
|
85
|
+
o = e.ns ? document.createElementNS(e.ns, e.tag) : document.createElement(e.tag);
|
|
86
|
+
t.appendChild(o), C(e, o, u);
|
|
87
|
+
const s = t.createChildAPI(o), c = [];
|
|
88
|
+
n !== null && t.setKeyedNode(n, {
|
|
89
|
+
key: n,
|
|
90
|
+
node: o,
|
|
91
|
+
unmountCallbacks: c
|
|
92
|
+
});
|
|
93
|
+
const i = k(o, s, c);
|
|
94
|
+
u && (o.innerHTML = ""), e.children && s.processChildren(() => e.children, {
|
|
95
|
+
parent: o
|
|
96
|
+
});
|
|
97
|
+
const y = s.getUnmountCallbacks();
|
|
98
|
+
return c.push(...y), t.addUnmountCallbacks(...c), s.executeMount(), { value: i };
|
|
99
|
+
}
|
|
100
|
+
function C(e, t, n) {
|
|
101
|
+
if (e.decorations)
|
|
102
|
+
for (const o of e.decorations)
|
|
103
|
+
a(o, "attribute") ? t.setAttribute(o.key, o.value) : a(o, "listener") && (n || t.addEventListener(o.key, o.value));
|
|
104
|
+
}
|
|
105
|
+
function k(e, t, n) {
|
|
106
|
+
return {
|
|
107
|
+
node: e,
|
|
108
|
+
refresh(o) {
|
|
109
|
+
const u = t.getUnmountCallbacks(), s = /* @__PURE__ */ new Set([...n, ...u]);
|
|
110
|
+
for (const i of s)
|
|
111
|
+
i();
|
|
112
|
+
for (n.length = 0; e.firstChild; )
|
|
113
|
+
e.removeChild(e.firstChild);
|
|
114
|
+
t.processChildren(o, { parent: e });
|
|
115
|
+
const c = t.getUnmountCallbacks();
|
|
116
|
+
n.push(...c), t.executeMount();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function h(e, t) {
|
|
121
|
+
const n = t.currentElement;
|
|
122
|
+
return n && n.setAttribute(e.key, e.value), {};
|
|
123
|
+
}
|
|
124
|
+
function g(e, t) {
|
|
125
|
+
if (t.isCurrentElementReused)
|
|
126
|
+
return {};
|
|
127
|
+
const n = t.currentElement;
|
|
128
|
+
return n && n.addEventListener(e.key, e.value), {};
|
|
129
|
+
}
|
|
130
|
+
function v(e, t) {
|
|
131
|
+
const n = document.createTextNode(e.content);
|
|
132
|
+
return t.appendChild(n), {};
|
|
133
|
+
}
|
|
134
|
+
function E(e, t) {
|
|
135
|
+
return t.isCurrentElementReused ? {} : (e.event === "mount" ? t.onMount(e.callback) : e.event === "unmount" && t.onUnmount(e.callback), {});
|
|
136
|
+
}
|
|
137
|
+
function U(e) {
|
|
138
|
+
const t = e.mountCallbacks, n = e.unmountCallbacks;
|
|
139
|
+
requestAnimationFrame(() => {
|
|
140
|
+
for (const o of t) {
|
|
141
|
+
const u = o();
|
|
142
|
+
typeof u == "function" && n.push(u);
|
|
143
|
+
}
|
|
144
|
+
e.mountCallbacks = [];
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function Pe() {
|
|
148
|
+
return {
|
|
149
|
+
name: "base",
|
|
150
|
+
types: ["element", "text", "attribute", "listener", "lifecycle"],
|
|
151
|
+
initContext(e) {
|
|
152
|
+
e.isCurrentElementReused = !1, e.keyedNodes = /* @__PURE__ */ new Map(), e.mountCallbacks = [], e.unmountCallbacks = [];
|
|
153
|
+
},
|
|
154
|
+
mergeChildContext(e, t) {
|
|
155
|
+
const n = e.mountCallbacks, o = t.mountCallbacks, u = e.unmountCallbacks, s = t.unmountCallbacks;
|
|
156
|
+
n && o && n.push(...o), u && s && u.push(...s);
|
|
157
|
+
},
|
|
158
|
+
extendAPI(e, t) {
|
|
159
|
+
Object.defineProperty(e, "isCurrentElementReused", {
|
|
160
|
+
get() {
|
|
161
|
+
return t.isCurrentElementReused;
|
|
162
|
+
},
|
|
163
|
+
enumerable: !0
|
|
164
|
+
}), e.appendChild = (s) => {
|
|
165
|
+
t.parent.appendChild(s);
|
|
166
|
+
}, e.setCurrentElement = (s) => {
|
|
167
|
+
t.currentElement = s;
|
|
168
|
+
}, e.setParent = (s) => {
|
|
169
|
+
t.parent = s;
|
|
170
|
+
}, e.setCurrentElementReused = (s) => {
|
|
171
|
+
t.isCurrentElementReused = s;
|
|
172
|
+
};
|
|
173
|
+
const n = t.keyedNodes;
|
|
174
|
+
e.getKeyedNode = (s) => n.get(s), e.setKeyedNode = (s, c) => {
|
|
175
|
+
n.set(s, c);
|
|
176
|
+
}, e.deleteKeyedNode = (s) => {
|
|
177
|
+
n.delete(s);
|
|
178
|
+
};
|
|
179
|
+
const o = t.mountCallbacks, u = t.unmountCallbacks;
|
|
180
|
+
e.onMount = (s) => {
|
|
181
|
+
o.push(s);
|
|
182
|
+
}, e.onUnmount = (s) => {
|
|
183
|
+
u.push(s);
|
|
184
|
+
}, e.addUnmountCallbacks = (...s) => {
|
|
185
|
+
u.push(...s);
|
|
186
|
+
}, e.executeMount = () => {
|
|
187
|
+
U(t);
|
|
188
|
+
}, e.getUnmountCallbacks = () => u;
|
|
189
|
+
},
|
|
190
|
+
process(e, t) {
|
|
191
|
+
return a(e, "element") ? p(e, t) : a(e, "text") ? v(e, t) : a(e, "attribute") ? h(e, t) : a(e, "listener") ? g(e, t) : a(e, "lifecycle") ? E(e, t) : {};
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export {
|
|
196
|
+
B as a,
|
|
197
|
+
J as article,
|
|
198
|
+
Q as aside,
|
|
199
|
+
M as attr,
|
|
200
|
+
I as button,
|
|
201
|
+
ie as circle,
|
|
202
|
+
w as classes,
|
|
203
|
+
ge as clipPath,
|
|
204
|
+
Pe as createBasePlugin,
|
|
205
|
+
r as createHTMLElement,
|
|
206
|
+
l as createSVGElement,
|
|
207
|
+
Ke as createSlotRef,
|
|
208
|
+
ke as defs,
|
|
209
|
+
G as div,
|
|
210
|
+
de as ellipse,
|
|
211
|
+
W as footer,
|
|
212
|
+
X as form,
|
|
213
|
+
Ce as g,
|
|
214
|
+
j as h1,
|
|
215
|
+
V as h2,
|
|
216
|
+
$ as h3,
|
|
217
|
+
F as header,
|
|
218
|
+
H as img,
|
|
219
|
+
O as input,
|
|
220
|
+
S as keyed,
|
|
221
|
+
Y as label,
|
|
222
|
+
q as li,
|
|
223
|
+
fe as line,
|
|
224
|
+
Ee as linearGradient,
|
|
225
|
+
z as main,
|
|
226
|
+
ve as mask,
|
|
227
|
+
Z as nav,
|
|
228
|
+
K as on,
|
|
229
|
+
R as onMount,
|
|
230
|
+
x as onUnmount,
|
|
231
|
+
ne as option,
|
|
232
|
+
A as p,
|
|
233
|
+
me as path,
|
|
234
|
+
ye as polygon,
|
|
235
|
+
be as polyline,
|
|
236
|
+
Ue as radialGradient,
|
|
237
|
+
pe as rect,
|
|
238
|
+
D as section,
|
|
239
|
+
te as select,
|
|
240
|
+
T as span,
|
|
241
|
+
Ne as stop,
|
|
242
|
+
L as style,
|
|
243
|
+
ae as svg,
|
|
244
|
+
Me as svgText,
|
|
245
|
+
oe as table,
|
|
246
|
+
re as tbody,
|
|
247
|
+
ce as td,
|
|
248
|
+
P as text,
|
|
249
|
+
ee as textarea,
|
|
250
|
+
le as th,
|
|
251
|
+
se as thead,
|
|
252
|
+
ue as tr,
|
|
253
|
+
we as tspan,
|
|
254
|
+
_ as ul,
|
|
255
|
+
he as use
|
|
256
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(n,i){typeof exports=="object"&&typeof module<"u"?i(exports,require("@ydant/core")):typeof define=="function"&&define.amd?define(["exports","@ydant/core"],i):(n=typeof globalThis<"u"?globalThis:n||self,i(n.YdantBase={},n.YdantCore))})(this,(function(n,i){"use strict";function f(e){return function*(...t){yield e(...t)}}const y=f((e,t)=>({type:"attribute",key:e,value:t}));function b(...e){return(function*(){yield{type:"attribute",key:"class",value:e.flat().join(" ")}})()}function g(e,t){return(function*(){yield{type:"listener",key:e,value:t}})()}const h=f(e=>({type:"text",content:e}));function*C(e){yield{type:"lifecycle",event:"mount",callback:e}}function*k(e){yield{type:"lifecycle",event:"unmount",callback:e}}function*v(e){yield{type:"attribute",key:"style",value:Object.entries(e).map(([l,u])=>`${l.startsWith("--")?l:l.replace(/([A-Z])/g,"-$1").toLowerCase()}: ${u}`).join("; ")}}function E(e,t){return(...l)=>(function*(){const u=t(...l),c=u.next();if(c.done)return c.value;const r=yield{...c.value,key:e};return u.next(r),r})()}const T="http://www.w3.org/2000/svg";function o(e){return function*(t){const l=i.toChildren(t());return yield{type:"element",tag:e,children:l}}}function a(e){return function*(t){const l=i.toChildren(t());return yield{type:"element",tag:e,children:l,ns:T}}}const U=o("div"),M=o("span"),N=o("p"),P=o("h1"),S=o("h2"),w=o("h3"),G=o("img"),R=o("button"),K=o("input"),L=o("ul"),j=o("li"),p=o("a"),A=o("section"),V=o("header"),B=o("footer"),H=o("nav"),O=o("main"),$=o("article"),q=o("aside"),I=o("form"),Y=o("label"),_=o("textarea"),D=o("select"),F=o("option"),W=o("table"),Z=o("thead"),z=o("tbody"),J=o("tr"),Q=o("th"),X=o("td"),x=a("svg"),ee=a("circle"),ne=a("ellipse"),te=a("line"),le=a("path"),oe=a("polygon"),ue=a("polyline"),se=a("rect"),ce=a("g"),ae=a("defs"),ie=a("use"),re=a("clipPath"),de=a("mask"),fe=a("linearGradient"),me=a("radialGradient"),ye=a("stop"),be=a("text"),ge=a("tspan");function he(){let e=null;return{get current(){return e},bind(t){e=t},refresh(t){e&&e.refresh(t)},get node(){return e?.node??null}}}function Ce(e,t){const l=e.key??null;let u,c=!1;if(l!==null&&t.getKeyedNode(l)){const m=t.getKeyedNode(l);u=m.node,c=!0,t.addUnmountCallbacks(...m.unmountCallbacks),t.deleteKeyedNode(l)}else u=e.ns?document.createElementNS(e.ns,e.tag):document.createElement(e.tag);t.appendChild(u),ke(e,u,c);const s=t.createChildAPI(u),r=[];l!==null&&t.setKeyedNode(l,{key:l,node:u,unmountCallbacks:r});const d=ve(u,s,r);c&&(u.innerHTML=""),e.children&&s.processChildren(()=>e.children,{parent:u});const Se=s.getUnmountCallbacks();return r.push(...Se),t.addUnmountCallbacks(...r),s.executeMount(),{value:d}}function ke(e,t,l){if(e.decorations)for(const u of e.decorations)i.isTagged(u,"attribute")?t.setAttribute(u.key,u.value):i.isTagged(u,"listener")&&(l||t.addEventListener(u.key,u.value))}function ve(e,t,l){return{node:e,refresh(u){const c=t.getUnmountCallbacks(),s=new Set([...l,...c]);for(const d of s)d();for(l.length=0;e.firstChild;)e.removeChild(e.firstChild);t.processChildren(u,{parent:e});const r=t.getUnmountCallbacks();l.push(...r),t.executeMount()}}}function Ee(e,t){const l=t.currentElement;return l&&l.setAttribute(e.key,e.value),{}}function Te(e,t){if(t.isCurrentElementReused)return{};const l=t.currentElement;return l&&l.addEventListener(e.key,e.value),{}}function Ue(e,t){const l=document.createTextNode(e.content);return t.appendChild(l),{}}function Me(e,t){return t.isCurrentElementReused?{}:(e.event==="mount"?t.onMount(e.callback):e.event==="unmount"&&t.onUnmount(e.callback),{})}function Ne(e){const t=e.mountCallbacks,l=e.unmountCallbacks;requestAnimationFrame(()=>{for(const u of t){const c=u();typeof c=="function"&&l.push(c)}e.mountCallbacks=[]})}function Pe(){return{name:"base",types:["element","text","attribute","listener","lifecycle"],initContext(e){e.isCurrentElementReused=!1,e.keyedNodes=new Map,e.mountCallbacks=[],e.unmountCallbacks=[]},mergeChildContext(e,t){const l=e.mountCallbacks,u=t.mountCallbacks,c=e.unmountCallbacks,s=t.unmountCallbacks;l&&u&&l.push(...u),c&&s&&c.push(...s)},extendAPI(e,t){Object.defineProperty(e,"isCurrentElementReused",{get(){return t.isCurrentElementReused},enumerable:!0}),e.appendChild=s=>{t.parent.appendChild(s)},e.setCurrentElement=s=>{t.currentElement=s},e.setParent=s=>{t.parent=s},e.setCurrentElementReused=s=>{t.isCurrentElementReused=s};const l=t.keyedNodes;e.getKeyedNode=s=>l.get(s),e.setKeyedNode=(s,r)=>{l.set(s,r)},e.deleteKeyedNode=s=>{l.delete(s)};const u=t.mountCallbacks,c=t.unmountCallbacks;e.onMount=s=>{u.push(s)},e.onUnmount=s=>{c.push(s)},e.addUnmountCallbacks=(...s)=>{c.push(...s)},e.executeMount=()=>{Ne(t)},e.getUnmountCallbacks=()=>c},process(e,t){return i.isTagged(e,"element")?Ce(e,t):i.isTagged(e,"text")?Ue(e,t):i.isTagged(e,"attribute")?Ee(e,t):i.isTagged(e,"listener")?Te(e,t):i.isTagged(e,"lifecycle")?Me(e,t):{}}}}n.a=p,n.article=$,n.aside=q,n.attr=y,n.button=R,n.circle=ee,n.classes=b,n.clipPath=re,n.createBasePlugin=Pe,n.createHTMLElement=o,n.createSVGElement=a,n.createSlotRef=he,n.defs=ae,n.div=U,n.ellipse=ne,n.footer=B,n.form=I,n.g=ce,n.h1=P,n.h2=S,n.h3=w,n.header=V,n.img=G,n.input=K,n.keyed=E,n.label=Y,n.li=j,n.line=te,n.linearGradient=fe,n.main=O,n.mask=de,n.nav=H,n.on=g,n.onMount=C,n.onUnmount=k,n.option=F,n.p=N,n.path=le,n.polygon=oe,n.polyline=ue,n.radialGradient=me,n.rect=se,n.section=A,n.select=D,n.span=M,n.stop=ye,n.style=v,n.svg=x,n.svgText=be,n.table=W,n.tbody=z,n.td=X,n.text=h,n.textarea=_,n.th=Q,n.thead=Z,n.tr=J,n.tspan=ge,n.ul=L,n.use=ie,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PluginAPI, PluginResult } from '@ydant/core';
|
|
2
|
+
import { Attribute, Listener, Text, Lifecycle } from '../types';
|
|
3
|
+
/** Attribute を処理 */
|
|
4
|
+
export declare function processAttribute(attr: Attribute, api: PluginAPI): PluginResult;
|
|
5
|
+
/** Listener を処理 */
|
|
6
|
+
export declare function processListener(listener: Listener, api: PluginAPI): PluginResult;
|
|
7
|
+
/** Text を処理 */
|
|
8
|
+
export declare function processText(text: Text, api: PluginAPI): PluginResult;
|
|
9
|
+
/** Lifecycle を処理 */
|
|
10
|
+
export declare function processLifecycle(lifecycle: Lifecycle, api: PluginAPI): PluginResult;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { CleanupFn, Primitive, Render } from '@ydant/core';
|
|
2
|
+
import { Attribute, Listener, Text, Lifecycle, ElementRender } from './types';
|
|
3
|
+
/** HTML 属性を yield */
|
|
4
|
+
export declare const attr: (key: string, value: string) => Primitive<Attribute>;
|
|
5
|
+
/** class 属性のショートハンド */
|
|
6
|
+
export declare function classes(...classNames: (string | string[])[]): Primitive<Attribute>;
|
|
7
|
+
/** イベントリスナを yield(HTMLElementEventMap のイベント型を推論) */
|
|
8
|
+
export declare function on<K extends keyof HTMLElementEventMap>(key: K, handler: (e: HTMLElementEventMap[K]) => void): Primitive<Listener>;
|
|
9
|
+
/** イベントリスナを yield(カスタムイベント用フォールバック) */
|
|
10
|
+
export declare function on(key: string, handler: (e: Event) => void): Primitive<Listener>;
|
|
11
|
+
/** テキストノードを yield */
|
|
12
|
+
export declare const text: (content: string) => Primitive<Text>;
|
|
13
|
+
/**
|
|
14
|
+
* コンポーネントがマウントされた時に実行されるコールバックを登録する
|
|
15
|
+
*
|
|
16
|
+
* @param callback - マウント時に実行される関数。クリーンアップ関数を返すことができる。
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* yield* onMount(() => {
|
|
21
|
+
* const interval = setInterval(() => console.log("tick"), 1000);
|
|
22
|
+
* return () => clearInterval(interval); // クリーンアップ
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function onMount(callback: () => void | CleanupFn): Primitive<Lifecycle>;
|
|
27
|
+
/**
|
|
28
|
+
* コンポーネントがアンマウントされる時に実行されるコールバックを登録する
|
|
29
|
+
*
|
|
30
|
+
* @param callback - アンマウント時に実行される関数
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* yield* onUnmount(() => {
|
|
35
|
+
* console.log("Component unmounted");
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function onUnmount(callback: () => void): Primitive<Lifecycle>;
|
|
40
|
+
/**
|
|
41
|
+
* インラインスタイルを設定する
|
|
42
|
+
*
|
|
43
|
+
* @param properties - CSS プロパティのオブジェクト
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* yield* style({
|
|
48
|
+
* padding: "16px",
|
|
49
|
+
* display: "flex",
|
|
50
|
+
* "--primary-color": "#3b82f6",
|
|
51
|
+
* });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function style(properties: Partial<CSSStyleDeclaration> & Record<`--${string}`, string>): Primitive<Attribute>;
|
|
55
|
+
/**
|
|
56
|
+
* 要素ファクトリやコンポーネントをラップし、生成される Element に key を付与する
|
|
57
|
+
*
|
|
58
|
+
* @param key - 一意な識別子(string または number)
|
|
59
|
+
* @param factory - 要素ファクトリ(div, li 等)またはコンポーネント
|
|
60
|
+
* @returns key 付きファクトリ(元と同じ引数を受け取る)
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // 要素ファクトリと組み合わせ
|
|
65
|
+
* yield* keyed(item.id, li)(() => [text(item.name)]);
|
|
66
|
+
*
|
|
67
|
+
* // コンポーネントと組み合わせ
|
|
68
|
+
* yield* keyed(item.id, ListItemView)({ item, onDelete });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function keyed<Args extends unknown[]>(key: string | number, factory: (...args: Args) => Render): (...args: Args) => ElementRender;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Builder } from '@ydant/core';
|
|
2
|
+
import { Slot } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Slot への参照を保持するオブジェクト
|
|
5
|
+
*
|
|
6
|
+
* イベントハンドラ等から Slot を操作するためのヘルパー。
|
|
7
|
+
* bind() で Slot を関連付け、refresh() で子要素を再レンダリングする。
|
|
8
|
+
*/
|
|
9
|
+
export interface SlotRef {
|
|
10
|
+
/** 現在バインドされている Slot(未バインドなら null) */
|
|
11
|
+
readonly current: Slot | null;
|
|
12
|
+
/** Slot をバインドする */
|
|
13
|
+
bind(slot: Slot): void;
|
|
14
|
+
/** バインドされた Slot の子要素を再レンダリングする */
|
|
15
|
+
refresh(children: Builder): void;
|
|
16
|
+
/** バインドされた Slot の DOM 要素(未バインドなら null) */
|
|
17
|
+
readonly node: HTMLElement | null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* SlotRef を作成する
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const counter = createSlotRef();
|
|
25
|
+
* let count = 0;
|
|
26
|
+
*
|
|
27
|
+
* counter.bind(yield* div(function* () {
|
|
28
|
+
* yield* text(`Count: ${count}`);
|
|
29
|
+
* yield* button(function* () {
|
|
30
|
+
* yield* on("click", () => {
|
|
31
|
+
* count++;
|
|
32
|
+
* counter.refresh(() => [text(`Count: ${count}`)]);
|
|
33
|
+
* });
|
|
34
|
+
* yield* text("Increment");
|
|
35
|
+
* });
|
|
36
|
+
* }));
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function createSlotRef(): SlotRef;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Tagged, CleanupFn, Instructor, Builder, ChildNext } from '@ydant/core';
|
|
2
|
+
/** 要素のスロット(DOM 参照と更新関数を持つ) */
|
|
3
|
+
export interface Slot {
|
|
4
|
+
/** マウントされた DOM 要素 */
|
|
5
|
+
readonly node: HTMLElement;
|
|
6
|
+
/** 子要素を再レンダリングする */
|
|
7
|
+
refresh(children: Builder): void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 要素ファクトリの戻り値型
|
|
11
|
+
*
|
|
12
|
+
* Element を yield し、必ず Slot を返すジェネレーター。
|
|
13
|
+
* 汎用の Render より具体的な型で、yield* div() が Slot を返すことを保証する。
|
|
14
|
+
*
|
|
15
|
+
* ChildNext は void を含むが、Element yield 時は必ず Slot が返される。
|
|
16
|
+
* 型システムでこれを表現するため、Element と Slot のみに限定している。
|
|
17
|
+
*/
|
|
18
|
+
export type ElementRender = Generator<Element, Slot, ChildNext>;
|
|
19
|
+
/** HTML 属性 */
|
|
20
|
+
export type Attribute = Tagged<"attribute", {
|
|
21
|
+
key: string;
|
|
22
|
+
value: string;
|
|
23
|
+
}>;
|
|
24
|
+
/** イベントリスナ */
|
|
25
|
+
export type Listener = Tagged<"listener", {
|
|
26
|
+
key: string;
|
|
27
|
+
value: (e: Event) => void;
|
|
28
|
+
}>;
|
|
29
|
+
/** テキストノード */
|
|
30
|
+
export type Text = Tagged<"text", {
|
|
31
|
+
content: string;
|
|
32
|
+
}>;
|
|
33
|
+
/** マウント時のライフサイクルイベント */
|
|
34
|
+
export type MountLifecycle = Tagged<"lifecycle", {
|
|
35
|
+
event: "mount";
|
|
36
|
+
callback: () => void | CleanupFn;
|
|
37
|
+
}>;
|
|
38
|
+
/** アンマウント時のライフサイクルイベント */
|
|
39
|
+
export type UnmountLifecycle = Tagged<"lifecycle", {
|
|
40
|
+
event: "unmount";
|
|
41
|
+
callback: () => void;
|
|
42
|
+
}>;
|
|
43
|
+
/** ライフサイクルイベント */
|
|
44
|
+
export type Lifecycle = MountLifecycle | UnmountLifecycle;
|
|
45
|
+
/** Keyed 要素の情報 */
|
|
46
|
+
export interface KeyedNode {
|
|
47
|
+
key: string | number;
|
|
48
|
+
node: globalThis.Element;
|
|
49
|
+
unmountCallbacks: Array<() => void>;
|
|
50
|
+
}
|
|
51
|
+
/** HTML 要素の装飾 (Attribute, Listener) */
|
|
52
|
+
export type Decoration = Attribute | Listener;
|
|
53
|
+
/** HTML 要素 */
|
|
54
|
+
export type Element = Tagged<"element", {
|
|
55
|
+
tag: string;
|
|
56
|
+
children: Instructor;
|
|
57
|
+
decorations?: Decoration[];
|
|
58
|
+
key?: string | number;
|
|
59
|
+
ns?: string;
|
|
60
|
+
}>;
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ydant/base",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Base DSL primitives and plugin for Ydant",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"dom",
|
|
7
|
+
"elements",
|
|
8
|
+
"plugin",
|
|
9
|
+
"primitives",
|
|
10
|
+
"ydant"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/cwd-k2/ydant#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/cwd-k2/ydant/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "cwd-k2",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/cwd-k2/ydant.git",
|
|
21
|
+
"directory": "packages/base"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"main": "./dist/index.umd.js",
|
|
29
|
+
"module": "./dist/index.es.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"@ydant/dev": {
|
|
35
|
+
"types": "./src/index.ts",
|
|
36
|
+
"default": "./src/index.ts"
|
|
37
|
+
},
|
|
38
|
+
"import": "./dist/index.es.js",
|
|
39
|
+
"require": "./dist/index.umd.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@ydant/core": "0.1.0"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "vite build",
|
|
47
|
+
"typecheck": "tsc --noEmit"
|
|
48
|
+
}
|
|
49
|
+
}
|