@symbo.ls/mcp 1.0.11 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +1 -1
- package/symbols_mcp/skills/AUDIT.md +148 -174
- package/symbols_mcp/skills/BRAND_IDENTITY.md +75 -0
- package/symbols_mcp/skills/COMPONENTS.md +151 -306
- package/symbols_mcp/skills/COOKBOOK.md +850 -0
- package/symbols_mcp/skills/DEFAULT_COMPONENTS.md +3856 -0
- package/symbols_mcp/skills/DEFAULT_LIBRARY.md +301 -0
- package/symbols_mcp/skills/DESIGN_CRITIQUE.md +70 -59
- package/symbols_mcp/skills/DESIGN_DIRECTION.md +109 -175
- package/symbols_mcp/skills/DESIGN_SYSTEM.md +473 -181
- package/symbols_mcp/skills/DESIGN_SYSTEM_ARCHITECT.md +65 -57
- package/symbols_mcp/skills/DESIGN_TO_CODE.md +83 -64
- package/symbols_mcp/skills/DESIGN_TREND.md +62 -50
- package/symbols_mcp/skills/FIGMA_MATCHING.md +69 -58
- package/symbols_mcp/skills/LEARNINGS.md +374 -0
- package/symbols_mcp/skills/MARKETING_ASSETS.md +71 -59
- package/symbols_mcp/skills/MIGRATION.md +158 -117
- package/symbols_mcp/skills/PATTERNS.md +101 -74
- package/symbols_mcp/skills/PRESENTATION.md +78 -0
- package/symbols_mcp/skills/PROJECT_STRUCTURE.md +114 -116
- package/symbols_mcp/skills/RULES.md +179 -148
- package/symbols_mcp/skills/RUNNING_APPS.md +476 -0
- package/symbols_mcp/skills/SEO-METADATA.md +33 -18
- package/symbols_mcp/skills/SNIPPETS.md +598 -0
- package/symbols_mcp/skills/SSR-BRENDER.md +99 -0
- package/symbols_mcp/skills/SYNTAX.md +356 -298
- package/symbols_mcp/skills/BRAND_INDENTITY.md +0 -69
- package/symbols_mcp/skills/THE_PRESENTATION.md +0 -69
|
@@ -1,84 +1,69 @@
|
|
|
1
|
-
# DOMQL v3 Syntax
|
|
1
|
+
# DOMQL v3 Syntax Reference
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Authoritative reference for generating correct DOMQL v3 code. Every pattern is derived from real working code.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Element Anatomy
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Produce DOMQL elements as plain JS objects. Every key has a specific role:
|
|
10
10
|
|
|
11
11
|
```js
|
|
12
12
|
export const MyCard = {
|
|
13
|
-
// ── Composition ────────────────────────────────────────────────────
|
|
14
|
-
extends: 'Flex', // extend from registered component(s)
|
|
15
|
-
|
|
16
|
-
// ── DOM ────────────────────────────────────────────────────────────
|
|
17
13
|
tag: 'section', // HTML tag (default: div)
|
|
18
14
|
|
|
19
|
-
//
|
|
20
|
-
padding: 'B C',
|
|
15
|
+
// CSS props (top-level, promoted via propertizeElement)
|
|
16
|
+
padding: 'B C',
|
|
21
17
|
gap: 'A',
|
|
22
18
|
flow: 'column',
|
|
23
19
|
theme: 'dialog',
|
|
24
20
|
round: 'C',
|
|
25
21
|
|
|
26
|
-
//
|
|
27
|
-
attr: {
|
|
28
|
-
role: 'region',
|
|
29
|
-
'aria-label': ({ props }) => props.label
|
|
30
|
-
},
|
|
22
|
+
// HTML attributes
|
|
23
|
+
attr: { role: 'region', 'aria-label': ({ props }) => props.label },
|
|
31
24
|
|
|
32
|
-
//
|
|
25
|
+
// State
|
|
33
26
|
state: { open: false },
|
|
34
27
|
|
|
35
|
-
//
|
|
28
|
+
// Events (v3 top-level)
|
|
36
29
|
onClick: (event, el, state) => { state.update({ open: !state.open }) },
|
|
37
30
|
onRender: (el, state) => { console.log('rendered') },
|
|
38
31
|
|
|
39
|
-
//
|
|
40
|
-
Header: {
|
|
41
|
-
|
|
42
|
-
text: ({ props }) => props.title
|
|
43
|
-
},
|
|
44
|
-
Body: {
|
|
45
|
-
html: ({ props }) => props.content
|
|
46
|
-
}
|
|
32
|
+
// Children (PascalCase keys)
|
|
33
|
+
Header: { text: ({ props }) => props.title },
|
|
34
|
+
Body: { html: ({ props }) => props.content }
|
|
47
35
|
}
|
|
48
36
|
```
|
|
49
37
|
|
|
50
38
|
---
|
|
51
39
|
|
|
52
|
-
##
|
|
40
|
+
## Element Lifecycle
|
|
53
41
|
|
|
54
42
|
```
|
|
55
43
|
create(props, parent, key, options)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
44
|
+
|- createElement() creates bare element
|
|
45
|
+
|- applyExtends() merges extends stack (element wins)
|
|
46
|
+
|- propertizeElement() routes onXxx events, promotes CSS props
|
|
47
|
+
|- addMethods() attaches el.lookup / el.update / etc.
|
|
48
|
+
|- initProps() builds props from propsStack
|
|
49
|
+
+- createNode()
|
|
50
|
+
|- throughInitialExec() executes function props
|
|
51
|
+
|- applyEventsOnNode() binds element.on.* to DOM
|
|
52
|
+
+- iterates children -> create() recursively
|
|
65
53
|
```
|
|
66
54
|
|
|
67
|
-
|
|
55
|
+
`propertizeElement` runs BEFORE `addMethods`. Do not rely on prototype methods during propertization.
|
|
68
56
|
|
|
69
|
-
### Propertization and
|
|
57
|
+
### Propertization and Define System
|
|
70
58
|
|
|
71
|
-
`propertizeElement()` classifies keys between
|
|
59
|
+
`propertizeElement()` classifies keys between root and `props`. Keys starting with `$` overlap between css-in-props conditionals (`$isActive`) and define handlers (`$router`, deprecated v2 handlers like `$propsCollection`, `$collection`).
|
|
72
60
|
|
|
73
|
-
|
|
61
|
+
Define handlers (`element.define[key]` or `context.define[key]`) must stay at the element root so `throughInitialDefine` can process them. Propertization checks for define handlers BEFORE applying `CSS_SELECTOR_PREFIXES`:
|
|
74
62
|
|
|
75
63
|
```js
|
|
76
|
-
// Check define handlers first — these stay at root
|
|
77
64
|
const defineValue = this.define?.[key]
|
|
78
65
|
const globalDefineValue = this.context?.define?.[key]
|
|
79
66
|
if (isFunction(defineValue) || isFunction(globalDefineValue)) continue
|
|
80
|
-
|
|
81
|
-
// Only then apply prefix-based classification
|
|
82
67
|
if (CSS_SELECTOR_PREFIXES.has(firstChar)) {
|
|
83
68
|
obj.props[key] = value // move to props for css-in-props
|
|
84
69
|
}
|
|
@@ -86,7 +71,9 @@ if (CSS_SELECTOR_PREFIXES.has(firstChar)) {
|
|
|
86
71
|
|
|
87
72
|
---
|
|
88
73
|
|
|
89
|
-
##
|
|
74
|
+
## REGISTRY Keys
|
|
75
|
+
|
|
76
|
+
These keys are handled by DOMQL internally and are NOT promoted to CSS props:
|
|
90
77
|
|
|
91
78
|
```
|
|
92
79
|
attr, style, text, html, data, classlist, state, scope, deps,
|
|
@@ -96,64 +83,58 @@ props, if, define, __name, __ref, __hash, __text,
|
|
|
96
83
|
key, tag, query, parent, node, variables, on, component, context
|
|
97
84
|
```
|
|
98
85
|
|
|
99
|
-
Any key NOT in this list and not
|
|
86
|
+
Any key NOT in this list and not PascalCase (component) is promoted to `element.props` as a CSS prop.
|
|
100
87
|
|
|
101
|
-
|
|
88
|
+
Always use `childExtends` (plural). The singular `childExtend` is deprecated v2 syntax kept for backwards compatibility.
|
|
102
89
|
|
|
103
90
|
---
|
|
104
91
|
|
|
105
|
-
##
|
|
92
|
+
## Extending and Composing
|
|
106
93
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
94
|
+
| Pattern | Syntax |
|
|
95
|
+
|---|---|
|
|
96
|
+
| Single extend | `extends: 'Button'` |
|
|
97
|
+
| Multiple (first = highest priority) | `extends: [Link, RouterLink]` |
|
|
98
|
+
| String reference (from `context.components`) | `extends: 'Hoverable'` |
|
|
99
|
+
| Multiple strings | `extends: ['IconText', 'FocusableComponent']` |
|
|
111
100
|
|
|
112
|
-
###
|
|
113
|
-
```js
|
|
114
|
-
export const RouteLink = { extends: [Link, RouterLink] }
|
|
115
|
-
export const Button = { extends: ['IconText', 'FocusableComponent'] }
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### String reference (resolved from `context.components`)
|
|
119
|
-
```js
|
|
120
|
-
export const MyItem = { extends: 'Hoverable' }
|
|
121
|
-
```
|
|
101
|
+
### Merge Semantics
|
|
122
102
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
103
|
+
| Type | Rule |
|
|
104
|
+
|---|---|
|
|
105
|
+
| Own properties | Always win over extends |
|
|
106
|
+
| Objects | Deep-merged (both sides preserved) |
|
|
107
|
+
| Functions | NOT merged; element's function replaces extend's |
|
|
108
|
+
| Arrays | Concatenated |
|
|
128
109
|
|
|
129
110
|
---
|
|
130
111
|
|
|
131
|
-
##
|
|
112
|
+
## CSS Props (Top-Level Promotion)
|
|
132
113
|
|
|
133
|
-
|
|
114
|
+
Place CSS props at the element root. Non-registry, non-PascalCase keys become `element.props`:
|
|
134
115
|
|
|
135
116
|
```js
|
|
136
117
|
export const Card = {
|
|
137
|
-
padding: 'B C', //
|
|
138
|
-
gap: 'Z', //
|
|
139
|
-
flow: 'column', //
|
|
140
|
-
align: 'center', //
|
|
141
|
-
fontSize: 'A',
|
|
142
|
-
fontWeight: '500',
|
|
143
|
-
color: 'currentColor',
|
|
144
|
-
background: 'codGray',
|
|
145
|
-
round: 'C', //
|
|
118
|
+
padding: 'B C', // -> props.padding
|
|
119
|
+
gap: 'Z', // -> props.gap
|
|
120
|
+
flow: 'column', // shorthand for flexDirection
|
|
121
|
+
align: 'center', // NOT flexAlign
|
|
122
|
+
fontSize: 'A',
|
|
123
|
+
fontWeight: '500',
|
|
124
|
+
color: 'currentColor',
|
|
125
|
+
background: 'codGray',
|
|
126
|
+
round: 'C', // border-radius token
|
|
146
127
|
opacity: '0.85',
|
|
147
128
|
overflow: 'hidden',
|
|
148
129
|
transition: 'B defaultBezier',
|
|
149
130
|
transitionProperty: 'opacity, transform',
|
|
150
131
|
zIndex: 10,
|
|
151
|
-
tag: 'section', // stays at root (
|
|
152
|
-
attr: { href: ... }
|
|
132
|
+
tag: 'section', // stays at root (REGISTRY)
|
|
133
|
+
attr: { href: '...' } // stays at root (REGISTRY)
|
|
153
134
|
}
|
|
154
135
|
```
|
|
155
136
|
|
|
156
|
-
### Pseudo-
|
|
137
|
+
### Pseudo-Classes and Pseudo-Elements
|
|
157
138
|
|
|
158
139
|
```js
|
|
159
140
|
export const Hoverable = {
|
|
@@ -168,7 +149,7 @@ export const Hoverable = {
|
|
|
168
149
|
}
|
|
169
150
|
```
|
|
170
151
|
|
|
171
|
-
### CSS
|
|
152
|
+
### CSS Class State Modifiers (Emotion `.className`)
|
|
172
153
|
|
|
173
154
|
```js
|
|
174
155
|
export const Item = {
|
|
@@ -179,7 +160,7 @@ export const Item = {
|
|
|
179
160
|
}
|
|
180
161
|
```
|
|
181
162
|
|
|
182
|
-
### Raw
|
|
163
|
+
### Raw Style Object (Escape Hatch)
|
|
183
164
|
|
|
184
165
|
```js
|
|
185
166
|
export const DropdownParent = {
|
|
@@ -192,7 +173,7 @@ export const DropdownParent = {
|
|
|
192
173
|
}
|
|
193
174
|
```
|
|
194
175
|
|
|
195
|
-
### Media
|
|
176
|
+
### Media Queries
|
|
196
177
|
|
|
197
178
|
```js
|
|
198
179
|
export const Grid = {
|
|
@@ -206,32 +187,58 @@ export const Grid = {
|
|
|
206
187
|
|
|
207
188
|
---
|
|
208
189
|
|
|
209
|
-
##
|
|
190
|
+
## Events
|
|
210
191
|
|
|
211
|
-
### v3
|
|
192
|
+
### v3 Syntax (Top-Level `onXxx`)
|
|
193
|
+
|
|
194
|
+
Use top-level `onXxx` handlers. Two signatures exist:
|
|
195
|
+
|
|
196
|
+
**DOM events** -- signature: `(event, el, state)`:
|
|
212
197
|
|
|
213
198
|
```js
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
onFocus: (event, el, state) => { /* ... */ },
|
|
199
|
+
onClick: (event, el, state) => { /* ... */ }
|
|
200
|
+
onChange: (event, el, state) => { /* ... */ }
|
|
201
|
+
onInput: (event, el, state) => { state.update({ value: event.target.value }) }
|
|
202
|
+
onSubmit: (event, el, state) => { event.preventDefault() }
|
|
203
|
+
onKeydown: (event, el, state) => { if (event.key === 'Enter') /* ... */ }
|
|
204
|
+
onMouseover: (event, el, state) => { /* ... */ }
|
|
205
|
+
onBlur: (event, el, state) => { /* ... */ }
|
|
206
|
+
onFocus: (event, el, state) => { /* ... */ }
|
|
207
|
+
```
|
|
224
208
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
209
|
+
**Lifecycle events** -- signature: `(el, state)`:
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
onInit: (el, state) => { /* before render */ }
|
|
213
|
+
onRender: (el, state) => { /* after render */ }
|
|
214
|
+
onCreate: (el, state) => { /* after full creation */ }
|
|
215
|
+
onUpdate: (el, state) => { /* after state/props update */ }
|
|
216
|
+
onStateUpdate: (el, state) => { /* after state update */ }
|
|
232
217
|
```
|
|
233
218
|
|
|
234
|
-
###
|
|
219
|
+
### Element Lifecycle Events (Full Signatures)
|
|
220
|
+
|
|
221
|
+
| Event | Signature | When | Notes |
|
|
222
|
+
|---|---|---|---|
|
|
223
|
+
| `onInit` | `(element, state, context, updateOptions)` | Before init | Return `false` to break |
|
|
224
|
+
| `onAttachNode` | `(element, state, context, updateOptions)` | After DOM node attached | |
|
|
225
|
+
| `onRender` | `(element, state, context, updateOptions)` | After render | |
|
|
226
|
+
| `onComplete` | `(element, state, context, updateOptions)` | After full creation | |
|
|
227
|
+
| `onBeforeUpdate` | `(changes, element, state, context, updateOptions)` | Before update | `changes` is first param |
|
|
228
|
+
| `onUpdate` | `(element, state, context, updateOptions)` | After update | |
|
|
229
|
+
|
|
230
|
+
### State Events
|
|
231
|
+
|
|
232
|
+
| Event | Signature | When | Notes |
|
|
233
|
+
|---|---|---|---|
|
|
234
|
+
| `onStateInit` | `(element, state, context, updateOptions)` | Before state init | Return `false` to break |
|
|
235
|
+
| `onStateCreated` | `(element, state, context, updateOptions)` | After state created | |
|
|
236
|
+
| `onBeforeStateUpdate` | `(changes, element, state, context, updateOptions)` | Before state update | Return `false` to prevent; `changes` first |
|
|
237
|
+
| `onStateUpdate` | `(changes, element, state, context, updateOptions)` | After state update | `changes` is first param |
|
|
238
|
+
|
|
239
|
+
`onBeforeStateUpdate` and `onStateUpdate` receive `changes` as their FIRST parameter.
|
|
240
|
+
|
|
241
|
+
### DOMQL Lifecycle Names (Never Bound to DOM)
|
|
235
242
|
|
|
236
243
|
```
|
|
237
244
|
init, beforeClassAssign, render, renderRouter, attachNode,
|
|
@@ -239,17 +246,18 @@ stateInit, stateCreated, beforeStateUpdate, stateUpdate,
|
|
|
239
246
|
beforeUpdate, done, create, complete, frame, update
|
|
240
247
|
```
|
|
241
248
|
|
|
242
|
-
### Event
|
|
249
|
+
### Event Detection Rule
|
|
250
|
+
|
|
251
|
+
A key is a v3 event handler when:
|
|
243
252
|
|
|
244
253
|
```js
|
|
245
|
-
// A key is a v3 event handler if:
|
|
246
254
|
key.length > 2 &&
|
|
247
255
|
key.startsWith('on') &&
|
|
248
|
-
key[2] === key[2].toUpperCase() && // onClick, onRender
|
|
256
|
+
key[2] === key[2].toUpperCase() && // onClick, onRender -- NOT "one", "only"
|
|
249
257
|
isFunction(value)
|
|
250
258
|
```
|
|
251
259
|
|
|
252
|
-
### Async
|
|
260
|
+
### Async Events
|
|
253
261
|
|
|
254
262
|
```js
|
|
255
263
|
onRender: async (el, state) => {
|
|
@@ -264,29 +272,20 @@ onRender: async (el, state) => {
|
|
|
264
272
|
|
|
265
273
|
---
|
|
266
274
|
|
|
267
|
-
##
|
|
268
|
-
|
|
269
|
-
### Defining state
|
|
270
|
-
|
|
271
|
-
```js
|
|
272
|
-
export const Counter = {
|
|
273
|
-
state: { count: 0, open: false, selected: null },
|
|
274
|
-
}
|
|
275
|
-
```
|
|
275
|
+
## State
|
|
276
276
|
|
|
277
|
-
###
|
|
277
|
+
### Define, Read, Update
|
|
278
278
|
|
|
279
279
|
```js
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
opacity: ({ state }) => state.loading ? 0.5 : 1,
|
|
283
|
-
isActive: ({ key, state }) => state.active === key
|
|
284
|
-
}
|
|
285
|
-
```
|
|
280
|
+
// Define
|
|
281
|
+
state: { count: 0, open: false, selected: null }
|
|
286
282
|
|
|
287
|
-
|
|
283
|
+
// Read in definitions
|
|
284
|
+
text: ({ state }) => state.label
|
|
285
|
+
opacity: ({ state }) => state.loading ? 0.5 : 1
|
|
286
|
+
isActive: ({ key, state }) => state.active === key
|
|
288
287
|
|
|
289
|
-
|
|
288
|
+
// Update from events
|
|
290
289
|
onClick: (event, el, state) => {
|
|
291
290
|
state.update({ on: !state.on }) // partial update
|
|
292
291
|
state.set({ on: false }) // replace
|
|
@@ -295,31 +294,78 @@ onClick: (event, el, state) => {
|
|
|
295
294
|
}
|
|
296
295
|
```
|
|
297
296
|
|
|
298
|
-
###
|
|
297
|
+
### Root State Access
|
|
299
298
|
|
|
300
299
|
```js
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
300
|
+
// From events
|
|
301
|
+
const rootState = el.getRootState()
|
|
302
|
+
const user = el.getRootState('user')
|
|
305
303
|
|
|
306
304
|
// In definitions
|
|
307
305
|
text: (el) => el.getRootState('currentPage')
|
|
308
306
|
```
|
|
309
307
|
|
|
310
|
-
### Targeted
|
|
308
|
+
### Targeted Updates (Performance)
|
|
311
309
|
|
|
312
310
|
```js
|
|
313
|
-
state.root.update({
|
|
314
|
-
activeModal: true
|
|
315
|
-
}, {
|
|
311
|
+
state.root.update({ activeModal: true }, {
|
|
316
312
|
onlyUpdate: 'ModalCard' // only ModalCard subtree re-renders
|
|
317
313
|
})
|
|
318
314
|
```
|
|
319
315
|
|
|
320
316
|
---
|
|
321
317
|
|
|
322
|
-
##
|
|
318
|
+
## State Methods
|
|
319
|
+
|
|
320
|
+
| Method | Description |
|
|
321
|
+
|---|---|
|
|
322
|
+
| `state.update(value, options?)` | Deep overwrite, triggers re-render |
|
|
323
|
+
| `state.set(value, options?)` | Replace state entirely (removes old values) |
|
|
324
|
+
| `state.reset(options?)` | Reset to initial values |
|
|
325
|
+
| `state.add(value, options?)` | Add item to array state |
|
|
326
|
+
| `state.toggle(key, options?)` | Toggle boolean property |
|
|
327
|
+
| `state.remove(key, options?)` | Remove property |
|
|
328
|
+
| `state.apply(fn, options?)` | Apply fn that RETURNS new value |
|
|
329
|
+
| `state.applyFunction(fn, options?)` | Apply fn that MUTATES state directly |
|
|
330
|
+
| `state.replace(value, options?)` | SHALLOW replace (nested keys disappear) |
|
|
331
|
+
| `state.clean(options?)` | Empty the state |
|
|
332
|
+
| `state.parse()` | Get purified plain object |
|
|
333
|
+
| `state.quietUpdate(value)` | Update without triggering re-render |
|
|
334
|
+
| `state.quietReplace(value)` | Replace without triggering re-render |
|
|
335
|
+
| `state.destroy(options?)` | Completely remove state |
|
|
336
|
+
| `state.setByPath('a.b.c', value)` | Update nested by dot-path |
|
|
337
|
+
|
|
338
|
+
`apply()` expects the function to RETURN a new value. `applyFunction()` expects direct MUTATION:
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
state.apply(s => ({ ...s, count: s.count + 1 })) // return
|
|
342
|
+
state.applyFunction(s => { s.count++ }) // mutate
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### State Update Options
|
|
346
|
+
|
|
347
|
+
| Option | Description |
|
|
348
|
+
|---|---|
|
|
349
|
+
| `isHoisted` | Mark update as hoisted |
|
|
350
|
+
| `preventHoistElementUpdate` | Prevent hoisted element from updating |
|
|
351
|
+
|
|
352
|
+
### State Navigation
|
|
353
|
+
|
|
354
|
+
```js
|
|
355
|
+
state.parent // parent element's state
|
|
356
|
+
state.root // application-level root state
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
State as string inherits from parent:
|
|
360
|
+
|
|
361
|
+
```js
|
|
362
|
+
// Parent has state: { userProfile: { name: 'John' } }
|
|
363
|
+
state: 'userProfile' // child inherits parent's userProfile key
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## `attr` (HTML Attributes)
|
|
323
369
|
|
|
324
370
|
```js
|
|
325
371
|
export const Input = {
|
|
@@ -329,7 +375,7 @@ export const Input = {
|
|
|
329
375
|
autocomplete: 'off',
|
|
330
376
|
placeholder: ({ props }) => props.placeholder,
|
|
331
377
|
name: ({ props }) => props.name,
|
|
332
|
-
disabled: ({ props }) => props.disabled || null, // null removes
|
|
378
|
+
disabled: ({ props }) => props.disabled || null, // null removes attr
|
|
333
379
|
value: (el) => el.call('exec', el.props.value, el),
|
|
334
380
|
required: ({ props }) => props.required,
|
|
335
381
|
role: 'button',
|
|
@@ -339,33 +385,32 @@ export const Input = {
|
|
|
339
385
|
}
|
|
340
386
|
```
|
|
341
387
|
|
|
342
|
-
|
|
388
|
+
Return `null` or `undefined` from an attr function to remove the attribute.
|
|
343
389
|
|
|
344
390
|
---
|
|
345
391
|
|
|
346
|
-
##
|
|
392
|
+
## `text` and `html`
|
|
347
393
|
|
|
348
394
|
```js
|
|
349
|
-
|
|
350
|
-
export const
|
|
351
|
-
export const
|
|
352
|
-
export const
|
|
353
|
-
|
|
354
|
-
// Raw HTML (XSS risk — use sparingly)
|
|
355
|
-
export const RichText = { html: ({ props }) => props.html }
|
|
395
|
+
export const Label = { text: ({ props }) => props.label }
|
|
396
|
+
export const Badge = { text: 'New' }
|
|
397
|
+
export const Price = { text: ({ state }) => `$${state.amount.toFixed(2)}` }
|
|
398
|
+
export const RichText = { html: ({ props }) => props.html } // XSS risk
|
|
356
399
|
```
|
|
357
400
|
|
|
358
401
|
---
|
|
359
402
|
|
|
360
|
-
##
|
|
403
|
+
## Children
|
|
361
404
|
|
|
362
|
-
### Named
|
|
405
|
+
### Named Children
|
|
406
|
+
|
|
407
|
+
PascalCase or numeric keys become child elements:
|
|
363
408
|
|
|
364
409
|
```js
|
|
365
410
|
export const Card = {
|
|
366
|
-
|
|
411
|
+
flow: 'y',
|
|
367
412
|
Header: {
|
|
368
|
-
|
|
413
|
+
flow: 'x',
|
|
369
414
|
Title: { text: ({ props }) => props.title },
|
|
370
415
|
},
|
|
371
416
|
Body: { html: ({ props }) => props.content },
|
|
@@ -375,27 +420,23 @@ export const Card = {
|
|
|
375
420
|
}
|
|
376
421
|
```
|
|
377
422
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
### `childExtends` — extend all direct children
|
|
423
|
+
### `childExtends`
|
|
381
424
|
|
|
382
|
-
|
|
425
|
+
Extend all direct children. Use a named component string:
|
|
383
426
|
|
|
384
427
|
```js
|
|
385
|
-
export const NavList = {
|
|
386
|
-
childExtends: 'NavLink' // ✅ named string only
|
|
387
|
-
}
|
|
428
|
+
export const NavList = { childExtends: 'NavLink' }
|
|
388
429
|
```
|
|
389
430
|
|
|
390
|
-
### `
|
|
431
|
+
### `childExtendsRecursive`
|
|
432
|
+
|
|
433
|
+
Apply to ALL descendants:
|
|
391
434
|
|
|
392
435
|
```js
|
|
393
|
-
export const Tree = {
|
|
394
|
-
childExtendRecursive: { fontSize: 'A' }
|
|
395
|
-
}
|
|
436
|
+
export const Tree = { childExtendsRecursive: { fontSize: 'A' } }
|
|
396
437
|
```
|
|
397
438
|
|
|
398
|
-
### `children`
|
|
439
|
+
### `children` (Dynamic Child List)
|
|
399
440
|
|
|
400
441
|
```js
|
|
401
442
|
export const DropdownList = {
|
|
@@ -404,108 +445,100 @@ export const DropdownList = {
|
|
|
404
445
|
}
|
|
405
446
|
```
|
|
406
447
|
|
|
407
|
-
### `childrenAs`
|
|
448
|
+
### `childrenAs`
|
|
408
449
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
```js
|
|
412
|
-
// Default (childrenAs: 'props') — each item becomes element props
|
|
413
|
-
{ children: [{ text: 'Hello' }] }
|
|
414
|
-
// → child gets: { props: { text: 'Hello' } }
|
|
450
|
+
Control how children data maps to elements:
|
|
415
451
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
452
|
+
| Value | Behavior |
|
|
453
|
+
|---|---|
|
|
454
|
+
| `'props'` (default) | Each item becomes child's `props` |
|
|
455
|
+
| `'state'` | Each item becomes child's `state` |
|
|
456
|
+
| `'element'` | Each item is used directly as element definition |
|
|
419
457
|
|
|
420
|
-
|
|
421
|
-
{ children: [{
|
|
422
|
-
|
|
458
|
+
```js
|
|
459
|
+
{ children: [{ text: 'Hello' }] } // -> { props: { text: 'Hello' } }
|
|
460
|
+
{ children: [{ count: 5 }], childrenAs: 'state' } // -> { state: { count: 5 } }
|
|
461
|
+
{ children: [{ tag: 'span', text: 'Hi' }], childrenAs: 'element' } // -> { tag: 'span', text: 'Hi' }
|
|
423
462
|
```
|
|
424
463
|
|
|
425
|
-
### `state: 'key'`
|
|
464
|
+
### `state: 'key'` (Narrow State Scope)
|
|
426
465
|
|
|
427
|
-
|
|
466
|
+
Narrow parent state for children:
|
|
428
467
|
|
|
429
468
|
```js
|
|
430
|
-
// Parent narrows state to the 'members' key
|
|
431
469
|
export const TeamList = {
|
|
432
470
|
state: 'members',
|
|
433
471
|
childExtends: 'TeamItem',
|
|
434
472
|
children: ({ state }) => state
|
|
435
473
|
}
|
|
436
474
|
|
|
437
|
-
// Child reads individual item state — requires state: true
|
|
438
475
|
export const TeamItem = {
|
|
439
|
-
state: true,
|
|
476
|
+
state: true, // REQUIRED for children to receive individual state
|
|
440
477
|
Title: { text: ({ state }) => state.name }
|
|
441
478
|
}
|
|
442
479
|
```
|
|
443
480
|
|
|
444
|
-
|
|
481
|
+
`state: true` is required on child components reading `({ state }) => state.field` when used with `childExtends`.
|
|
445
482
|
|
|
446
|
-
### `content`
|
|
483
|
+
### `content` (Single Dynamic Child)
|
|
447
484
|
|
|
448
485
|
```js
|
|
449
|
-
export const Page = {
|
|
450
|
-
content: ({ props }) => props.page
|
|
451
|
-
}
|
|
486
|
+
export const Page = { content: ({ props }) => props.page }
|
|
452
487
|
```
|
|
453
488
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
## 11. Props
|
|
457
|
-
|
|
458
|
-
### Passing props (consumer side)
|
|
489
|
+
### Children as Async
|
|
459
490
|
|
|
460
491
|
```js
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
text: 'Submit',
|
|
465
|
-
href: '/dashboard',
|
|
466
|
-
disabled: false
|
|
467
|
-
}
|
|
492
|
+
{
|
|
493
|
+
children: async (element, state, context) => await window.fetch('...endpoint'),
|
|
494
|
+
childrenAs: 'state',
|
|
468
495
|
}
|
|
469
496
|
```
|
|
470
497
|
|
|
471
|
-
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## Props
|
|
501
|
+
|
|
502
|
+
### Pass and Access
|
|
472
503
|
|
|
473
504
|
```js
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
},
|
|
480
|
-
|
|
505
|
+
// Pass (consumer side)
|
|
506
|
+
{ extends: 'Button', props: { text: 'Submit', href: '/dashboard', disabled: false } }
|
|
507
|
+
|
|
508
|
+
// Access (definition side)
|
|
509
|
+
attr: {
|
|
510
|
+
placeholder: ({ props }) => props.placeholder,
|
|
511
|
+
value: (el) => el.props.value,
|
|
512
|
+
disabled: ({ props }) => props.disabled || null
|
|
481
513
|
}
|
|
514
|
+
text: ({ props }) => props.label
|
|
482
515
|
```
|
|
483
516
|
|
|
484
|
-
### Boolean
|
|
517
|
+
### Boolean/Computed Props
|
|
518
|
+
|
|
519
|
+
`is*`, `has*`, `use*` prefixes are treated as boolean flags:
|
|
485
520
|
|
|
486
521
|
```js
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
useCache: true
|
|
491
|
-
}
|
|
522
|
+
isActive: ({ key, state }) => state.active === key
|
|
523
|
+
hasIcon: ({ props }) => Boolean(props.icon)
|
|
524
|
+
useCache: true
|
|
492
525
|
```
|
|
493
526
|
|
|
494
|
-
|
|
527
|
+
### `childProps`
|
|
495
528
|
|
|
496
|
-
|
|
529
|
+
Inject props into all named children:
|
|
497
530
|
|
|
498
531
|
```js
|
|
499
532
|
export const Layout = {
|
|
500
533
|
childProps: {
|
|
501
|
-
onClick: (ev) => { ev.stopPropagation() }
|
|
534
|
+
onClick: (ev) => { ev.stopPropagation() }
|
|
502
535
|
}
|
|
503
536
|
}
|
|
504
537
|
```
|
|
505
538
|
|
|
506
539
|
---
|
|
507
540
|
|
|
508
|
-
##
|
|
541
|
+
## `define` (Custom Property Transformers)
|
|
509
542
|
|
|
510
543
|
```js
|
|
511
544
|
define: {
|
|
@@ -516,11 +549,13 @@ define: {
|
|
|
516
549
|
}
|
|
517
550
|
```
|
|
518
551
|
|
|
519
|
-
### Built-
|
|
520
|
-
|
|
521
|
-
These are registered globally and work on any element:
|
|
552
|
+
### Built-In Defines
|
|
522
553
|
|
|
523
|
-
|
|
554
|
+
| Define | Purpose |
|
|
555
|
+
|---|---|
|
|
556
|
+
| `metadata` | SEO metadata (see SEO-METADATA.md) |
|
|
557
|
+
| `routes` | Route definitions for the router |
|
|
558
|
+
| `$router` | Render route content into the element |
|
|
524
559
|
|
|
525
560
|
```js
|
|
526
561
|
export const aboutPage = {
|
|
@@ -532,17 +567,14 @@ export const aboutPage = {
|
|
|
532
567
|
}
|
|
533
568
|
```
|
|
534
569
|
|
|
535
|
-
- `routes` — route definitions for the router
|
|
536
|
-
- `$router` — renders route content into the element
|
|
537
|
-
|
|
538
570
|
---
|
|
539
571
|
|
|
540
|
-
##
|
|
572
|
+
## `if` (Conditional Rendering)
|
|
541
573
|
|
|
542
574
|
```js
|
|
543
575
|
export const AuthView = {
|
|
544
576
|
if: (el, state) => state.isAuthenticated,
|
|
545
|
-
Dashboard: { /*
|
|
577
|
+
Dashboard: { /* renders only when true */ }
|
|
546
578
|
}
|
|
547
579
|
|
|
548
580
|
export const ErrorMsg = {
|
|
@@ -553,16 +585,16 @@ export const ErrorMsg = {
|
|
|
553
585
|
|
|
554
586
|
---
|
|
555
587
|
|
|
556
|
-
##
|
|
588
|
+
## `scope` and `data`
|
|
557
589
|
|
|
558
590
|
```js
|
|
559
|
-
// scope: 'state'
|
|
591
|
+
// scope: 'state' -- element.scope becomes element.state
|
|
560
592
|
export const Form = {
|
|
561
593
|
scope: 'state',
|
|
562
594
|
state: { name: '', email: '' }
|
|
563
595
|
}
|
|
564
596
|
|
|
565
|
-
// data
|
|
597
|
+
// data -- non-reactive storage (no re-renders)
|
|
566
598
|
export const Chart = {
|
|
567
599
|
data: { chartInstance: null },
|
|
568
600
|
onRender: (el) => {
|
|
@@ -573,78 +605,67 @@ export const Chart = {
|
|
|
573
605
|
|
|
574
606
|
---
|
|
575
607
|
|
|
576
|
-
##
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
el.lookup('key')
|
|
581
|
-
el.lookdown('key')
|
|
582
|
-
el.lookdownAll('key')
|
|
583
|
-
el.
|
|
584
|
-
el.
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
el.update({ key: value })
|
|
588
|
-
el.set({ key: value })
|
|
589
|
-
el.setProps({ key: value })
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
el.
|
|
593
|
-
el.
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
el.
|
|
597
|
-
el.
|
|
598
|
-
el.
|
|
599
|
-
el.
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
el.
|
|
603
|
-
el.
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
608
|
+
## Element Methods
|
|
609
|
+
|
|
610
|
+
| Category | Method | Description |
|
|
611
|
+
|---|---|---|
|
|
612
|
+
| **Navigation** | `el.lookup('key')` | Find ancestor by key or predicate |
|
|
613
|
+
| | `el.lookdown('key')` | Find first descendant by key |
|
|
614
|
+
| | `el.lookdownAll('key')` | Find all descendants by key |
|
|
615
|
+
| | `el.spotByPath(['Header', 'Nav', 'Logo'])` | Find by array path |
|
|
616
|
+
| | `el.nextElement()` | Next sibling |
|
|
617
|
+
| | `el.previousElement()` | Previous sibling |
|
|
618
|
+
| | `el.getRoot()` | Root element |
|
|
619
|
+
| **Updates** | `el.update({ key: value })` | Deep overwrite element properties |
|
|
620
|
+
| | `el.set({ key: value })` | Set content element |
|
|
621
|
+
| | `el.setProps({ key: value })` | Update props specifically |
|
|
622
|
+
| **Content** | `el.updateContent(newContent)` | Update content |
|
|
623
|
+
| | `el.removeContent()` | Remove content |
|
|
624
|
+
| **State** | `el.getRootState()` | App-level root state |
|
|
625
|
+
| | `el.getRootState('key')` | Specific key from root state |
|
|
626
|
+
| | `el.getContext('key')` | Value from element's context |
|
|
627
|
+
| **DOM** | `el.setNodeStyles({ key: value })` | Apply inline styles |
|
|
628
|
+
| | `el.remove()` | Remove from tree and DOM |
|
|
629
|
+
| **Context** | `el.call('fnKey', ...args)` | Lookup: `context.utils -> functions -> methods -> snippets` |
|
|
630
|
+
| **Router** | `el.router(path, root)` | SPA navigation |
|
|
631
|
+
| **Dependencies** | `el.require('moduleName')` | Cross-environment dependency loading |
|
|
632
|
+
| **Debug** | `el.parse(exclude)` | One-level purified plain object |
|
|
633
|
+
| | `el.parseDeep(exclude)` | Deep purified plain object |
|
|
634
|
+
| | `el.keys()` | List element's own keys |
|
|
635
|
+
| | `el.verbose()` | Log element in console |
|
|
636
|
+
|
|
637
|
+
### Element Update Options
|
|
638
|
+
|
|
639
|
+
Pass as second argument to `el.update(value, options)`:
|
|
640
|
+
|
|
641
|
+
| Option | Description |
|
|
642
|
+
|---|---|
|
|
643
|
+
| `onlyUpdate` | Only update specific subtree by key |
|
|
644
|
+
| `preventUpdate` | Prevent element update |
|
|
645
|
+
| `preventStateUpdate` | Prevent state update |
|
|
646
|
+
| `preventUpdateListener` | Skip update event listeners |
|
|
647
|
+
| `preventUpdateAfter` | Skip post-update hooks |
|
|
648
|
+
| `lazyLoad` | Enable lazy loading for the update |
|
|
613
649
|
|
|
614
650
|
---
|
|
615
651
|
|
|
616
|
-
##
|
|
617
|
-
|
|
618
|
-
```js
|
|
619
|
-
state.update({ key: value }) // partial update, triggers re-render
|
|
620
|
-
state.set({ key: value }) // replace, triggers re-render
|
|
621
|
-
state.reset() // reset to initial value
|
|
622
|
-
state.toggle('open') // toggle boolean property
|
|
623
|
-
state.remove() // remove state node
|
|
624
|
-
state.quietUpdate({ key: value }) // update without re-render
|
|
625
|
-
state.add(item) // add to collection
|
|
626
|
-
state.setByPath('a.b.c', value) // update nested by path
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
---
|
|
652
|
+
## `el.call()` (Context Function Lookup)
|
|
630
653
|
|
|
631
|
-
|
|
654
|
+
Lookup order: `context.utils -> context.functions -> context.methods -> context.snippets`
|
|
632
655
|
|
|
633
656
|
```js
|
|
634
657
|
el.call('router', href, root, {}, options)
|
|
635
|
-
el.call('exec', value, el)
|
|
658
|
+
el.call('exec', value, el)
|
|
636
659
|
el.call('isString', value)
|
|
637
660
|
el.call('fetchData', id)
|
|
638
661
|
el.call('replaceLiteralsWithObjectFields', template)
|
|
639
662
|
```
|
|
640
663
|
|
|
641
|
-
Lookup order: `context.utils → context.functions → context.methods → context.snippets`
|
|
642
|
-
|
|
643
664
|
---
|
|
644
665
|
|
|
645
|
-
##
|
|
666
|
+
## Router
|
|
646
667
|
|
|
647
|
-
###
|
|
668
|
+
### Declare Pages
|
|
648
669
|
|
|
649
670
|
```js
|
|
650
671
|
// pages/index.js
|
|
@@ -654,7 +675,7 @@ export default {
|
|
|
654
675
|
}
|
|
655
676
|
```
|
|
656
677
|
|
|
657
|
-
### Navigation
|
|
678
|
+
### Link Navigation
|
|
658
679
|
|
|
659
680
|
```js
|
|
660
681
|
export const NavItem = {
|
|
@@ -664,11 +685,13 @@ export const NavItem = {
|
|
|
664
685
|
}
|
|
665
686
|
```
|
|
666
687
|
|
|
667
|
-
### Programmatic
|
|
688
|
+
### Programmatic Navigation
|
|
689
|
+
|
|
690
|
+
Call `event.preventDefault()` BEFORE the router call:
|
|
668
691
|
|
|
669
692
|
```js
|
|
670
693
|
onClick: (event, el) => {
|
|
671
|
-
event.preventDefault()
|
|
694
|
+
event.preventDefault()
|
|
672
695
|
el.call('router', '/dashboard', el.__ref.root, {}, {
|
|
673
696
|
scrollToTop: true,
|
|
674
697
|
scrollToOptions: { behavior: 'instant' }
|
|
@@ -676,9 +699,9 @@ onClick: (event, el) => {
|
|
|
676
699
|
}
|
|
677
700
|
```
|
|
678
701
|
|
|
679
|
-
### Custom
|
|
702
|
+
### Custom Router Element (Persistent Layouts)
|
|
680
703
|
|
|
681
|
-
Configure in `config.js` to render pages inside a specific element
|
|
704
|
+
Configure in `config.js` to render pages inside a specific element:
|
|
682
705
|
|
|
683
706
|
```js
|
|
684
707
|
// config.js
|
|
@@ -689,18 +712,32 @@ export default {
|
|
|
689
712
|
}
|
|
690
713
|
```
|
|
691
714
|
|
|
692
|
-
The `/` page defines the persistent layout shell. Sub-pages render inside the target element without destroying the layout.
|
|
715
|
+
The `/` page defines the persistent layout shell. Sub-pages render inside the target element without destroying the layout.
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
## `element.require()` (Cross-Environment Dependency Loading)
|
|
720
|
+
|
|
721
|
+
```js
|
|
722
|
+
{
|
|
723
|
+
tag: 'canvas',
|
|
724
|
+
onRender: async (element, state) => {
|
|
725
|
+
const Chart = element.require('chartjs')
|
|
726
|
+
const ctx = element.node.getContext('2d')
|
|
727
|
+
new Chart(ctx, { type: 'bar', data: { /* ... */ } })
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
```
|
|
693
731
|
|
|
694
732
|
---
|
|
695
733
|
|
|
696
|
-
##
|
|
734
|
+
## Common Patterns
|
|
697
735
|
|
|
698
|
-
### Loading
|
|
736
|
+
### Loading State
|
|
699
737
|
|
|
700
738
|
```js
|
|
701
739
|
export const DataList = {
|
|
702
740
|
state: { items: [], loading: true, error: null },
|
|
703
|
-
|
|
704
741
|
Loader: { if: ({ state }) => state.loading, extends: 'Spinner' },
|
|
705
742
|
Error: { if: ({ state }) => Boolean(state.error), text: ({ state }) => state.error },
|
|
706
743
|
Items: {
|
|
@@ -708,7 +745,6 @@ export const DataList = {
|
|
|
708
745
|
children: ({ state }) => state.items,
|
|
709
746
|
childExtends: 'ListItem'
|
|
710
747
|
},
|
|
711
|
-
|
|
712
748
|
onRender: async (el, state) => {
|
|
713
749
|
try {
|
|
714
750
|
const items = await el.call('fetchItems')
|
|
@@ -720,7 +756,7 @@ export const DataList = {
|
|
|
720
756
|
}
|
|
721
757
|
```
|
|
722
758
|
|
|
723
|
-
### Active
|
|
759
|
+
### Active List Item
|
|
724
760
|
|
|
725
761
|
```js
|
|
726
762
|
export const Menu = {
|
|
@@ -734,11 +770,11 @@ export const Menu = {
|
|
|
734
770
|
}
|
|
735
771
|
```
|
|
736
772
|
|
|
737
|
-
### Modal
|
|
773
|
+
### Modal
|
|
738
774
|
|
|
739
775
|
```js
|
|
740
776
|
export const ModalCard = {
|
|
741
|
-
position: 'absolute',
|
|
777
|
+
position: 'absolute', align: 'center center',
|
|
742
778
|
top: 0, left: 0, boxSize: '100% 100%',
|
|
743
779
|
transition: 'all C defaultBezier',
|
|
744
780
|
opacity: '0', visibility: 'hidden', pointerEvents: 'none', zIndex: '-1',
|
|
@@ -753,12 +789,34 @@ export const ModalCard = {
|
|
|
753
789
|
|
|
754
790
|
---
|
|
755
791
|
|
|
756
|
-
##
|
|
792
|
+
## Naming Conventions
|
|
793
|
+
|
|
794
|
+
| Category | Convention | Examples |
|
|
795
|
+
|---|---|---|
|
|
796
|
+
| Components | PascalCase | `CustomComponent`, `NavBar`, `UserProfile` |
|
|
797
|
+
| Properties | camelCase | `paddingInlineStart`, `fontSize`, `backgroundColor` |
|
|
798
|
+
| Repeating keys | Snake_Case suffixes | `Li_1`, `Li_2`, `Li_One` |
|
|
799
|
+
|
|
800
|
+
---
|
|
801
|
+
|
|
802
|
+
## Reserved Keywords
|
|
803
|
+
|
|
804
|
+
These keys are handled by the DOMQL engine and are NOT CSS props or child components:
|
|
805
|
+
|
|
806
|
+
```
|
|
807
|
+
key, extends, childExtends, childExtendsRecursive, childProps, props,
|
|
808
|
+
state, tag, query, data, scope, children, childrenAs, context
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
All other keys: lowercase/camelCase = CSS props, PascalCase = child components.
|
|
812
|
+
|
|
813
|
+
---
|
|
814
|
+
|
|
815
|
+
## Finding DOMQL Elements in Browser DOM
|
|
757
816
|
|
|
758
|
-
DOMQL
|
|
817
|
+
Every DOMQL-managed DOM node has `.ref` pointing to its DOMQL element:
|
|
759
818
|
|
|
760
819
|
```js
|
|
761
|
-
// Every DOMQL-managed DOM node has .ref pointing to its DOMQL element
|
|
762
820
|
const domqlElement = someNode.ref
|
|
763
821
|
domqlElement.key // element key name
|
|
764
822
|
domqlElement.props // current props
|