Skip to content

Commit

Permalink
feat: added modal links feature
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas Frey <mail@lukasfrey.cz>
  • Loading branch information
lukas-frey committed May 29, 2024
1 parent 4c11168 commit 04a12a5
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 0 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,36 @@ reference the documentation you linked.

![Documentation button example](https://github.com/GuavaCZ/filament-knowledge-base/raw/main/docs/images/screenshot_help_menu.png)

#### Opening documentations in modals
From any livewire component where you use the documentation pages (you have rendered the Help Menu), you can create links that will automatically open the documentation in a modal, by simply adding this fragment to the `href` attribute of the link:

```html
#modal-<documentation-id>
```

such as
```html
<a href="#modal-users.introduction">Open Introduction</a>
```

### Modal Links
To make it easy to access the documentation from anywhere, this plugin adds intercepts fragment links anywhere in the filament panel in order to open up a modal for a documentation page.

To use modal links, simply add a link in **any place** in your panel with a fragment in the format `#modal-<documentation-id>`, such as `#modal-intro.getting-started`, for example:
```html
<a href="#modal-intro.getting-started">Open Introduction</a>
```

As long as a documentation with that ID exists (/docs/en/intro/getting-started.md), it will automatically open a modal with the content of that documentation.

You can even share the URL with someone and it will automatically open the modal upon opening!

![Modal links example](https://github.com/GuavaCZ/filament-knowledge-base/raw/main/docs/images/screenshot_modal_links.gif)


#### Disabling Modal Links
to disable modal links, simply call `disableModalLinks()` on the KnowledgeBasePlugin in your panel Service Provider..

### Help Actions

The plugin comes with a neat `HelpAction`, which can be linked to a specific markdown file or even a partial markdown
Expand Down
6 changes: 6 additions & 0 deletions bin/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,9 @@ compile({
entryPoints: ['./resources/js/anchors-component.js'],
outfile: './dist/js/anchors-component.js',
})

compile({
...defaultOptions,
entryPoints: ['./resources/js/modals-component.js'],
outfile: './dist/js/modals-component.js',
})
23 changes: 23 additions & 0 deletions dist/js/modals-component.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added docs/images/screenshot_modal_links.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions resources/js/modals-component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export default function modalsComponent() {
return {

init: async function () {
this.hashChanged();
window.addEventListener('hashchange', ()=>this.hashChanged());
},

hashChanged: function() {
const fragment = location.hash.substring(1);
const prefix = 'modal-';

if (fragment.startsWith(prefix)) {
const modal = fragment.substring(fragment.indexOf(prefix) + prefix.length);

console.log('Open modal via wire: ', modal);
this.$wire.showDocumentation(modal);
// window.dispatchEvent(new CustomEvent('open-modal', {detail: {id: modal}}));
history.replaceState(null, null, ' ');
}
},

}

};
56 changes: 56 additions & 0 deletions resources/views/livewire/modals.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
@php
use Filament\Facades\Filament;
$hasModalPreviews = Filament::getPlugin('guava::filament-knowledge-base')->hasModalPreviews();
$hasSlideOverPreviews = Filament::getPlugin('guava::filament-knowledge-base')->hasSlideOverPreviews();
$hasModalTitleBreadcrumbs = Filament::getPlugin('guava::filament-knowledge-base')->hasModalTitleBreadcrumbs();
$target = Filament::getPlugin('guava::filament-knowledge-base')->shouldOpenDocumentationInNewTab() ? '_blank' : '_self';
$articleClass = \Guava\FilamentKnowledgeBase\Facades\KnowledgeBase::panel()->getArticleClass();
@endphp

<div
x-ignore
ax-load
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('modals-component', 'guava/filament-knowledge-base') }}"
x-data="modalsComponent()"
>
@if($documentable)
<x-filament::modal id="kb-custom-modal"
:close-by-clicking-away="true"
:close-button="true"
width="2xl"
:slide-over="$hasSlideOverPreviews"
class="[&_.fi-modal-content]:px-12 [&_.fi-modal-content]:gap-y-0"
:footer-actions-alignment="\Filament\Support\Enums\Alignment::End"
:sticky-footer="true"
:sticky-header="true">

<x-slot name="heading">
@if($hasModalTitleBreadcrumbs && !empty($documentable->getBreadcrumbs()))
{{ KnowledgeBase::breadcrumbs($documentable) }}
@else
{{ $documentable->getTitle() }}
@endif
</x-slot>

<x-filament-knowledge-base::content @class([
"gu-kb-article-modal",
$articleClass => ! empty($articleClass),
])>
{!! $documentable->getSimpleHtml() !!}
</x-filament-knowledge-base::content>
<x-slot name="footerActions">
<x-filament::button tag="a"
:href="$documentable->getUrl()"
:target="$target">
{{ __('filament-knowledge-base::translations.open-documentation') }}
</x-filament::button>
<x-filament::button color="gray"
x-on:click.prevent="$dispatch('close-modal', { id: 'kb-custom-modal' })">

{{ __('filament-knowledge-base::translations.close') }}
</x-filament::button>
</x-slot>
</x-filament::modal>
@endif
</div>
54 changes: 54 additions & 0 deletions resources/views/modals.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@php
use Filament\Facades\Filament;
$hasModalPreviews = Filament::getPlugin('guava::filament-knowledge-base')->hasModalPreviews();
$hasSlideOverPreviews = Filament::getPlugin('guava::filament-knowledge-base')->hasSlideOverPreviews();
$hasModalTitleBreadcrumbs = Filament::getPlugin('guava::filament-knowledge-base')->hasModalTitleBreadcrumbs();
$target = Filament::getPlugin('guava::filament-knowledge-base')->shouldOpenDocumentationInNewTab() ? '_blank' : '_self';
$articleClass = \Guava\FilamentKnowledgeBase\Facades\KnowledgeBase::panel()->getArticleClass();
@endphp

<div
x-ignore
ax-load
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('modals-component', 'guava/filament-knowledge-base') }}"
x-data="modalsComponent()"
>
<x-filament::modal id="kb-custom-modal"
:close-by-clicking-away="true"
:close-button="true"
width="2xl"
:slide-over="$hasSlideOverPreviews"
class="[&_.fi-modal-content]:px-12 [&_.fi-modal-content]:gap-y-0"
:footer-actions-alignment="\Filament\Support\Enums\Alignment::End"
:sticky-footer="true"
:sticky-header="true">

<x-slot name="heading">
@if($hasModalTitleBreadcrumbs && !empty($documentable->getBreadcrumbs()))
{{ KnowledgeBase::breadcrumbs($documentable) }}
@else
{{ $documentable->getTitle() }}
@endif
</x-slot>

<x-filament-knowledge-base::content @class([
"gu-kb-article-modal",
$articleClass => ! empty($articleClass),
])>
{!! $documentable->getSimpleHtml() !!}
</x-filament-knowledge-base::content>
<x-slot name="footerActions">
<x-filament::button tag="a"
:href="$documentable->getUrl()"
:target="$target">
{{ __('filament-knowledge-base::translations.open-documentation') }}
</x-filament::button>
<x-filament::button color="gray"
x-on:click.prevent="$dispatch('close-modal', { id: '{{$documentable->getId()}}' })">

{{ __('filament-knowledge-base::translations.close') }}
</x-filament::button>
</x-slot>
</x-filament::modal>
</div>
20 changes: 20 additions & 0 deletions src/Concerns/CanDisableModalLinks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Guava\FilamentKnowledgeBase\Concerns;

trait CanDisableModalLinks
{
protected bool $disableModalLinks = false;

public function disableModalLinks(bool $condition = true): static
{
$this->disableModalLinks = $condition;

return $this;
}

public function shouldDisableModalLinks(): bool
{
return $this->disableModalLinks;
}
}
9 changes: 9 additions & 0 deletions src/KnowledgeBasePlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
use Filament\Panel;
use Filament\View\PanelsRenderHook;
use Guava\FilamentKnowledgeBase\Concerns\CanDisableKnowledgeBasePanelButton;
use Guava\FilamentKnowledgeBase\Concerns\CanDisableModalLinks;
use Guava\FilamentKnowledgeBase\Concerns\HasModalPreviews;
use Illuminate\Support\Facades\Blade;

class KnowledgeBasePlugin implements Plugin
{
use CanDisableKnowledgeBasePanelButton;
use CanDisableModalLinks;
use HasModalPreviews;

protected bool $modalTitleBreadcrumbs = false;
Expand Down Expand Up @@ -68,6 +70,13 @@ public function register(Panel $panel): void
$this->getHelpMenuRenderHook(),
fn (): string => Blade::render('@livewire(\'help-menu\')'),
)
->when(
! $this->shouldDisableModalLinks(),
fn (Panel $panel) => $panel->renderHook(
PanelsRenderHook::BODY_END,
fn (): string => Blade::render('@livewire(\'modals\')'),
)
)
->when(
! $this->shouldDisableKnowledgeBasePanelButton(),
fn (Panel $panel) => $panel
Expand Down
6 changes: 6 additions & 0 deletions src/KnowledgeBaseServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Filament\Support\Facades\FilamentAsset;
use Guava\FilamentKnowledgeBase\Commands\MakeDocumentationCommand;
use Guava\FilamentKnowledgeBase\Livewire\HelpMenu;
use Guava\FilamentKnowledgeBase\Livewire\Modals;
use Guava\FilamentKnowledgeBase\Providers\KnowledgeBasePanelProvider;
use Livewire\Livewire;
use Spatie\LaravelPackageTools\Package;
Expand Down Expand Up @@ -38,13 +39,18 @@ public function packageRegistered(): void
public function packageBooted(): void
{
Livewire::component('help-menu', HelpMenu::class);
Livewire::component('modals', Modals::class);

FilamentAsset::register(
assets: [
AlpineComponent::make(
'anchors-component',
__DIR__ . '/../dist/js/anchors-component.js',
),
AlpineComponent::make(
'modals-component',
__DIR__ . '/../dist/js/modals-component.js',
),
],
package: 'guava/filament-knowledge-base'
);
Expand Down
59 changes: 59 additions & 0 deletions src/Livewire/Modals.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Guava\FilamentKnowledgeBase\Livewire;

use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Facades\Filament;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Guava\FilamentKnowledgeBase\Contracts\Documentable;
use Guava\FilamentKnowledgeBase\Facades\KnowledgeBase;
use Livewire\Attributes\On;
use Livewire\Component;

class Modals extends Component implements HasActions, HasForms
{
use InteractsWithActions;
use InteractsWithForms;

public ?Documentable $documentable = null;
protected bool $shouldOpenDocumentationInNewTab;

public function mount(): void
{
$this->shouldOpenDocumentationInNewTab = Filament::getPlugin('guava::filament-knowledge-base')->shouldOpenDocumentationInNewTab();
}

#[On('close-modal')]
public function onClose($id) {
if ($id !== 'kb-custom-modal') {
return;
}
$this->js(<<<JS
\$nextTick(() => {
\$wire.resetDocumentation();
});
JS);
}

public function showDocumentation(string $id)
{
$this->documentable = KnowledgeBase::documentable($id);

$this->js(<<<JS
\$nextTick(() => {
\$dispatch('open-modal', { id: 'kb-custom-modal' });
});
JS);
}

public function resetDocumentation() {
$this->documentable = null;
}

public function render()
{
return view('filament-knowledge-base::livewire.modals');
}
}

0 comments on commit 04a12a5

Please sign in to comment.