Skip to content

Commit cea527f

Browse files
committed
Add interactive LoadingIndicator playground for JavaScript frameworks
1 parent f6cef9c commit cea527f

13 files changed

Lines changed: 334 additions & 0 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace App\Filament\Pages;
4+
5+
use App\Filament\Widgets\LoadingIndicatorBlade;
6+
use App\Filament\Widgets\LoadingIndicatorReact;
7+
use App\Filament\Widgets\LoadingIndicatorSvelte;
8+
use App\Filament\Widgets\LoadingIndicatorVue;
9+
use Filament\Pages\Dashboard;
10+
11+
class LoadingIndicatorPlayground extends Dashboard
12+
{
13+
protected static string $routePath = 'loading-indicator-playground';
14+
15+
protected static ?string $title = 'Loading indicator playground';
16+
17+
public function getWidgets(): array
18+
{
19+
return [LoadingIndicatorBlade::class, LoadingIndicatorReact::class, LoadingIndicatorVue::class, LoadingIndicatorSvelte::class];
20+
}
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace App\Filament\Widgets;
4+
5+
use Filament\Widgets\Widget;
6+
7+
class LoadingIndicatorBlade extends Widget
8+
{
9+
protected string $view = 'filament.widgets.loading-indicator-blade';
10+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace App\Filament\Widgets;
4+
5+
use Filament\Widgets\Concerns\HasJsRenderer;
6+
use Filament\Widgets\Widget;
7+
use Illuminate\Support\Facades\Vite;
8+
9+
class LoadingIndicatorReact extends Widget
10+
{
11+
use HasJsRenderer;
12+
13+
public function getRenderer(): string
14+
{
15+
return Vite::asset('resources/js/filament/widgets/loading-indicator-react.jsx');
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace App\Filament\Widgets;
4+
5+
use Filament\Widgets\Concerns\HasJsRenderer;
6+
use Filament\Widgets\Widget;
7+
use Illuminate\Support\Facades\Vite;
8+
9+
class LoadingIndicatorSvelte extends Widget
10+
{
11+
use HasJsRenderer;
12+
13+
public function getRenderer(): string
14+
{
15+
return Vite::asset('resources/js/filament/widgets/loading-indicator-svelte.svelte.js');
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace App\Filament\Widgets;
4+
5+
use Filament\Widgets\Concerns\HasJsRenderer;
6+
use Filament\Widgets\Widget;
7+
use Illuminate\Support\Facades\Vite;
8+
9+
class LoadingIndicatorVue extends Widget
10+
{
11+
use HasJsRenderer;
12+
13+
public function getRenderer(): string
14+
{
15+
return Vite::asset('resources/js/filament/widgets/loading-indicator-vue.js');
16+
}
17+
}

‎resources/css/filament/admin/theme.css‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,31 @@
7979
.fieldset-demo-accent {
8080
@apply bg-primary-50 dark:bg-primary-950;
8181
}
82+
83+
.loading-indicator-demo {
84+
@apply grid gap-4;
85+
86+
& p {
87+
@apply text-sm text-gray-600 dark:text-gray-300;
88+
}
89+
}
90+
91+
.loading-indicator-demo-status {
92+
@apply flex min-h-12 items-center gap-3 text-sm;
93+
}
94+
95+
.loading-indicator-demo-controls {
96+
@apply flex flex-wrap items-center gap-4 text-sm;
97+
98+
& label {
99+
@apply flex items-center gap-2;
100+
}
101+
102+
& select {
103+
@apply rounded-md border border-gray-300 bg-white p-1 text-gray-950 dark:border-gray-600 dark:bg-gray-900 dark:text-white;
104+
}
105+
}
106+
107+
.loading-indicator-demo-accent {
108+
@apply text-primary-600 dark:text-primary-400;
109+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script>
2+
import LoadingIndicator from '../../../../vendor/filament/support/resources/js/svelte/LoadingIndicator.svelte'
3+
4+
let size = $state('md')
5+
let loading = $state(true)
6+
let accent = $state(false)
7+
</script>
8+
9+
<div class="loading-indicator-demo" data-framework="svelte">
10+
<h2 class="fi-section-header-heading">Svelte · JavaScript</h2>
11+
<div role="status" class="loading-indicator-demo-status">
12+
{#if loading}<LoadingIndicator
13+
{size}
14+
class={accent ? 'loading-indicator-demo-accent' : undefined}
15+
/>{/if}
16+
<span
17+
>{loading ? 'Preparing your report…' : 'Your report is ready'}</span
18+
>
19+
</div>
20+
<div class="loading-indicator-demo-controls">
21+
<label
22+
>Size <select bind:value={size}
23+
>{#each ['xs', 'sm', 'md', 'lg', 'xl', '2xl'] as size}<option
24+
>{size}</option
25+
>{/each}</select
26+
></label
27+
>
28+
<label><input type="checkbox" bind:checked={loading} /> Loading</label>
29+
<label
30+
><input type="checkbox" bind:checked={accent} /> Theme accent</label
31+
>
32+
</div>
33+
<button
34+
type="button"
35+
class="fi-btn fi-size-sm"
36+
onclick={() => {
37+
size = 'md'
38+
loading = true
39+
accent = false
40+
}}>Reset</button
41+
>
42+
</div>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<script setup>
2+
import { ref } from 'vue'
3+
import LoadingIndicator from '../../../../vendor/filament/support/resources/js/vue/LoadingIndicator.vue'
4+
5+
const size = ref('md')
6+
const loading = ref(true)
7+
const accent = ref(false)
8+
9+
function reset() {
10+
size.value = 'md'
11+
loading.value = true
12+
accent.value = false
13+
}
14+
</script>
15+
16+
<template>
17+
<div class="loading-indicator-demo" data-framework="vue">
18+
<h2 class="fi-section-header-heading">Vue · JavaScript</h2>
19+
<div role="status" class="loading-indicator-demo-status">
20+
<LoadingIndicator
21+
v-if="loading"
22+
:size="size"
23+
:class="accent ? 'loading-indicator-demo-accent' : undefined"
24+
/>
25+
<span>{{
26+
loading ? 'Preparing your report…' : 'Your report is ready'
27+
}}</span>
28+
</div>
29+
<div class="loading-indicator-demo-controls">
30+
<label
31+
>Size
32+
<select v-model="size">
33+
<option
34+
v-for="size in ['xs', 'sm', 'md', 'lg', 'xl', '2xl']"
35+
:key="size"
36+
>
37+
{{ size }}
38+
</option>
39+
</select></label
40+
>
41+
<label><input type="checkbox" v-model="loading" /> Loading</label>
42+
<label
43+
><input type="checkbox" v-model="accent" /> Theme accent</label
44+
>
45+
</div>
46+
<button type="button" class="fi-btn fi-size-sm" @click="reset">
47+
Reset
48+
</button>
49+
</div>
50+
</template>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React, { useState } from 'react'
2+
import { createRoot } from 'react-dom/client'
3+
import LoadingIndicator from '../../../../vendor/filament/support/resources/js/react/LoadingIndicator'
4+
5+
function LoadingIndicatorReact() {
6+
const [size, setSize] = useState('md')
7+
const [loading, setLoading] = useState(true)
8+
const [accent, setAccent] = useState(false)
9+
return (
10+
<div className="loading-indicator-demo" data-framework="react">
11+
<h2 className="fi-section-header-heading">React · JavaScript</h2>
12+
<div role="status" className="loading-indicator-demo-status">
13+
{loading && (
14+
<LoadingIndicator
15+
size={size}
16+
className={
17+
accent ? 'loading-indicator-demo-accent' : undefined
18+
}
19+
/>
20+
)}
21+
<span>
22+
{loading
23+
? 'Preparing your report…'
24+
: 'Your report is ready'}
25+
</span>
26+
</div>
27+
<div className="loading-indicator-demo-controls">
28+
<label>
29+
Size{' '}
30+
<select
31+
value={size}
32+
onChange={(event) => setSize(event.target.value)}
33+
>
34+
{['xs', 'sm', 'md', 'lg', 'xl', '2xl'].map((size) => (
35+
<option key={size}>{size}</option>
36+
))}
37+
</select>
38+
</label>
39+
<label>
40+
<input
41+
type="checkbox"
42+
checked={loading}
43+
onChange={(event) => setLoading(event.target.checked)}
44+
/>{' '}
45+
Loading
46+
</label>
47+
<label>
48+
<input
49+
type="checkbox"
50+
checked={accent}
51+
onChange={(event) => setAccent(event.target.checked)}
52+
/>{' '}
53+
Theme accent
54+
</label>
55+
</div>
56+
<button
57+
type="button"
58+
className="fi-btn fi-size-sm"
59+
onClick={() => {
60+
setSize('md')
61+
setLoading(true)
62+
setAccent(false)
63+
}}
64+
>
65+
Reset
66+
</button>
67+
</div>
68+
)
69+
}
70+
71+
export default function mountLoadingIndicatorReact({
72+
host,
73+
props: initialProps,
74+
}) {
75+
const root = createRoot(host)
76+
const update = (props) => root.render(<LoadingIndicatorReact {...props} />)
77+
update(initialProps)
78+
79+
return { update, destroy: () => root.unmount() }
80+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { mount, unmount } from 'svelte'
2+
import LoadingIndicatorSvelte from './LoadingIndicatorSvelte.svelte'
3+
4+
export default function mountLoadingIndicatorSvelte({
5+
host,
6+
props: initialProps,
7+
}) {
8+
const props = $state({ ...initialProps })
9+
const component = mount(LoadingIndicatorSvelte, { target: host, props })
10+
11+
return {
12+
update: (nextProps) => {
13+
Object.assign(props, nextProps)
14+
},
15+
destroy: () => unmount(component),
16+
}
17+
}

0 commit comments

Comments
 (0)