| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- @php
- $gridColumns = $getColumns();
- $id = $getId();
- $label = $getLabel();
- $name = $getName();
- $statePath = $getStatePath();
- @endphp
- <x-dynamic-component component="filament-forms::field-wrapper"
- :field="$field"
- :grid-columns="$gridColumns"
- class="math-editor-field">
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
- <!-- Input Section -->
- <div>
- @if ($label)
- <label for="{{ $id }}" class="block text-sm font-medium text-gray-700 mb-2">
- {{ $label }}
- </label>
- @endif
- <textarea
- id="{{ $id }}"
- name="{{ $name }}"
- type="text"
- class="block w-full rounded-lg border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 font-mono text-sm"
- @if ($isRequired()) required @endif
- @if ($isDisabled()) disabled @endif
- x-data="mathEditor({
- value: @js($getState()),
- statePath: '{{ $statePath }}',
- })"
- x-model="value"
- x-on:input="updateValue"
- x-on:change="updateValue"
- x-on:livewireUpdating="$event.target.value = value"
- x-on:livewireUpdated="$nextTick(() => { value = $event.detail.value; updateValue(); })"
- placeholder="Enter LaTeX code...
- Example:
- $f(x) = ax^2 + bx + c$
- $\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}$"
- rows="{{ $getMaxHeight() ? min($getMaxHeight(), 20) : 8 }}"
- {{ $attributes->merge($getExtraAttributes())->class(['filament-forms-textarea-input']) }}
- >{{ $getState() }}</textarea>
- @error($statePath)
- <p class="mt-1 text-sm text-red-600">{{ $message }}</p>
- @enderror
- @if ($hint = $getHint())
- <p class="mt-1 text-sm text-gray-500">{{ $hint }}</p>
- @endif
- </div>
- <!-- Preview Section -->
- <div class="bg-gray-50 rounded-lg p-4 border border-gray-200">
- <h4 class="text-sm font-medium text-gray-700 mb-3">Preview</h4>
- <div class="min-h-[200px] p-4 bg-white rounded border overflow-auto">
- <div class="math-render math-preview">
- @if ($getState())
- {{ $getState() }}
- @else
- <span class="text-gray-400 text-sm">Enter LaTeX code to see preview...</span>
- @endif
- </div>
- </div>
- </div>
- </div>
- </x-dynamic-component>
- @push('scripts')
- <script>
- document.addEventListener('alpine:init', () => {
- Alpine.data('mathEditor', (config = {}) => ({
- value: config.value || '',
- statePath: config.statePath || '',
- updateValue() {
- // 触发 Livewire 更新
- this.$wire.set(this.statePath, this.value);
- // 更新预览
- this.updatePreview();
- },
- updatePreview() {
- const previewElement = this.$el.closest('.math-editor-field').querySelector('.math-preview');
- if (previewElement && typeof window.renderMathElement === 'function') {
- previewElement.dataset.mathContent = this.value;
- window.renderMathElement(previewElement);
- }
- },
- init() {
- // 初始化预览
- this.$nextTick(() => {
- this.updatePreview();
- // 监听 Livewire 更新
- this.$el.addEventListener('livewire:updated', (e) => {
- this.value = e.detail.value;
- this.updatePreview();
- });
- });
- }
- }));
- });
- // 添加全局渲染函数
- window.renderMathElement = function(element) {
- if (typeof window.katex === 'undefined') {
- // 等待 KaTeX 加载
- setTimeout(() => {
- if (typeof window.renderMathElement === 'function') {
- window.renderMathElement(element);
- }
- }, 100);
- return;
- }
- const content = element.dataset.mathContent || element.textContent;
- if (!content) return;
- try {
- let html = content;
- // 渲染 $$...$$ 块级公式
- html = html.replace(/\$\$([\s\S]*?)\$\$/g, (match, formula) => {
- try {
- return window.katex.renderToString(formula.trim(), {
- throwOnError: false,
- displayMode: true
- });
- } catch (e) {
- return match;
- }
- });
- // 渲染 $...$ 行内公式
- html = html.replace(/\$(.*?)\$/g, (match, formula) => {
- try {
- return window.katex.renderToString(formula, {
- throwOnError: false,
- displayMode: false
- });
- } catch (e) {
- return match;
- }
- });
- // 渲染 \(...\) 行内公式
- html = html.replace(/\\\((.*?)\\\)/g, (match, formula) => {
- try {
- return window.katex.renderToString(formula, {
- throwOnError: false,
- displayMode: false
- });
- } catch (e) {
- return match;
- }
- });
- element.innerHTML = html;
- } catch (e) {
- console.error('Math render error:', e);
- }
- };
- // 页面加载完成后渲染所有数学元素
- document.addEventListener('DOMContentLoaded', () => {
- if (typeof window.katex === 'undefined') {
- const script = document.createElement('script');
- script.src = '/js/katex.min.js';
- script.onload = () => {
- document.querySelectorAll('.math-render').forEach(window.renderMathElement);
- };
- document.head.appendChild(script);
- } else {
- document.querySelectorAll('.math-render').forEach(window.renderMathElement);
- }
- });
- // Livewire 兼容性
- document.addEventListener('livewire:initialized', () => {
- document.querySelectorAll('.math-render').forEach(window.renderMathElement);
- });
- document.addEventListener('livewire:updated', () => {
- setTimeout(() => {
- document.querySelectorAll('.math-render').forEach(window.renderMathElement);
- }, 100);
- });
- </script>
- @endpush
- @push('styles')
- <link rel="stylesheet" href="/css/katex/katex.min.css">
- <style>
- .math-preview {
- word-wrap: break-word;
- line-height: 1.6;
- }
- .math-preview .katex {
- font-size: 1em;
- }
- </style>
- @endpush
|