Skip to content

Commit 7312ae5

Browse files
authored
Merge pull request #30 from mailcarrierapp/feat/live-preview
[2.x] Live preview
2 parents a0d4afa + 28d567c commit 7312ae5

30 files changed

+664
-55
lines changed

bun.lockb

9.68 KB
Binary file not shown.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"laravel/socialite": "^5.6.1",
2626
"livewire/livewire": "^3.4",
2727
"nunomaduro/termwind": "^1.15|^2.0",
28+
"pboivin/filament-peek": "^2.2",
2829
"ralphjsmit/laravel-filament-components": "^2.0",
2930
"socialiteproviders/manager": "^4.3",
3031
"spatie/data-transfer-object": "^3.9.1",

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@
88
"build": "mix --production"
99
},
1010
"devDependencies": {
11+
"@codemirror/lang-html": "^6.4.8",
12+
"@codemirror/lang-json": "^6.0.1",
1113
"@tailwindcss/forms": "^0.5.7",
1214
"@tailwindcss/typography": "^0.5.10",
1315
"autoprefixer": "^10.4.17",
16+
"codemirror": "^6.0.1",
1417
"highlight.js": "^11.9.0",
1518
"laravel-mix": "^6.0.6",
1619
"postcss": "^8.4.33",
1720
"postcss-nesting": "^12.0.2",
1821
"resolve-url-loader": "^5.0.0",
1922
"sass": "^1.70.0",
2023
"sass-loader": "^12.1.0",
21-
"tailwindcss": "^3.4.1"
24+
"tailwindcss": "^3.4.1",
25+
"thememirror": "^2.0.1"
2226
}
2327
}

resources/css/theme.css

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@import '../../vendor/filament/filament/resources/css/theme.css';
1+
@import "../../vendor/filament/filament/resources/css/theme.css";
22

33
@config '../../tailwind.config.js';
44

@@ -7,3 +7,11 @@
77
top: -2px;
88
transform: rotate(-40deg);
99
}
10+
11+
.filament-peek-panel-body iframe {
12+
@apply rounded bg-white;
13+
}
14+
15+
.cm-editor {
16+
@apply h-full;
17+
}

resources/dist/css/theme.css

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/dist/js/codemirror.component.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/dist/mix-manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"/js/highlight.js": "/js/highlight.js",
3+
"/js/codemirror.component.js": "/js/codemirror.component.js",
34
"/css/theme.css": "/css/theme.css",
45
"/css/highlight.css": "/css/highlight.css"
56
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {
2+
history,
3+
indentWithTab
4+
} from "@codemirror/commands";
5+
import { html } from "@codemirror/lang-html";
6+
import { Compartment, EditorState } from '@codemirror/state';
7+
import { EditorView, highlightActiveLineGutter, keymap, lineNumbers } from "@codemirror/view";
8+
import { dracula, smoothy } from 'thememirror';
9+
10+
// Adapted from https://github.com/dotswan/filament-code-editor
11+
const CodeEditorAlpinePlugin = (Alpine) => {
12+
Alpine.data('codeEditorFormComponent', ({ state, isReadOnly, language = 'html' }) => ({
13+
state,
14+
editor: undefined,
15+
themeConfig: undefined,
16+
languageConfig: undefined,
17+
isReadOnly: false,
18+
19+
init() {
20+
this.isReadOnly = isReadOnly;
21+
this.themeConfig = new Compartment();
22+
this.languageConfig = new Compartment();
23+
this.render();
24+
25+
// Needed for programmatic updates from Livewire (e.g. form fill) to the component
26+
this.$watch('state', (value) => {
27+
if (this.editor.state.doc.toString() !== value) {
28+
this.editor.dispatch({
29+
changes: { from: 0, to: this.editor.state.doc.length, insert: value }
30+
});
31+
}
32+
});
33+
},
34+
35+
render() {
36+
this.editor = new EditorView({
37+
parent: this.$refs.codeEditor,
38+
state: EditorState.create({
39+
doc: this.state,
40+
autofocus: true,
41+
indentWithTabs: true,
42+
smartIndent: true,
43+
lineNumbers: true,
44+
matchBrackets: true,
45+
tabSize: 2,
46+
styleSelectedText: true,
47+
extensions: [
48+
keymap.of([indentWithTab]),
49+
this.languageConfig.of(language === 'json' ? json() : html()),
50+
this.themeConfig.of([dracula]),
51+
EditorView.lineWrapping,
52+
EditorState.readOnly.of(this.isReadOnly),
53+
lineNumbers(),
54+
history(),
55+
highlightActiveLineGutter(),
56+
EditorView.updateListener.of((v) => {
57+
if (v.docChanged) {
58+
this.state = v.state.doc.toString();
59+
this.$wire.$commit();
60+
}
61+
}),
62+
],
63+
}),
64+
});
65+
66+
window.addEventListener('theme-changed', () => {
67+
let theme = localStorage.getItem('theme');
68+
if (theme === 'system') {
69+
theme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)
70+
? 'dark'
71+
: 'light';
72+
}
73+
74+
this.editor.dispatch({
75+
effects: this.themeConfig.reconfigure([
76+
theme === 'light' ? smoothy : dracula
77+
])
78+
});
79+
});
80+
},
81+
}));
82+
}
83+
84+
document.addEventListener('alpine:init', () => {
85+
window.Alpine.plugin(CodeEditorAlpinePlugin);
86+
});
Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,17 @@
1+
{{-- Adapted from https://github.com/dotswan/filament-code-editor --}}
12
<x-dynamic-component :component="$getFieldWrapperView()" :field="$field">
2-
<div x-data="codeEditor" class="w-full max-h-full rounded-lg shadow-sm ring-1 transition duration-75 bg-white dark:bg-white/5 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-2 ring-gray-950/10 dark:ring-white/20 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-600 dark:[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-500 overflow-hidden">
3-
<div x-ref="editor" class="w-full h-[300px]" wire:ignore></div>
3+
<div class="relative max-w-full overflow-hidden rounded-lg shadow-sm ring-1 transition duration-75 bg-white dark:bg-white/5 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-2 ring-gray-950/10 dark:ring-white/20 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-600 dark:[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-500 h-[365px]">
4+
<div
5+
class="w-full h-full"
6+
x-data="codeEditorFormComponent({
7+
state: $wire.$entangle('{{ $getStatePath() }}'),
8+
isReadOnly: @js($isDisabled()),
9+
})">
10+
<div
11+
wire:ignore
12+
x-ref="codeEditor"
13+
class="w-full h-full">
14+
</div>
15+
</div>
416
</div>
517
</x-dynamic-component>
6-
7-
<script type="module">
8-
import * as monaco from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm';
9-
10-
Alpine.data('codeEditor', () => ({
11-
state: @entangle($getStatePath()),
12-
init() {
13-
const editor = monaco.editor.create(this.$refs.editor, {
14-
value: this.state,
15-
language: 'twig',
16-
automaticLayout: true,
17-
scrollBeyondLastLine: false,
18-
readOnly: {{ $isDisabled() ? 'true' : 'false' }},
19-
minimap: {
20-
enabled: false,
21-
},
22-
});
23-
24-
editor.getModel().onDidChangeContent(() => this.state = editor.getValue());
25-
26-
window.addEventListener('theme-changed', () => {
27-
let theme = localStorage.getItem('theme');
28-
29-
if (theme === 'system') {
30-
theme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)
31-
? 'dark'
32-
: 'light';
33-
}
34-
35-
monaco.editor.setTheme(theme === 'light' ? 'vs' : 'vs-dark');
36-
});
37-
}
38-
}));
39-
</script>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE html>
2+
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
3+
<head>
4+
<meta charset="utf-8" />
5+
6+
<meta name="application-name" content="{{ config('app.name') }}" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1" />
8+
<meta name="csrf_token" value="{{ csrf_token() }}"/>
9+
10+
<title>MailCarrier preview</title>
11+
12+
<style>
13+
[x-cloak] {
14+
display: none !important;
15+
}
16+
</style>
17+
18+
@livewireStyles
19+
</head>
20+
21+
<body class="antialiased">
22+
{{ $slot }}
23+
24+
@livewireScripts
25+
</body>
26+
</html>

0 commit comments

Comments
 (0)