Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Plotly Chart Editor Laravel Package

uneca/plotly-chart-editor

Reactive Plotly.js chart builder for Laravel via Livewire. Sidebar-driven editor to configure traces and layout, multi-language UI (EN/FR/PT/ES), multiple sync modes and persistence options. Requires Plotly.js 3.x (peer dep), Alpine, PHP 8.4+.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require uneca/plotly-chart-editor
    npm install  # Ensure Plotly.js is available (peer dependency)
    
  2. Publish Config:

    php artisan vendor:publish --provider="Uneca\PlotlyChartEditor\PlotlyChartEditorServiceProvider" --tag="config"
    
    • Edit config/plotly-chart-editor.php to define your chart profiles (e.g., scatter, bar).
  3. First Use Case:

    <livewire:plotly-editor
        :chart-profiles="['scatter']"
        wire:model="chartConfig"
        sync-mode="hybrid"
    />
    
    • Bind a Livewire model (chartConfig) to persist state server-side.

Implementation Patterns

Core Workflow

  1. Define Profiles: Extend config/plotly-chart-editor.php to add custom trace types (e.g., pie):

    'profiles' => [
        'pie' => [
            'groups' => [
                'data' => ['title', 'values', 'labels'],
                'style' => ['hole', 'pull'],
            ],
        ],
    ],
    
  2. Dynamic Field Binding: Use x-model in Blade to sync Alpine state to Livewire:

    <x-plotly-chart-editor::schema-field
        type="number"
        x-model="chartBuilder.config.traces[0].pull"
    />
    
  3. Sync Strategies:

    • Auto: Debounced 50ms updates (default).
    • Manual: Add a Save button:
      <button @click="chartBuilder.syncToBackend()">Save</button>
      
    • Hybrid: Combine both (recommended for complex charts).
  4. Data Integration: Fetch data via Livewire properties and pass to Alpine:

    public $dataset = []; // Loaded via Livewire mount()
    
    <script>
        Alpine.store('chartBuilder').dataset = @this.dataset;
    </script>
    

Advanced Patterns

  • Custom Fields: Extend resources/js/plotly-chart-editor.js to add field types (e.g., color-picker):

    Alpine.data('colorPicker', () => ({
        init() {
            this.$watch('modelValue', (val) => {
                this.$el.style.backgroundColor = val;
            });
        },
    }));
    

    Register in Blade:

    <x-plotly-chart-editor::schema-field type="color-picker" />
    
  • Theme Overrides: Override Tailwind tokens in your app’s CSS:

    @layer theme {
        --plotly-editor-primary: #3b82f6;
    }
    
  • Server-Side Rendering: Use wire:ignore for the Plotly canvas to avoid Livewire re-renders:

    <div wire:ignore id="plotly-canvas"></div>
    

Gotchas and Tips

Common Pitfalls

  1. State Mismatch:

    • Issue: Alpine and Livewire states diverge after rapid edits.
    • Fix: Force a sync with chartBuilder.syncToBackend() or reset _lastStructuralSig in Alpine:
      Alpine.store('chartBuilder')._lastStructuralSig = Date.now();
      
  2. Plotly Initialization:

    • Issue: Plotly.newPlot() fails if the DOM element isn’t ready.
    • Fix: Use Alpine’s x-init to defer initialization:
      <div x-init="setTimeout(() => Plotly.newPlot('plotly-canvas', data), 100)"></div>
      
  3. Deep Copy Quirks:

    • Issue: JSON.parse(JSON.stringify()) flattens Date objects.
    • Fix: Pre-process dates in Alpine:
      const deepCopy = (obj) => JSON.parse(JSON.stringify(obj, (_, v) =>
          v instanceof Date ? v.toISOString() : v
      ));
      
  4. Tailwind Conflicts:

    • Issue: Custom CSS overrides Tailwind classes.
    • Fix: Use !important sparingly and prefix classes (e.g., pe- for package-specific).

Debugging Tips

  • Alpine State Inspection: Add a debug panel:
    <pre x-text="JSON.stringify(Alpine.store('chartBuilder'), null, 2)"></pre>
    
  • Livewire Logs: Enable Livewire logging in config/livewire.php:
    'log' => env('APP_DEBUG', false),
    

Extension Points

  1. Custom Sync Logic: Override syncToBackend() in Alpine by extending the store:

    Alpine.store('chartBuilder').syncToBackend = function() {
        // Custom logic (e.g., validation)
        this.$wire.call('customSync', this.config);
    };
    
  2. Plotly Events: Listen to Plotly events (e.g., plotly_relayout) in Alpine:

    document.addEventListener('plotly_relayout', (e) => {
        Alpine.store('chartBuilder').config.layout = e.details.layout;
    });
    
  3. Server-Side Validation: Validate incoming data in Livewire:

    protected function rules() {
        return [
            'chartConfig.traces.*.type' => 'required|in:scatter,bar,pie',
        ];
    }
    

Configuration Quirks

  • Sync Mode Priority: manual overrides auto, but hybrid merges both (auto + manual button).
  • Profile Dependencies: Fields marked as required in the schema will show validation errors if omitted.
  • Alpine Store Isolation: Avoid polluting the global Alpine.store() namespace; use chartBuilder exclusively.

```markdown
## Performance Considerations
- **Large Datasets**: Use `Plotly.react()` for incremental updates instead of `newPlot()`.
- **Memory Leaks**: Clear Alpine stores on component unmount:
  ```js
  window.addEventListener('livewire:init', () => {
      Livewire.hook('component.updated', ({ component }) => {
          if (component.$id === 'plotly-editor') {
              component.$wire.on('chartUpdated', () => {
                  Alpine.store('chartBuilder').reset();
              });
          }
      });
  });

Testing Strategies

  • Unit Tests: Mock Alpine store interactions:
    $this->mock(Alpine::class, function ($mock) {
        $mock->shouldReceive('store')->andReturn(['chartBuilder' => ['config' => []]]);
    });
    
  • E2E Tests: Use Playwright to test UI interactions:
    $page->fill('#chart-title', 'Test Chart');
    $page->click('text=Save');
    $this->assertDatabaseHas('charts', ['title' => 'Test Chart']);
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nasirkhan/laravel-sharekit
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony