@jay-framework/compiler-jay-html 0.5.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/dist/index.d.ts +96 -0
- package/dist/index.js +6464 -0
- package/docs/compiler-patterns.md +145 -0
- package/docs/contract-file-format.md +297 -0
- package/docs/jay-html-docs.md +178 -0
- package/package.json +62 -0
- package/readme.md +115 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Jay Compiler Patterns
|
|
2
|
+
|
|
3
|
+
Jay Compiler Patterns are used by jay security module for detecting safe code patterns that can be extracted from
|
|
4
|
+
a component file and run in the main context, while the rest of the component runs in a sandbox.
|
|
5
|
+
|
|
6
|
+
A simple example is the following event handler:
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
refs.cycles.oninput(({ event }: JayEvent<Event, MainViewState>) => {
|
|
10
|
+
const cycles = (event.target as HTMLInputElement).value;
|
|
11
|
+
setCycles(Number(cycles));
|
|
12
|
+
});
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
As the event handler can run in a sandbox environment that does not have access to the browser native event,
|
|
16
|
+
the code above will break unless is running in the main context `(event.target as HTMLInputElement).value`.
|
|
17
|
+
|
|
18
|
+
Given the compiler pattern
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { JayEvent } from '@jay-framework/runtime';
|
|
22
|
+
function inputValuePattern({ event }: JayEvent<any, any>) {
|
|
23
|
+
return event.target.value;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The event handler is rewritten into
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
refs.cycles.oninput$(handler$('1')).then(({ event }: JayEvent<any, MainViewState>) => {
|
|
31
|
+
setCycles(Number(event.$0));
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
while the main context gains
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
const funcRepository: FunctionsRepository = {
|
|
39
|
+
'1': ({ event }: JayEvent<any, any>) => {
|
|
40
|
+
const cycles = (event.target as HTMLInputElement).value;
|
|
41
|
+
return { $0: event.target.value };
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The full example can be found at [mini-benchmark](../../../../examples/jay/mini-benchmark)
|
|
47
|
+
|
|
48
|
+
## Writing compiler patterns
|
|
49
|
+
|
|
50
|
+
Jay compiler patterns are written as TS functions. However, those functions are not run directly, instead, they are
|
|
51
|
+
compiled into AST patterns that are matched against AST of event handlers and `exec$` APIs.
|
|
52
|
+
|
|
53
|
+
Pattern matching is checking
|
|
54
|
+
|
|
55
|
+
1. the access path used for values
|
|
56
|
+
2. the input parameter types
|
|
57
|
+
3. the output type
|
|
58
|
+
4. Compiler patterns are designed to be simple, one liner expressions.
|
|
59
|
+
5. A function can contain multiple lines, at which each line is considered a different expression.
|
|
60
|
+
6. the `@JayPattern` can be used to force a pattern to only be valid in a certain environment.
|
|
61
|
+
|
|
62
|
+
Jay supports a number of types of patterns:
|
|
63
|
+
|
|
64
|
+
## return patterns
|
|
65
|
+
|
|
66
|
+
A return pattern is a pattern that returns a value, which can be used by the component code (as in the example above).
|
|
67
|
+
|
|
68
|
+
A return pattern is written as a function that returns a value.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { JayEvent } from '@jay-framework/runtime';
|
|
72
|
+
|
|
73
|
+
function inputValuePattern(handler: JayEvent<any, any>): string {
|
|
74
|
+
return handler.event.target.value;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- A return pattern must have a type
|
|
79
|
+
- A pattern must have parameter types
|
|
80
|
+
|
|
81
|
+
> Note: it is not important if we write the pattern using `handler.event.target.value` or
|
|
82
|
+
> deconstruct the handler with `{ event }` and write the pattern as `event.target.value` - the AST matching is the same.
|
|
83
|
+
|
|
84
|
+
## call patterns
|
|
85
|
+
|
|
86
|
+
A call pattern is a pattern that matches a call to a function.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import {JayEvent} from '@jay-framework/runtime';
|
|
90
|
+
|
|
91
|
+
@JayPattern(JayTargetEnv.main)
|
|
92
|
+
function eventPreventDefault(handler: JayEvent<any, any>) {
|
|
93
|
+
handler.event.preventDefault();
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The pattern above allows the call of the native `event.preventDefault()` in the main context.
|
|
98
|
+
|
|
99
|
+
## chainable call patterns
|
|
100
|
+
|
|
101
|
+
The combination of a call pattern and a return pattern, allowing to match a sequence of return and call functions.
|
|
102
|
+
|
|
103
|
+
An example is the string replace function which can run in any environment.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
@JayPattern(JayTargetEnv.any)
|
|
107
|
+
function stringReplace(value: string, regex: RegExp, replacement: string): string {
|
|
108
|
+
return value.replace(regex, replacement)
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> Note: the matching is only on the function body `value.replace(regex, replacement)`. The parameters and return
|
|
113
|
+
> are just indications of types and inputs / outputs.
|
|
114
|
+
|
|
115
|
+
## assignment pattern
|
|
116
|
+
|
|
117
|
+
Assignment pattern allows to support assignment of values directly in the main context.
|
|
118
|
+
|
|
119
|
+
An example is the assignment of a string value to an input.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import {JayEvent} from '@jay-framework/runtime';
|
|
123
|
+
|
|
124
|
+
@JayPattern(JayTargetEnv.main)
|
|
125
|
+
function setInputValue(handler: JayEvent<any, any>, value: string) {
|
|
126
|
+
handler.event.target.value = value;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
In most cases, the same can be achieved using state management. However, consider the following event handler
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
refs.input.onClick(({ event }) => {
|
|
134
|
+
const inputValue = event.target.value;
|
|
135
|
+
const validValue = inputValue.replace(/[^A-Za-z0-9]+/g, '');
|
|
136
|
+
event.target.value = validValue;
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The combination of assignment, read and chainable call patterns allows to push all of the event handler to the main
|
|
141
|
+
context for sync input validation.
|
|
142
|
+
|
|
143
|
+
## constants
|
|
144
|
+
|
|
145
|
+
Constants are considered "safe" be definition and if used in patterns are also extracted to the main context.
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# Contract File Format
|
|
2
|
+
|
|
3
|
+
Contract files (`.jay-contract`) define the interface between headless components and their UI implementations. They specify the data structure, interactive elements, and component composition using a YAML-based format.
|
|
4
|
+
|
|
5
|
+
## Basic Structure
|
|
6
|
+
|
|
7
|
+
A contract file consists of a `name` and a list of `tags`:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
name: component-name
|
|
11
|
+
tags:
|
|
12
|
+
- tag: tagName
|
|
13
|
+
type: tagType
|
|
14
|
+
# additional properties based on type
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Tag Types
|
|
18
|
+
|
|
19
|
+
### Data Tags
|
|
20
|
+
|
|
21
|
+
Data tags define the component's view state properties.
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
- tag: count
|
|
25
|
+
type: data
|
|
26
|
+
dataType: number
|
|
27
|
+
required: true
|
|
28
|
+
description: The current count value
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Properties:**
|
|
32
|
+
|
|
33
|
+
- `type: data` - Identifies this as a data property
|
|
34
|
+
- `dataType` - The data type (string, number, boolean, enum)
|
|
35
|
+
- `required` - (Optional) Whether this property is required
|
|
36
|
+
- `description` - (Optional) Documentation for the property
|
|
37
|
+
|
|
38
|
+
### Interactive Tags
|
|
39
|
+
|
|
40
|
+
Interactive tags define elements that can be interacted with programmatically.
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
- tag: submitButton
|
|
44
|
+
type: interactive
|
|
45
|
+
elementType: HTMLButtonElement
|
|
46
|
+
description: Button to submit the form
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Properties:**
|
|
50
|
+
|
|
51
|
+
- `type: interactive` - Identifies this as an interactive element
|
|
52
|
+
- `elementType` - The HTML element type (required)
|
|
53
|
+
- `description` - (Optional) Documentation for the element
|
|
54
|
+
|
|
55
|
+
### Variant Tags
|
|
56
|
+
|
|
57
|
+
Variant tags define design variations or states.
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
- tag: status
|
|
61
|
+
type: variant
|
|
62
|
+
dataType: enum (active | inactive | pending)
|
|
63
|
+
description: The current status of the component
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Properties:**
|
|
67
|
+
|
|
68
|
+
- `type: variant` - Identifies this as a variant
|
|
69
|
+
- `dataType` - Must be an enum type (required)
|
|
70
|
+
- `description` - (Optional) Documentation for the variant
|
|
71
|
+
|
|
72
|
+
### Sub-Contract Tags
|
|
73
|
+
|
|
74
|
+
Sub-contract tags define nested component structures.
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
- tag: items
|
|
78
|
+
type: sub-contract
|
|
79
|
+
repeated: true
|
|
80
|
+
tags:
|
|
81
|
+
- tag: title
|
|
82
|
+
type: data
|
|
83
|
+
dataType: string
|
|
84
|
+
- tag: completed
|
|
85
|
+
type: data
|
|
86
|
+
dataType: boolean
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Properties:**
|
|
90
|
+
|
|
91
|
+
- `type: sub-contract` - Identifies this as a nested contract
|
|
92
|
+
- `repeated` - (Optional) Whether this represents an array of items
|
|
93
|
+
- `tags` - Nested tag definitions
|
|
94
|
+
- `link` - (Alternative to tags) Reference to another contract file
|
|
95
|
+
|
|
96
|
+
## Linked Contracts
|
|
97
|
+
|
|
98
|
+
Linked contracts allow you to reference external contract files, enabling component reuse and modular design. Instead of defining nested tags inline, you can link to a separate contract file.
|
|
99
|
+
|
|
100
|
+
### Basic Linked Contract
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
- tag: discount
|
|
104
|
+
type: sub-contract
|
|
105
|
+
link: ./discount
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This references a `discount.jay-contract` file in the same directory.
|
|
109
|
+
|
|
110
|
+
### Linked Contract with Repeated Items
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
- tag: media
|
|
114
|
+
type: sub-contract
|
|
115
|
+
tags:
|
|
116
|
+
- tag: items
|
|
117
|
+
type: sub-contract
|
|
118
|
+
link: ./media-item
|
|
119
|
+
repeated: true
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This creates an array of items, each following the structure defined in `media-item.jay-contract`.
|
|
123
|
+
|
|
124
|
+
### Benefits of Linked Contracts
|
|
125
|
+
|
|
126
|
+
1. **Reusability** - Define a contract once and use it across multiple components
|
|
127
|
+
2. **Maintainability** - Changes to the linked contract automatically apply everywhere
|
|
128
|
+
3. **Modularity** - Break complex components into smaller, focused contracts
|
|
129
|
+
4. **Consistency** - Ensure consistent structure across related components
|
|
130
|
+
|
|
131
|
+
### File Resolution
|
|
132
|
+
|
|
133
|
+
- **Relative paths** - `./component` or `../components/button`
|
|
134
|
+
- **Module imports** - `<npm-module-name>/<path-to-contract>`
|
|
135
|
+
- **File extension** - The `.jay-contract` extension is automatically appended
|
|
136
|
+
- **Directory structure** - Contracts can be organized in subdirectories
|
|
137
|
+
|
|
138
|
+
### Example: E-commerce Product Structure
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
name: product-page
|
|
142
|
+
tags:
|
|
143
|
+
- tag: name
|
|
144
|
+
type: data
|
|
145
|
+
dataType: string
|
|
146
|
+
- tag: price
|
|
147
|
+
type: data
|
|
148
|
+
dataType: number
|
|
149
|
+
- tag: discount
|
|
150
|
+
type: sub-contract
|
|
151
|
+
link: ./discount
|
|
152
|
+
- tag: media
|
|
153
|
+
type: sub-contract
|
|
154
|
+
tags:
|
|
155
|
+
- tag: items
|
|
156
|
+
type: sub-contract
|
|
157
|
+
link: ./media-item
|
|
158
|
+
repeated: true
|
|
159
|
+
- tag: mainMedia
|
|
160
|
+
type: sub-contract
|
|
161
|
+
link: ./media-item
|
|
162
|
+
- tag: addToCart
|
|
163
|
+
type: interactive
|
|
164
|
+
elementType: HTMLButtonElement
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
In this example:
|
|
168
|
+
|
|
169
|
+
- `discount` links to a reusable discount contract
|
|
170
|
+
- `media.items` creates an array of media items using the linked contract
|
|
171
|
+
- `media.mainMedia` uses the same contract for a single item
|
|
172
|
+
|
|
173
|
+
### Best Practices for Linked Contracts
|
|
174
|
+
|
|
175
|
+
1. **Use descriptive names** for contract files that indicate their purpose
|
|
176
|
+
2. **Keep contracts focused** - Each linked contract should represent a single concept
|
|
177
|
+
3. **Organize by feature** - Group related contracts in feature-specific directories
|
|
178
|
+
4. **Document dependencies** - Make it clear which contracts are required
|
|
179
|
+
5. **Version contracts** - Consider versioning for breaking changes
|
|
180
|
+
|
|
181
|
+
## Data Types
|
|
182
|
+
|
|
183
|
+
### Basic Types
|
|
184
|
+
|
|
185
|
+
| Type | Example | Description |
|
|
186
|
+
| --------- | ------------------- | ----------------- |
|
|
187
|
+
| `string` | `dataType: string` | Text values |
|
|
188
|
+
| `number` | `dataType: number` | Numeric values |
|
|
189
|
+
| `boolean` | `dataType: boolean` | True/false values |
|
|
190
|
+
|
|
191
|
+
### Enum Types
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
dataType: enum (option1 | option2 | option3)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Enums define a set of allowed values separated by `|`.
|
|
198
|
+
|
|
199
|
+
## Examples
|
|
200
|
+
|
|
201
|
+
### Simple Counter Component
|
|
202
|
+
|
|
203
|
+
```yaml
|
|
204
|
+
name: counter
|
|
205
|
+
tags:
|
|
206
|
+
- tag: count
|
|
207
|
+
type: data
|
|
208
|
+
dataType: number
|
|
209
|
+
required: true
|
|
210
|
+
- tag: add
|
|
211
|
+
type: interactive
|
|
212
|
+
elementType: HTMLButtonElement
|
|
213
|
+
- tag: subtract
|
|
214
|
+
type: interactive
|
|
215
|
+
elementType: HTMLButtonElement
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Form Component with Nested Structure
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
name: userForm
|
|
222
|
+
tags:
|
|
223
|
+
- tag: submitButton
|
|
224
|
+
type: interactive
|
|
225
|
+
elementType: HTMLButtonElement
|
|
226
|
+
- tag: personalInfo
|
|
227
|
+
type: sub-contract
|
|
228
|
+
tags:
|
|
229
|
+
- tag: sectionTitle
|
|
230
|
+
type: data
|
|
231
|
+
dataType: string
|
|
232
|
+
- tag: nameFields
|
|
233
|
+
type: sub-contract
|
|
234
|
+
tags:
|
|
235
|
+
- tag: firstName
|
|
236
|
+
type: [data, interactive]
|
|
237
|
+
dataType: string
|
|
238
|
+
elementType: HTMLInputElement
|
|
239
|
+
- tag: lastName
|
|
240
|
+
type: [data, interactive]
|
|
241
|
+
dataType: string
|
|
242
|
+
elementType: HTMLInputElement
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Product Component with External References
|
|
246
|
+
|
|
247
|
+
```yaml
|
|
248
|
+
name: product-page
|
|
249
|
+
tags:
|
|
250
|
+
- tag: name
|
|
251
|
+
type: data
|
|
252
|
+
dataType: string
|
|
253
|
+
- tag: price
|
|
254
|
+
type: data
|
|
255
|
+
dataType: number
|
|
256
|
+
- tag: discount
|
|
257
|
+
type: sub-contract
|
|
258
|
+
link: ./discount
|
|
259
|
+
- tag: media
|
|
260
|
+
type: sub-contract
|
|
261
|
+
tags:
|
|
262
|
+
- tag: items
|
|
263
|
+
type: sub-contract
|
|
264
|
+
link: ./media-item
|
|
265
|
+
repeated: true
|
|
266
|
+
- tag: addToCart
|
|
267
|
+
type: interactive
|
|
268
|
+
elementType: HTMLButtonElement
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Validation Rules
|
|
272
|
+
|
|
273
|
+
### Required Properties
|
|
274
|
+
|
|
275
|
+
- **Variant tags** must have a `dataType` (enum)
|
|
276
|
+
- **Interactive tags** must have an `elementType`
|
|
277
|
+
- **Sub-contract tags** must have either `tags` or `link`
|
|
278
|
+
|
|
279
|
+
### Type Restrictions
|
|
280
|
+
|
|
281
|
+
- **Sub-contract tags** cannot be combined with other types
|
|
282
|
+
- **Sub-contract tags** cannot have `dataType` or `elementType`
|
|
283
|
+
- **Unknown tag types** will cause validation errors
|
|
284
|
+
|
|
285
|
+
### Defaults
|
|
286
|
+
|
|
287
|
+
- If `type` is not specified, defaults to `data`
|
|
288
|
+
- If `dataType` is not specified for data tags, defaults to `string`
|
|
289
|
+
|
|
290
|
+
## Best Practices
|
|
291
|
+
|
|
292
|
+
1. **Use descriptive names** for tags that clearly indicate their purpose
|
|
293
|
+
2. **Add descriptions** to document complex or non-obvious tags
|
|
294
|
+
3. **Use enums** for variant tags to ensure type safety
|
|
295
|
+
4. **Structure nested contracts** logically to match your component hierarchy
|
|
296
|
+
5. **Reference external contracts** for reusable sub-components
|
|
297
|
+
6. **Mark required properties** explicitly for better validation
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Jay-HTML Syntax Reference
|
|
2
|
+
|
|
3
|
+
Jay-HTML extends standard HTML with several features for component-based development. This document covers the syntax elements that distinguish Jay-HTML from regular HTML.
|
|
4
|
+
|
|
5
|
+
A Jay-HTML file must contain one `application/jay-data` script and can include several unique Jay directives.
|
|
6
|
+
|
|
7
|
+
## Data Contract Definition
|
|
8
|
+
|
|
9
|
+
The data contract defines the `ViewState` - the input data structure for the component.
|
|
10
|
+
The ViewState is defined as a YAML script with `data:` as the root element.
|
|
11
|
+
Each property in the YAML becomes a property of the component's view state, supporting nested objects and arrays.
|
|
12
|
+
|
|
13
|
+
### Supported Data Types
|
|
14
|
+
|
|
15
|
+
| Type | Example | Description |
|
|
16
|
+
| --------------- | ---------------------------------------------------------------------------- | ------------------------------------ |
|
|
17
|
+
| `string` | `text: string` | Text values |
|
|
18
|
+
| `number` | `count: number` | Numeric values |
|
|
19
|
+
| `boolean` | `isVisible: boolean` | True/false values |
|
|
20
|
+
| `object` | <code>user: </br> name: string</br> age: number</code> | Nested object structures |
|
|
21
|
+
| `array` | <code>items: </br>- name: string</br> value: number</code> | Collections of items |
|
|
22
|
+
| `enum` | `status: enum(active \| inactive \| pending)` | Enumerated values |
|
|
23
|
+
| `imported type` | `config: ImportedConfig` | Types imported from other components |
|
|
24
|
+
|
|
25
|
+
### View State Context
|
|
26
|
+
|
|
27
|
+
Jay-HTML treats the view state as the **current context** that gets bound to components and elements. The `forEach` directive changes this context from an object to individual array items.
|
|
28
|
+
|
|
29
|
+
#### Example: Context Switching with forEach
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
data:
|
|
33
|
+
users:
|
|
34
|
+
- id: string
|
|
35
|
+
- name: string
|
|
36
|
+
- email: string
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<!-- Current context: users array -->
|
|
41
|
+
<div forEach="users" trackBy="id">
|
|
42
|
+
<!-- Current context: individual user object -->
|
|
43
|
+
<span>{name}</span>
|
|
44
|
+
<span>{email}</span>
|
|
45
|
+
</div>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
In this example, the context switches from the `users` array to individual `user` objects within the forEach loop.
|
|
49
|
+
|
|
50
|
+
## Component Usage
|
|
51
|
+
|
|
52
|
+
Jay-HTML allows you to use components as part of the HTML structure. Components must be imported before they can be used.
|
|
53
|
+
|
|
54
|
+
### Importing Components
|
|
55
|
+
|
|
56
|
+
```html
|
|
57
|
+
<script
|
|
58
|
+
type="application/jay-headfull"
|
|
59
|
+
src="./my-component.ts"
|
|
60
|
+
names="MyComponent"
|
|
61
|
+
sandbox="false"
|
|
62
|
+
></script>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Using Components
|
|
66
|
+
|
|
67
|
+
```html
|
|
68
|
+
<MyComponent title="Welcome" count="{itemCount}"></MyComponent>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Property Binding Options
|
|
72
|
+
|
|
73
|
+
Components accept properties in three ways:
|
|
74
|
+
|
|
75
|
+
1. **Static values**: `title="Welcome"`
|
|
76
|
+
2. **View state bindings**: `count="{itemCount}"` - binds to a specific property
|
|
77
|
+
3. **Context binding**: `data="{.}"` - binds the entire current context
|
|
78
|
+
|
|
79
|
+
> **Note**: Jay currently doesn't support passing children as properties to components. This feature is planned for future releases.
|
|
80
|
+
|
|
81
|
+
## Element References
|
|
82
|
+
|
|
83
|
+
The `ref` attribute creates references to HTML elements or components that your component code can interact with.
|
|
84
|
+
|
|
85
|
+
### Creating References
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<div ref="mainContainer">{content}</div>
|
|
89
|
+
<Counter ref="counterComponent" initialValue="{startCount}" />
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
For each element with a `ref` attribute, Jay generates a corresponding member in the component's refs type, enabling programmatic interaction.
|
|
93
|
+
|
|
94
|
+
Reference types are defined in the `@jay-framework/runtime` library - see [refs.md](../../../runtime/runtime/docs/refs.md) for details.
|
|
95
|
+
|
|
96
|
+
## Data Binding
|
|
97
|
+
|
|
98
|
+
The `{}` syntax enables dynamic data binding to the current view state context. This syntax supports property access, conditional expressions, and simple operations.
|
|
99
|
+
|
|
100
|
+
### Basic Binding
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<div>{title}</div>
|
|
104
|
+
<div>Hello, {user.name}!</div>
|
|
105
|
+
<div>Count: {items.length}</div>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Conditional Expressions
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<div>{isVisible ? 'Visible' : 'Hidden'}</div>
|
|
112
|
+
<div>{hasName ? name : 'Anonymous'}</div>
|
|
113
|
+
<div>{!isLoading ? 'Ready' : 'Loading...'}</div>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Enum Comparisons
|
|
117
|
+
|
|
118
|
+
```html
|
|
119
|
+
<div>{status === 'active' ? 'Online' : 'Offline'}</div>
|
|
120
|
+
<div>{role !== 'admin' ? 'User' : 'Administrator'}</div>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Class Binding
|
|
124
|
+
|
|
125
|
+
Class bindings support dynamic and conditional class inclusion:
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<div class="{status}">Status indicator</div>
|
|
129
|
+
<div class="{isActive ? 'active' : 'inactive'}">Toggle state</div>
|
|
130
|
+
<div class="{isPrimary ? 'primary' : 'secondary'} button">Button</div>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Conditional Rendering
|
|
134
|
+
|
|
135
|
+
The `if` directive conditionally renders elements based on expressions.
|
|
136
|
+
|
|
137
|
+
### Basic Conditional Rendering
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<div if="isVisible">
|
|
141
|
+
<p>This content is only shown when isVisible is true</p>
|
|
142
|
+
</div>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
> **Note**: The `if` directive automatically evaluates expressions, so `{}` binding syntax is not needed.
|
|
146
|
+
|
|
147
|
+
## List Rendering
|
|
148
|
+
|
|
149
|
+
The `forEach` and `trackBy` directives enable rendering lists of items.
|
|
150
|
+
|
|
151
|
+
### Basic List Rendering
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<ul>
|
|
155
|
+
<li forEach="users" trackBy="id">
|
|
156
|
+
<span>{name}</span>
|
|
157
|
+
<span>{email}</span>
|
|
158
|
+
</li>
|
|
159
|
+
</ul>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### How It Works
|
|
163
|
+
|
|
164
|
+
- `forEach="users"` - iterates over the `users` array
|
|
165
|
+
- `trackBy="id"` - uses the `id` property to track items for efficient DOM updates
|
|
166
|
+
- Within the loop, the context becomes individual array items
|
|
167
|
+
|
|
168
|
+
Since Jay uses immutable view state, the `trackBy` attribute is essential for proper item tracking and DOM optimization.
|
|
169
|
+
|
|
170
|
+
### Example with Component
|
|
171
|
+
|
|
172
|
+
```html
|
|
173
|
+
<div forEach="products" trackBy="sku">
|
|
174
|
+
<ProductCard product="{.}" />
|
|
175
|
+
</div>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
In this example, each `ProductCard` component receives the entire product object as its context.
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jay-framework/compiler-jay-html",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"directories": {
|
|
9
|
+
"lib": "lib"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"docs",
|
|
14
|
+
"readme.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "npm run build:pegjs && npm run build:js && npm run build:types",
|
|
18
|
+
"build:watch": "npm run build:js -- --watch & npm run build:types -- --watch",
|
|
19
|
+
"build:pegjs": "pegjs --allowed-start-rules dynamicAttribute,booleanAttribute,dynamicText,dynamicProperty,conditionFunc,accessor,Identifier,classExpression,dynamicComponentProp,importNames,enum,is_enum,reactDynamicText,reactDynamicProperty,reactClassExpression,condition -o lib/expressions/expression-parser.cjs lib/expressions/expression-parser.pegjs",
|
|
20
|
+
"build:js": "vite build",
|
|
21
|
+
"build:types": "tsup lib/index.ts --dts-only --format cjs",
|
|
22
|
+
"build:check-types": "tsc",
|
|
23
|
+
"clean": "rimraf dist",
|
|
24
|
+
"confirm": "npm run clean && npm run build && npm run build:check-types && npm run test",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest"
|
|
27
|
+
},
|
|
28
|
+
"author": "",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@jay-framework/compiler-analyze-exported-types": "workspace:^",
|
|
31
|
+
"@jay-framework/compiler-shared": "workspace:^",
|
|
32
|
+
"@jay-framework/component": "workspace:^",
|
|
33
|
+
"@jay-framework/runtime": "workspace:^",
|
|
34
|
+
"@jay-framework/secure": "workspace:^",
|
|
35
|
+
"@types/js-yaml": "^4.0.9",
|
|
36
|
+
"change-case": "^4.1.2",
|
|
37
|
+
"js-yaml": "^4.1.0",
|
|
38
|
+
"node-html-parser": "^6.1.12",
|
|
39
|
+
"pegjs": "^0.10.0",
|
|
40
|
+
"pluralize": "^8.0.0",
|
|
41
|
+
"style-to-object": "^1.0.8",
|
|
42
|
+
"typescript": "^5.3.3"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@caiogondim/strip-margin": "^1.0.0",
|
|
46
|
+
"@jay-framework/4-react": "workspace:^",
|
|
47
|
+
"@jay-framework/dev-environment": "workspace:^",
|
|
48
|
+
"@testing-library/jest-dom": "^6.2.0",
|
|
49
|
+
"@types/js-beautify": "^1",
|
|
50
|
+
"@types/node": "^20.11.5",
|
|
51
|
+
"@types/react": "^18.2.0",
|
|
52
|
+
"jest-diff": "^29.7.0",
|
|
53
|
+
"jest-matcher-utils": "^29.7.0",
|
|
54
|
+
"js-beautify": "^1.14.11",
|
|
55
|
+
"react": "^18.2.0",
|
|
56
|
+
"react-dom": "^18.2.0",
|
|
57
|
+
"rimraf": "^5.0.5",
|
|
58
|
+
"tsup": "^8.0.1",
|
|
59
|
+
"vite": "^5.0.11",
|
|
60
|
+
"vitest": "^1.2.1"
|
|
61
|
+
}
|
|
62
|
+
}
|