Skip to content

Commit a6adeed

Browse files
authored
Merge pull request #20 from Group-One-Technology/jr.refactor/group-variables
Refactor: Group Variables (rename of Parent Variables)
2 parents 374701e + ed27712 commit a6adeed

File tree

7 files changed

+121
-69
lines changed

7 files changed

+121
-69
lines changed

README.md

+19-15
Original file line numberDiff line numberDiff line change
@@ -340,30 +340,26 @@ If you want to use the variable across an element's attributes and events, you c
340340

341341
Like the example above, `:load` can be used to set the initial value of the variable.
342342

343-
### Parent Element Variables
343+
### Group Variables
344344

345-
Adding a `:parent` attribute to an element will allow you to access its variables from its children using `parent.` variables.
346-
347-
A children's `parent.` variable is the same as the parent's `el.` variable.
345+
Adding a `:group` attribute to an element will allow you to access its variables from its children using `group.` variables.
348346

349347
```html
350-
<div id="accordion" class="accordion" :parent>
351-
<!-- Parent Element -->
352-
348+
<!-- Group Element -->
349+
<div id="accordion" class="accordion" :group>
353350
<!-- Children Elements -->
354-
<!-- parent.variable == #accordion's el.variable -->
355351
<section
356352
class="grid transition-all border-gray-300 border border-b-0 rounded hover:bg-gray-100"
357353
>
358354
<button
359-
:click="parent.activeSection = 'about'"
355+
:click="group.activeSection = 'about'"
360356
class="cursor-pointer font-bold p-4"
361357
>
362358
About Us
363359
</button>
364360
<div
365361
class="p-4 pt-2 overflow-hidden hidden"
366-
:class="parent.activeSection =='about' ? 'block' : 'hidden'"
362+
:class="group.activeSection =='about' ? 'block' : 'hidden'"
367363
>
368364
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
369365
eirmod.
@@ -374,14 +370,14 @@ A children's `parent.` variable is the same as the parent's `el.` variable.
374370
class="grid transition-all border-gray-300 border border-b-0 rounded hover:bg-gray-100"
375371
>
376372
<button
377-
:click="parent.activeSection = 'contact'"
373+
:click="group.activeSection = 'contact'"
378374
class="cursor-pointer font-bold p-4"
379375
>
380376
Contact Us
381377
</button>
382378
<div
383379
class="p-4 pt-2 overflow-hidden"
384-
:class="parent.activeSection =='contact' ? 'block' : 'hidden'"
380+
:class="group.activeSection =='contact' ? 'block' : 'hidden'"
385381
>
386382
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
387383
eirmod.
@@ -390,17 +386,17 @@ A children's `parent.` variable is the same as the parent's `el.` variable.
390386

391387
<section
392388
class="grid transition-all border-gray-300 border rounded hover:bg-gray-100"
393-
:class="parent.activeSection =='team' ? 'active' : ''"
389+
:class="group.activeSection =='team' ? 'active' : ''"
394390
>
395391
<button
396-
:click="parent.activeSection = 'team'"
392+
:click="group.activeSection = 'team'"
397393
class="cursor-pointer font-bold p-4"
398394
>
399395
Team 3
400396
</button>
401397
<div
402398
class="p-4 pt-2 overflow-hidden"
403-
:class="parent.activeSection =='team' ? 'block' : 'hidden'"
399+
:class="group.activeSection =='team' ? 'block' : 'hidden'"
404400
>
405401
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
406402
eirmod.
@@ -409,6 +405,14 @@ A children's `parent.` variable is the same as the parent's `el.` variable.
409405
</div>
410406
```
411407

408+
You can set the default value of the group variables in the `:group` directive:
409+
410+
```html
411+
<div id="accordion" class="accordion" :group="activeSection = 'about'">
412+
<!-- ... -->
413+
</div>
414+
```
415+
412416
### Variable Methods
413417

414418
MiniJS added some commonly-used custom methods to variables.

index.html

+13-10
Original file line numberDiff line numberDiff line change
@@ -1471,14 +1471,15 @@ <h3 class="font-bold font-mono">Multi Select:</h3>
14711471
grid-template-rows: 0fr 1fr;
14721472
}
14731473
</style>
1474-
<div class="accordion" :parent>
1474+
<div class="accordion" :group="activeSection = 'about'">
14751475
<section
1476-
class="grid transition-all border-gray-300 border border-b-0 rounded hover:bg-gray-100"
1477-
:class="parent.activeSection == 'about' ? 'active' : ''"
1476+
class="transition-all border-gray-300 border border-b-0 rounded hover:bg-gray-100"
1477+
:class="group.activeSection == 'about' ? 'active' : ''"
14781478
>
14791479
<a
1480-
:click="parent.activeSection='about'"
1480+
:click="group.activeSection = 'about'"
14811481
class="cursor-pointer font-bold p-4"
1482+
:aria-expanded="group.activeSection == 'about'"
14821483
>
14831484
About Us
14841485
</a>
@@ -1491,12 +1492,13 @@ <h3 class="font-bold font-mono">Multi Select:</h3>
14911492
</section>
14921493

14931494
<section
1494-
class="grid transition-all border-gray-300 border border-b-0 rounded hover:bg-gray-100"
1495-
:class="parent.activeSection =='contact' ? 'active' : ''"
1495+
class="transition-all border-gray-300 border border-b-0 rounded hover:bg-gray-100"
1496+
:class="group.activeSection =='contact' ? 'active' : ''"
14961497
>
14971498
<a
1498-
:click="parent.activeSection='contact'"
1499+
:click="group.activeSection = 'contact'"
14991500
class="cursor-pointer font-bold p-4"
1501+
:aria-expanded="group.activeSection == 'contact'"
15001502
>
15011503
Contact Us
15021504
</a>
@@ -1509,11 +1511,12 @@ <h3 class="font-bold font-mono">Multi Select:</h3>
15091511
</section>
15101512

15111513
<section
1512-
class="grid transition-all border-gray-300 border rounded hover:bg-gray-100"
1513-
:class="parent.activeSection =='team' ? 'active' : ''"
1514+
class="transition-all border-gray-300 border rounded hover:bg-gray-100"
1515+
:class="group.activeSection == 'team' ? 'active' : ''"
15141516
>
15151517
<a
1516-
:click="parent.activeSection='team'"
1518+
:click="group.activeSection = 'team'"
1519+
:aria-expanded="group.activeSection == 'team'"
15171520
class="cursor-pointer font-bold p-4"
15181521
>
15191522
Team 3

lib/entity.js

+25-21
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export class Entity {
1414
this.dynamicScripts = dynamicScripts
1515

1616
this.variables = []
17+
this.groupVariables = []
1718
this.id = this.generateEntityUUID()
1819

1920
this.state = {}
@@ -23,15 +24,18 @@ export class Entity {
2324

2425
if (this.base.debug) this.element.dataset.entityId = this.id
2526

26-
this.attributes.evaluateParent()
27+
this.setAsGroup()
2728
}
2829

29-
setAsParent() {
30+
setAsGroup() {
31+
if (!this.element.hasAttribute(':group')) return
32+
if (this.isGroup()) return
33+
3034
this.uuid = this.id
3135
this.element.dataset['mini.uuid'] = this.uuid
3236
}
3337

34-
isParent() {
38+
isGroup() {
3539
return !!this.uuid
3640
}
3741

@@ -117,7 +121,8 @@ export class Entity {
117121
return !(variable in this.state)
118122
})
119123

120-
this.variables.push(...identifiers)
124+
if (name === ':group') this.groupVariables.push(...identifiers)
125+
else this.variables.push(...identifiers)
121126
})
122127
}
123128

@@ -143,34 +148,34 @@ export class Entity {
143148

144149
this.variables.forEach((variable) => {
145150
if (State.isElState(variable)) {
146-
this.setAsParent()
151+
this.setAsGroup()
147152

148153
if (window[this.id] == null) {
149154
window[this.id] = this.base.state.create({}, this.id)
150155
}
151156

152157
this.base.state.addVariable(this.id, this.id)
153158

154-
if (variable !== 'el') {
159+
if (variable !== State.EL_STATE) {
155160
const [_, varName] = variable.split('.')
156161
this.base.state.addEntityVariable(this.id, varName, this.id)
157162
}
158-
} else if (State.isParentState(variable)) {
159-
if (!this.parent) this.parent = this.getParent()
163+
} else if (State.isGroupState(variable)) {
164+
if (!this.group) this.group = this.getGroup()
160165

161-
// Cases where parent is not found:
162-
// - an each item with a :parent directive being removed due to re-evaluation of :each attribute
163-
if (this.parent == null) return
166+
// Cases where group is not found:
167+
// - an each item with a :group directive being removed due to re-evaluation of :each attribute
168+
if (this.group == null) return
164169

165-
if (window[this.parent.id] == null) {
166-
window[this.parent.id] = this.base.state.create({}, this.parent.id)
170+
if (window[this.group.id] == null) {
171+
window[this.group.id] = this.base.state.create({}, this.group.id)
167172
}
168173

169-
this.base.state.addVariable(this.parent.id, this.id)
174+
this.base.state.addVariable(this.group.id, this.id)
170175

171-
if (variable !== 'parent') {
176+
if (variable !== State.GROUP_STATE) {
172177
const [_, varName] = variable.split('.')
173-
this.base.state.addEntityVariable(this.parent.id, varName, this.id)
178+
this.base.state.addEntityVariable(this.group.id, varName, this.id)
174179
}
175180
} else if (typeof window[variable] === 'function') {
176181
this.variables.splice(this.variables.indexOf(variable), 1)
@@ -186,15 +191,14 @@ export class Entity {
186191
})
187192
}
188193

189-
getParent() {
190-
let currentElement = this.element
191-
let parentNode = this.getClosestEl('data-mini.uuid')
194+
getGroup() {
195+
const groupNode = this.getClosestEl('data-mini.uuid')
192196

193-
if (parentNode == null) return { id: 'EntityDocument' }
197+
if (groupNode == null) return { id: 'EntityDocument' }
194198

195199
const entities = Array.from(this.base.state.entities.values())
196200
const entity = entities.find(
197-
(e) => e.uuid == parentNode.dataset['mini.uuid']
201+
(e) => e.uuid == groupNode.dataset['mini.uuid']
198202
)
199203

200204
return entity

lib/entity/attributes.js

+33-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export class Attributes {
1111
':checked',
1212
':each',
1313
':each.item',
14-
':parent',
14+
':group',
1515
]
1616
static FORBIDDEN_ATTRIBUTES = [':innerHTML', ':innerText']
1717

@@ -64,12 +64,15 @@ export class Attributes {
6464
const ids = {
6565
$: 'document-querySelector',
6666
el: `proxyWindow['${this.base.id}${State.DISABLE_RE_RENDER_KEY}']`,
67+
group: this.base.group
68+
? `proxyWindow['${this.base.group.id}${
69+
!options.isGroup ? State.DISABLE_RE_RENDER_KEY : ''
70+
}']`
71+
: undefined,
72+
...(options.ids || {}),
6773
// "this" is set under the interpreter as bind context
6874
}
6975

70-
if (this.base.parent)
71-
ids.parent = `proxyWindow['${this.base.parent.id}${State.DISABLE_RE_RENDER_KEY}']`
72-
7376
engine.replace(ids)
7477

7578
// window variables are used instead of proxy window
@@ -93,7 +96,7 @@ export class Attributes {
9396

9497
async evaluateAttribute(attr) {
9598
if (!Attributes.isValidAttribute(attr, this.base.element)) return
96-
if (attr === ':parent') this.evaluateParent()
99+
if (attr === ':group') await this.evaluateGroup()
97100
else if (attr === ':class') await this.evaluateClass()
98101
else if (attr === ':text') await this.evaluateText()
99102
else if (attr === ':value') await this.evaluateValue()
@@ -131,10 +134,31 @@ export class Attributes {
131134
})
132135
}
133136

134-
evaluateParent() {
135-
if (!this.base.element.hasAttribute(':parent')) return
136-
if (this.base.isParent()) return
137-
this.base.setAsParent()
137+
/*
138+
:group is a special attribute that acts as an :load event
139+
when it has a given expr. Unlike other attributes, state updates
140+
inside :group will trigger re-renders.
141+
142+
NOTE: This should NOT be used in this.evaluate() because it will
143+
trigger an infinite loop.
144+
*/
145+
async evaluateGroup() {
146+
if (!this.base.isGroup()) return
147+
148+
const expr = this.base.element.getAttribute(':group')
149+
if (!expr) return
150+
151+
const ids = {}
152+
153+
this.base.groupVariables.forEach((variable) => {
154+
ids[variable] = `proxyWindow['${this.base.id}'].${variable}`
155+
})
156+
157+
try {
158+
await this._interpret(expr, { isGroup: true, ids })
159+
} catch (error) {
160+
this._handleError(':group', expr, error)
161+
}
138162
}
139163

140164
async evaluateClass() {

lib/entity/events.js

+20-5
Original file line numberDiff line numberDiff line change
@@ -425,19 +425,34 @@ export class Events {
425425
const expr = entity.element.getAttribute(attr)
426426
if (!expr) return
427427

428+
const EL_PREFIX = State.EL_STATE + '.'
428429
const elVariables = entity.variables
429-
.filter((v) => v.startsWith('el.') && v !== 'el')
430-
.map((v) => v.replace('el.', ''))
431-
const variables = entity.variables.filter((v) => !v.startsWith('el.'))
430+
.filter((v) => v.startsWith(EL_PREFIX) && v !== State.EL_STATE)
431+
.map((v) => v.replace(EL_PREFIX, ''))
432+
433+
const GROUP_PREFIX = State.EL_STATE + '.'
434+
const groupVariables = entity.variables
435+
.filter((v) => v.startsWith(GROUP_PREFIX) && v !== State.GROUP_STATE)
436+
.map((v) => v.replace(GROUP_PREFIX, ''))
437+
438+
const variables = entity.variables.filter(
439+
(v) => !v.startsWith(EL_PREFIX) && !v.startsWith(GROUP_PREFIX)
440+
)
432441

433442
mini.state.attachVariableHelpers(variables)
434443
mini.state.attachVariableHelpers(elVariables, entity.id)
435444

445+
if (entity.group)
446+
mini.state.attachVariableHelpers(groupVariables, entity.group.id)
447+
436448
try {
437449
await this._interpret(expr)
438450

439451
mini.state.attachVariableHelpers(variables)
440452
mini.state.attachVariableHelpers(elVariables, entity.id)
453+
454+
if (entity.group)
455+
mini.state.attachVariableHelpers(groupVariables, entity.group.id)
441456
} catch (error) {
442457
if (!entity.isExists()) return
443458
console.error(
@@ -455,10 +470,10 @@ export class Events {
455470
// "this" is set under the interpreter as bind context
456471
}
457472

458-
if (this.base.parent) ids.parent = `proxyWindow['${this.base.parent.id}']`
473+
if (this.base.group) ids.group = `proxyWindow['${this.base.group.id}']`
459474

460475
this.base.variables.forEach((variable) => {
461-
if (State.isElState(variable) || State.isParentState(variable)) return
476+
if (State.isElState(variable) || State.isGroupState(variable)) return
462477

463478
ids[variable] = `proxyWindow-${variable}`
464479
})

lib/generators/lexer.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Parser } from 'acorn'
22
import * as walk from 'acorn-walk'
33
import escodegen from 'escodegen'
4+
import { State } from '../state'
45

56
const FUNCTION_NODE_TYPES = [
67
'FunctionDeclaration',
@@ -113,7 +114,7 @@ function getVariables(node) {
113114
export class Lexer {
114115
static debug = false
115116
static IGNORED_KEYS = ['event', 'window', 'document', 'console', 'Math']
116-
static ENTITY_KEYS = ['el', 'parent']
117+
static ENTITY_KEYS = [State.EL_STATE, State.GROUP_STATE]
117118

118119
constructor(code) {
119120
this._code = code

0 commit comments

Comments
 (0)