Skip to content

Commit 67a77ba

Browse files
committed
feature: implement inverse token modifier
closes #475
1 parent 159d993 commit 67a77ba

File tree

3 files changed

+91
-7
lines changed

3 files changed

+91
-7
lines changed

src/HTMLAttributeBinder.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ private function handleModifier(
390390
string $modifier,
391391
mixed $bindValue
392392
):void {
393-
$modifierChar = $modifier[0];
394-
$modifierValue = substr($modifier, 1);
393+
$modifierChar = $this->getModifierType($modifier);
394+
$modifierValue = $this->getModifierBody($modifier);
395395
$condition = null;
396396
if(false !== $spacePos = strpos($modifierValue, " ")) {
397397
$modifierValue = substr($modifierValue, $spacePos + 1);
@@ -409,6 +409,9 @@ private function handleModifier(
409409
case ":":
410410
$tokenList = $this->getTokenList($element, $attribute);
411411
$tokenNames = $this->resolveTokenNames($modifier, $bindValue);
412+
if($this->isInverseModifier($modifier)) {
413+
$bindValue = !$bindValue;
414+
}
412415
if($bindValue) {
413416
$tokenList->add(...$tokenNames);
414417
}
@@ -418,7 +421,7 @@ private function handleModifier(
418421
break;
419422

420423
case "?":
421-
if($modifierValue[0] === "!") {
424+
if($this->isInverseModifier($modifier)) {
422425
$bindValue = !$bindValue;
423426
}
424427

@@ -463,8 +466,7 @@ private function extractBindKey(string $bindExpression):string {
463466
}
464467

465468
private function extractModifierExpression(string $modifier):string {
466-
$modifierValue = substr($modifier, 1);
467-
$modifierValue = ltrim($modifierValue, "!");
469+
$modifierValue = $this->getModifierBody($modifier);
468470
return strtok($modifierValue, " ") ?: "";
469471
}
470472

@@ -485,8 +487,7 @@ private function resolveTokenNames(string $modifier, mixed $bindValue):array {
485487

486488
/** @return array<int, string> */
487489
private function extractModifierTokens(string $modifier):array {
488-
$modifierValue = substr($modifier, 1);
489-
$modifierValue = ltrim($modifierValue, "!");
490+
$modifierValue = $this->getModifierBody($modifier);
490491
$spacePos = strpos($modifierValue, " ");
491492
if($spacePos === false) {
492493
return [];
@@ -495,6 +496,30 @@ private function extractModifierTokens(string $modifier):array {
495496
return $this->prepareTokenListValues(substr($modifierValue, $spacePos + 1));
496497
}
497498

499+
private function getModifierType(string $modifier):string {
500+
foreach(str_split($modifier) as $char) {
501+
if($char === ":" || $char === "?") {
502+
return $char;
503+
}
504+
}
505+
506+
return $modifier[0];
507+
}
508+
509+
private function isInverseModifier(string $modifier):bool {
510+
return str_contains($modifier, "!");
511+
}
512+
513+
private function getModifierBody(string $modifier):string {
514+
$modifierType = $this->getModifierType($modifier);
515+
$modifierValue = ltrim($modifier, "!");
516+
if(str_starts_with($modifierValue, $modifierType)) {
517+
return ltrim(substr($modifierValue, 1), "!");
518+
}
519+
520+
return ltrim(substr($modifier, 1), "!");
521+
}
522+
498523
private function extractCondition(string $bindExpression):?string {
499524
$parts = explode("=", $bindExpression, 2);
500525
return $parts[1] ?? null;

test/phpunit/HTMLAttributeBinderTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,30 @@ public function testBind_modifierColon_removesMultipleClassNamesAtOnce():void {
158158
self::assertFalse($div->classList->contains("featured"));
159159
}
160160

161+
public function testBind_modifierColon_inverseLogic_afterTokenModifier():void {
162+
$document = new HTMLDocument(HTMLPageContent::HTML_INVERSE_MODIFIER_BINDING);
163+
$sut = new HTMLAttributeBinder();
164+
$div = $document->getElementById("div1");
165+
166+
$sut->bind("show", false, $div);
167+
self::assertTrue($div->classList->contains("hidden"));
168+
169+
$sut->bind("show", true, $div);
170+
self::assertFalse($div->classList->contains("hidden"));
171+
}
172+
173+
public function testBind_modifierColon_inverseLogic_beforeTokenModifier():void {
174+
$document = new HTMLDocument(HTMLPageContent::HTML_INVERSE_MODIFIER_BINDING);
175+
$sut = new HTMLAttributeBinder();
176+
$div = $document->getElementById("div2");
177+
178+
$sut->bind("show", false, $div);
179+
self::assertTrue($div->classList->contains("hidden"));
180+
181+
$sut->bind("show", true, $div);
182+
self::assertFalse($div->classList->contains("hidden"));
183+
}
184+
161185
public function testBind_modifierColon_multipleExpressionsCanBeBundled():void {
162186
$document = new HTMLDocument(HTMLPageContent::HTML_MULTI_CLASS_BINDING);
163187
$sut = new HTMLAttributeBinder();
@@ -208,6 +232,30 @@ public function testBind_modifierQuestion():void {
208232
self::assertFalse($btn2->disabled);
209233
}
210234

235+
public function testBind_modifierQuestion_inverseLogic_afterQuestionModifier():void {
236+
$document = new HTMLDocument(HTMLPageContent::HTML_INVERSE_MODIFIER_BINDING);
237+
$sut = new HTMLAttributeBinder();
238+
$button = $document->getElementById("btn1");
239+
240+
$sut->bind("isEnabled", false, $button);
241+
self::assertTrue($button->disabled);
242+
243+
$sut->bind("isEnabled", true, $button);
244+
self::assertFalse($button->disabled);
245+
}
246+
247+
public function testBind_modifierQuestion_inverseLogic_beforeQuestionModifier():void {
248+
$document = new HTMLDocument(HTMLPageContent::HTML_INVERSE_MODIFIER_BINDING);
249+
$sut = new HTMLAttributeBinder();
250+
$button = $document->getElementById("btn2");
251+
252+
$sut->bind("isEnabled", false, $button);
253+
self::assertTrue($button->disabled);
254+
255+
$sut->bind("isEnabled", true, $button);
256+
self::assertFalse($button->disabled);
257+
}
258+
211259
public function testBind_modifierQuestion_withNullValue():void {
212260
$document = new HTMLDocument(HTMLPageContent::HTML_DIFFERENT_BIND_PROPERTIES);
213261
$sut = new HTMLAttributeBinder();

test/phpunit/TestHelper/HTMLPageContent.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,17 @@ class HTMLPageContent {
197197
<div id="div4" class="panel" data-bind:class=":isSelected selected; :isAdmin admin"></div>
198198
199199
<button id="btn3" data-bind:disabled="?isBusy; ?isLocked"></button>
200+
HTML;
201+
202+
const HTML_INVERSE_MODIFIER_BINDING = <<<HTML
203+
<!doctype html>
204+
<div id="div1" class="panel" data-bind:class=":!show hidden" data-rebind></div>
205+
206+
<div id="div2" class="panel" data-bind:class="!:show hidden" data-rebind></div>
207+
208+
<button id="btn1" data-bind:disabled="?!isEnabled" data-rebind></button>
209+
210+
<button id="btn2" data-bind:disabled="!?isEnabled" data-rebind></button>
200211
HTML;
201212

202213
const HTML_SIMPLE_BOOLEAN = <<<HTML

0 commit comments

Comments
 (0)