Skip to content

Commit 864c06d

Browse files
committed
Add support for multiple themes
1 parent e90026f commit 864c06d

File tree

9 files changed

+232
-20
lines changed

9 files changed

+232
-20
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Added
6+
- Added the ability to define multiple themes for e.g. dark mode.
7+
- Cache time is now configurable.
8+
59
## 0.5.9 - 2022-01-19
610

711
### Fixed

config/torchlight.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,22 @@
66
// leave this blank your default app cache will be used.
77
'cache' => env('TORCHLIGHT_CACHE_DRIVER'),
88

9+
// Cache blocks for 30 days.
10+
'cache_seconds' => env('TORCHLIGHT_CACHE_TTL', 60 * 60 * 24 * 30),
11+
912
// Which theme you want to use. You can find all of the themes at
1013
// https://torchlight.dev/docs/themes.
1114
'theme' => env('TORCHLIGHT_THEME', 'material-theme-palenight'),
1215

16+
// If you want to use two separate themes for dark and light modes,
17+
// you can use an array to define both themes. Torchlight renders
18+
// both on the page, and you will be responsible for hiding one
19+
// or the other depending on the dark / light mode via CSS.
20+
// 'theme' => [
21+
// 'dark' => 'github-dark',
22+
// 'light' => 'github-light',
23+
// ],
24+
1325
// Your API token from torchlight.dev.
1426
'token' => env('TORCHLIGHT_TOKEN'),
1527

src/Blade/BladeManager.php

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use Illuminate\Http\Response;
99
use Illuminate\Support\Arr;
10+
use Illuminate\Support\Str;
1011
use Torchlight\Block;
1112
use Torchlight\Torchlight;
1213

@@ -62,15 +63,49 @@ public static function renderContent($content)
6263
return $content;
6364
}
6465

65-
Torchlight::highlight(static::$blocks);
66+
$response = Torchlight::highlight(static::$blocks);
67+
$response = collect($response)->keyBy->id();
6668

6769
$ids = Torchlight::findTorchlightIds($content);
6870

71+
// The first time through we have to expand all
72+
// the blocks to include the clones.
73+
foreach ($ids as $id) {
74+
// For each block, stash the unadulterated content so
75+
// we can duplicate it for clones if we need to.
76+
$begin = "<!-- __torchlight-block-[$id]_begin__ -->";
77+
$end = "<!-- __torchlight-block-[$id]_end__ -->";
78+
$clean = Str::between($content, $begin, $end);
79+
80+
$clones = '';
81+
82+
if ($block = Arr::get($response, $id)) {
83+
foreach ($block->clones() as $clone) {
84+
// Swap the original ID with the cloned ID.
85+
$clones .= str_replace(
86+
"__torchlight-block-[$id]", "__torchlight-block-[{$clone->id()}]", $clean
87+
);
88+
89+
// Since we've added a new ID to the template, we
90+
// need to make sure we add it to the array of
91+
// IDs that drives the str_replace below.
92+
$ids[] = $clone->id();
93+
}
94+
}
95+
96+
// Get rid of the first comment no matter what.
97+
$content = str_replace($begin, '', $content);
98+
99+
// Replace the second comment with the clones.
100+
$content = str_replace($end, $clones, $content);
101+
}
102+
69103
$swap = [];
70104

105+
// Second time through we'll populate the replacement array.
71106
foreach ($ids as $id) {
72107
/** @var Block $block */
73-
if (!$block = Arr::get(static::$blocks, $id)) {
108+
if (!$block = Arr::get($response, $id)) {
74109
continue;
75110
}
76111

src/Blade/CodeComponent.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,12 @@ public function render()
110110
// We have to add the ##PRE## and ##POST## tags to cover a framework bug.
111111
// @see BladeManager::renderContent.
112112
return <<<'EOT'
113-
##PRE_TL_COMPONENT##<code {{ $block->placeholder('attrs') }}{{
113+
##PRE_TL_COMPONENT##<!-- {{ $block->placeholder('begin') }} --><code {{ $block->placeholder('attrs') }}{{
114114
$attributes->except('style')->merge([
115115
'class' => $block->placeholder('classes'),
116116
'style' => $attributes->get('style') . $block->placeholder('styles')
117117
])
118-
}}><?php ob_start(); ?>{{ $slot }}<?php $capture(ob_get_clean()) ?>{{ $block->placeholder() }}</code>##POST_TL_COMPONENT##
118+
}}><?php ob_start(); ?>{{ $slot }}<?php $capture(ob_get_clean()) ?>{{ $block->placeholder() }}</code><!-- {{ $block->placeholder('end') }} -->##POST_TL_COMPONENT##
119119
EOT;
120120
}
121121
}

src/Block.php

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
namespace Torchlight;
77

8+
use Illuminate\Support\Arr;
89
use Illuminate\Support\Str;
910
use Illuminate\Support\Traits\Macroable;
1011

@@ -87,6 +88,11 @@ class Block
8788
*/
8889
protected $id;
8990

91+
/**
92+
* @var array
93+
*/
94+
protected $clones = [];
95+
9096
/**
9197
* @param null|string $id
9298
* @return static
@@ -105,7 +111,7 @@ public function __construct($id = null)
105111
$this->id = $id ?? $this->generateId();
106112

107113
// Set a default theme.
108-
$this->theme = Torchlight::config('theme');
114+
$this->theme(Torchlight::config('theme'));
109115
}
110116

111117
/**
@@ -140,6 +146,25 @@ public function hash()
140146
);
141147
}
142148

149+
/**
150+
* @return array
151+
*/
152+
public function clones()
153+
{
154+
return $this->clones;
155+
}
156+
157+
/**
158+
* @param $num
159+
* @return $this
160+
*/
161+
public function cloned($num)
162+
{
163+
$this->id = Str::finish($this->id, "_clone_$num");
164+
165+
return $this;
166+
}
167+
143168
/**
144169
* @param string $extra
145170
* @return string
@@ -170,6 +195,8 @@ public function language($language)
170195
*/
171196
public function theme($theme)
172197
{
198+
$theme = $this->normalizeArrayTheme($theme);
199+
173200
if ($theme) {
174201
$this->theme = $theme;
175202
}
@@ -229,6 +256,27 @@ public function wrapped($wrapped)
229256
return $this;
230257
}
231258

259+
/**
260+
* @return Block[]
261+
*/
262+
public function spawnClones()
263+
{
264+
$this->clones = [];
265+
266+
$themes = explode(',', $this->theme);
267+
268+
// Set the theme for the current block, so that we
269+
// don't break the reference to it.
270+
$this->theme(array_shift($themes));
271+
272+
// Then generate any clones for the remaining themes.
273+
$this->clones = collect($themes)->map(function ($theme, $num) {
274+
return (clone $this)->theme($theme)->cloned($num);
275+
})->toArray();
276+
277+
return $this->clones;
278+
}
279+
232280
/**
233281
* @return array
234282
*/
@@ -243,6 +291,25 @@ public function toRequestParams()
243291
];
244292
}
245293

294+
/**
295+
* @param $theme
296+
* @return mixed|string
297+
*/
298+
protected function normalizeArrayTheme($theme)
299+
{
300+
if (!is_array($theme)) {
301+
return $theme;
302+
}
303+
304+
if (Arr::isAssoc($theme)) {
305+
return collect($theme)->map(function ($name, $label) {
306+
return "$label:$name";
307+
})->join(',');
308+
}
309+
310+
return implode(',', $theme);
311+
}
312+
246313
/**
247314
* @param $code
248315
* @return string

src/Client.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ public function highlight($blocks)
2020
{
2121
$blocks = Arr::wrap($blocks);
2222

23-
$blocks = $this->collectionOfBlocks($blocks)->keyBy->id();
23+
$blocks = $this->collectionOfBlocks($blocks)->values();
24+
$blocks = $blocks->merge($blocks->map->spawnClones())->flatten();
25+
$blocks = $blocks->keyBy->id();
2426

2527
// First set the html from the cache if it is already stored.
2628
$this->setBlocksFromCache($blocks);
@@ -170,7 +172,8 @@ protected function setCacheFromBlocks(Collection $blocks, Collection $ids)
170172
}
171173

172174
if (count($value)) {
173-
Torchlight::cache()->put($this->cacheKey($block), $value, $seconds = 7 * 24 * 60 * 60);
175+
$seconds = Torchlight::config('cache_seconds', 7 * 24 * 60 * 60);
176+
Torchlight::cache()->put($this->cacheKey($block), $value, $seconds);
174177
}
175178
});
176179
}
@@ -215,7 +218,8 @@ protected function defaultResponse(Block $block)
215218
'classes' => 'torchlight',
216219
'styles' => '',
217220
'attrs' => [
218-
'data-lang' => $block->language
221+
'data-theme' => $block->theme,
222+
'data-lang' => $block->language,
219223
],
220224
'wrapped' => "<pre><code data-lang='{$block->language}' class='torchlight'>{$highlighted}</code></pre>",
221225
];

tests/BaseTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ protected function fakeApi()
6464
'classes' => 'torchlight',
6565
'styles' => 'background-color: #000000;',
6666
'attrs' => [
67+
'data-theme' => $block['theme'],
6768
'data-lang' => $block['language']
6869
],
6970
'wrapped' => "<pre><code>$highlighted</code></pre>",

tests/DualThemeTest.php

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
/**
3+
* @author Aaron Francis <[email protected]|https://twitter.com/aarondfrancis>
4+
*/
5+
6+
namespace Torchlight\Tests;
7+
8+
use Illuminate\Support\Facades\Route;
9+
use Illuminate\Support\Facades\View;
10+
use Torchlight\Middleware\RenderTorchlight;
11+
12+
class DualThemeTest extends BaseTest
13+
{
14+
public function getEnvironmentSetUp($app)
15+
{
16+
config()->set('torchlight.blade_components', true);
17+
config()->set('torchlight.token', 'token');
18+
config()->set('torchlight.theme', [
19+
'github-dark',
20+
'github-light'
21+
]);
22+
}
23+
24+
protected function getView($view)
25+
{
26+
// This helps when testing multiple Laravel versions locally.
27+
$this->artisan('view:clear');
28+
29+
Route::get('/torchlight', function () use ($view) {
30+
return View::file(__DIR__ . '/Support/' . $view);
31+
})->middleware(RenderTorchlight::class);
32+
33+
return $this->call('GET', 'torchlight');
34+
}
35+
36+
/** @test */
37+
public function multiple_themes_with_comma()
38+
{
39+
config()->set('torchlight.theme', [
40+
'github-dark,github-light'
41+
]);
42+
43+
$this->assertDarkLight('github-dark', 'github-light');
44+
}
45+
46+
/** @test */
47+
public function multiple_themes_no_labels()
48+
{
49+
config()->set('torchlight.theme', [
50+
'github-dark',
51+
'github-light'
52+
]);
53+
54+
$this->assertDarkLight('github-dark', 'github-light');
55+
}
56+
57+
/** @test */
58+
public function multiple_themes_with_labels()
59+
{
60+
config()->set('torchlight.theme', [
61+
'dark' => 'github-dark',
62+
'light' => 'github-light'
63+
]);
64+
65+
$this->assertDarkLight('dark:github-dark', 'light:github-light');
66+
}
67+
68+
protected function assertDarkLight($theme1, $theme2)
69+
{
70+
$this->fakeSuccessfulResponse('component', [
71+
'classes' => 'torchlight1',
72+
'styles' => 'background-color: #111111;',
73+
'highlighted' => 'response 1',
74+
]);
75+
76+
$this->fakeSuccessfulResponse('component_clone_0', [
77+
'classes' => 'torchlight2',
78+
'styles' => 'background-color: #222222;',
79+
'highlighted' => 'response 2',
80+
]);
81+
82+
$response = $this->getView('simple-php-hello-world.blade.php');
83+
84+
$this->assertEquals(
85+
"<pre><code data-theme=\"{$theme1}\" data-lang=\"php\" class=\"torchlight1\" style=\"background-color: #111111;\">response 1</code><code data-theme=\"{$theme2}\" data-lang=\"php\" class=\"torchlight2\" style=\"background-color: #222222;\">response 2</code></pre>",
86+
$response->content()
87+
);
88+
}
89+
}

0 commit comments

Comments
 (0)