Add Vault show page
Add VaultNode CRUD features Add VaultNode search feature Add TreeView component
@@ -0,0 +1,4 @@
|
||||
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 293 B |
@@ -0,0 +1,6 @@
|
||||
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25"
|
||||
stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 507 B |
@@ -0,0 +1,4 @@
|
||||
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m19.5 8.25-7.5 7.5-7.5-7.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 275 B |
@@ -0,0 +1,4 @@
|
||||
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m8.25 4.5 7.5 7.5-7.5 7.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 274 B |
@@ -0,0 +1,5 @@
|
||||
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 327 B |
@@ -0,0 +1,6 @@
|
||||
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
|
||||
stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 537 B |
@@ -0,0 +1,6 @@
|
||||
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M2.25 12.75V12A2.25 2.25 0 0 1 4.5 9.75h15A2.25 2.25 0 0 1 21.75 12v.75m-8.69-6.44-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z"
|
||||
stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 524 B |
@@ -0,0 +1,6 @@
|
||||
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 10.5v6m3-3H9m4.06-7.19-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z"
|
||||
stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 468 B |
@@ -0,0 +1,5 @@
|
||||
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 334 B |
@@ -0,0 +1,3 @@
|
||||
<div class="flex flex-grow w-full">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<li x-data="{ accordionOpen: false }" class="items-center justify-between py-0.5" {{ $attributes }}>
|
||||
{{ $slot }}
|
||||
</li>
|
||||
@@ -0,0 +1,33 @@
|
||||
@props(['node'])
|
||||
|
||||
<div class="relative w-full">
|
||||
<x-menu>
|
||||
<button x-ref="button" @click="$dispatch('file-open', {'node': {{ $node->id }}})"
|
||||
@contextmenu.prevent="menuOpen = !menuOpen" @keydown.escape="menuOpen = false"
|
||||
@auxclick.outside="menuOpen = false" class="flex items-center w-full">
|
||||
<span class="flex items-center w-full gap-2">
|
||||
<span title="{{ $node->name }}" class="ml-1 overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
{{ $node->name }}
|
||||
</span>
|
||||
@if ($node->extension !== 'md')
|
||||
<x-treeView.badge>{{ $node->extension }}</x-treeView.badge>
|
||||
@endif
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<x-menu.items>
|
||||
<x-menu.close>
|
||||
<x-menu.item @click="$wire.dispatchTo('modals.edit-node', 'open-modal', { node: {{ $node->id }} })">
|
||||
<x-icons.pencilSquare class="w-4 h-4" />
|
||||
{{ __('Rename') }}
|
||||
</x-menu.item>
|
||||
|
||||
<x-menu.item wire:confirm="{{ __('Are you sure you want to delete this file?') }}"
|
||||
wire:click="deleteNode({{ $node->id }})">
|
||||
<x-icons.trash class="w-4 h-4" />
|
||||
{{ __('Delete') }}
|
||||
</x-menu.item>
|
||||
</x-menu.close>
|
||||
</x-menu.items>
|
||||
</x-menu>
|
||||
</div>
|
||||
@@ -0,0 +1,45 @@
|
||||
@props(['node'])
|
||||
|
||||
<div class="relative w-full">
|
||||
<x-menu>
|
||||
<button x-ref="button" @click="accordionOpen = !accordionOpen" @contextmenu.prevent="menuOpen = !menuOpen"
|
||||
@keydown.escape="menuOpen = false" @auxclick.outside="menuOpen = false" class="flex items-center w-full">
|
||||
<span class="flex items-center w-full">
|
||||
<x-icons.chevronRight x-show="!accordionOpen" class="w-4 h-4" />
|
||||
<x-icons.chevronDown x-show="accordionOpen" class="w-4 h-4" x-cloak />
|
||||
|
||||
<span title="{{ $node->name }}" class="ml-1 overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
{{ $node->name }}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<x-menu.items>
|
||||
<x-menu.close>
|
||||
<x-menu.item
|
||||
@click="$wire.dispatchTo('modals.add-node', 'open-modal', { parent: {{ $node->id }} })">
|
||||
<x-icons.documentPlus class="w-4 h-4" />
|
||||
|
||||
{{ __('New note') }}
|
||||
</x-menu.item>
|
||||
|
||||
<x-menu.item
|
||||
@click="$wire.dispatchTo('modals.add-node', 'open-modal', { parent: {{ $node->id }}, isFile: false })">
|
||||
<x-icons.folderPlus class="w-4 h-4" />
|
||||
{{ __('New folder') }}
|
||||
</x-menu.item>
|
||||
|
||||
<x-menu.item @click="$wire.dispatchTo('modals.edit-node', 'open-modal', { node: {{ $node->id }} })">
|
||||
<x-icons.pencilSquare class="w-4 h-4" />
|
||||
{{ __('Rename') }}
|
||||
</x-menu.item>
|
||||
|
||||
<x-menu.item wire:confirm="{{ __('Are you sure you want to delete this folder?') }}"
|
||||
wire:click="deleteNode({{ $node->id }})">
|
||||
<x-icons.trash class="w-4 h-4" />
|
||||
{{ __('Delete') }}
|
||||
</x-menu.item>
|
||||
</x-menu.close>
|
||||
</x-menu.items>
|
||||
</x-menu>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
@props(['root'])
|
||||
|
||||
<ul @unless ($root)
|
||||
x-show="accordionOpen" x-collapse x-cloak
|
||||
@endunless
|
||||
class="relative w-full pl-4 first:pl-0">
|
||||
{{ $slot }}
|
||||
</ul>
|
||||
@@ -0,0 +1,7 @@
|
||||
@props(['nodes', 'root'])
|
||||
|
||||
<x-treeView.items :root="$root">
|
||||
@foreach ($nodes as $node)
|
||||
<x-vault.treeViewRow :$node />
|
||||
@endforeach
|
||||
</x-treeView.items>
|
||||
@@ -0,0 +1,13 @@
|
||||
@props(['node'])
|
||||
|
||||
<x-treeView.item>
|
||||
@if (!$node->is_file)
|
||||
<x-treeView.itemFolder :$node />
|
||||
|
||||
@if (!empty($node->children) && $node->children->count())
|
||||
@include('components.vault.treeViewNode', ['nodes' => $node->children, 'root' => false])
|
||||
@endif
|
||||
@else
|
||||
<x-treeView.itemFile :$node />
|
||||
@endif
|
||||
</x-treeView.item>
|
||||
@@ -0,0 +1,11 @@
|
||||
<x-modal wire:model="show">
|
||||
<x-modal.panel title="{{ $form->is_file ? __('New note') : __('New folder') }}">
|
||||
<x-form wire:submit="add" class="flex flex-col gap-6">
|
||||
<x-form.input name="form.name" placeholder="{{ __('Name') }}" type="name" required autofocus />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<x-form.submit label="{{ __('Add') }}" target="add" />
|
||||
</div>
|
||||
</x-form>
|
||||
</x-modal.panel>
|
||||
</x-modal>
|
||||
@@ -0,0 +1,11 @@
|
||||
<x-modal wire:model="show">
|
||||
<x-modal.panel title="{{ $form->is_file ? __('Rename file') : __('Rename folder') }}">
|
||||
<x-form wire:submit="edit" class="flex flex-col gap-6">
|
||||
<x-form.input name="form.name" placeholder="{{ __('Name') }}" type="name" required autofocus />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<x-form.submit label="{{ __('Edit') }}" target="edit" />
|
||||
</div>
|
||||
</x-form>
|
||||
</x-modal.panel>
|
||||
</x-modal>
|
||||
@@ -0,0 +1,30 @@
|
||||
<x-modal wire:model="show">
|
||||
<x-modal.panel title="Search" top>
|
||||
<input type="text" wire:model.live.debounce.500ms="search" placeholder="{{ __('Search') }}" autofocus
|
||||
class="block w-full p-2 border rounded-lg bg-light-base-100 dark:bg-base-800 text-light-base-700 dark:text-base-200 focus:ring-0 focus:outline focus:outline-0 border-light-base-300 dark:border-base-500 focus:border-light-base-600 dark:focus:border-base-400" />
|
||||
|
||||
<div class="mt-4">
|
||||
@if (count($nodes))
|
||||
<ul wire:loading.class="opacity-50">
|
||||
@foreach ($nodes as $node)
|
||||
<li>
|
||||
<button type="button" wire:click="$parent.openFile({{ $node->id }}); modalOpen = false"
|
||||
class="flex w-full gap-2 py-1 text-left">
|
||||
<span title="{{ $node->name }}"
|
||||
class="overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
{{ $node->full_path }}
|
||||
</span>
|
||||
|
||||
@if ($node->extension !== 'md')
|
||||
<x-treeView.badge>{{ $node->extension }}</x-treeView.badge>
|
||||
@endif
|
||||
</button>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@else
|
||||
<p>{{ __('No results found') }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</x-modal.panel>
|
||||
</x-modal>
|
||||
@@ -0,0 +1,234 @@
|
||||
<div class="flex flex-col h-dvh">
|
||||
<x-layouts.appHeader>
|
||||
<div class="flex items-center gap-4">
|
||||
<button type="button" @click="$dispatch('sidebar-left-toggle')">
|
||||
<x-icons.folder class="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
<button type="button" @click="$wire.dispatchTo('modals.search-node', 'open-modal')">
|
||||
<x-icons.magnifyingGlass class="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<livewire:layout.user-menu />
|
||||
</div>
|
||||
</x-layouts.appHeader>
|
||||
|
||||
<x-layouts.appMain>
|
||||
<div x-data="{
|
||||
isSidebarOpen: false,
|
||||
isEditMode: $wire.entangle('isEditMode'),
|
||||
selectedFile: $wire.entangle('selectedFile'),
|
||||
html: '',
|
||||
toggleEditMode() { this.isEditMode = !this.isEditMode },
|
||||
}" x-init="$watch('isEditMode', value => html = markdown())
|
||||
$watch('selectedFile', value => html = markdown())" x-cloak
|
||||
@sidebar-left-toggle.window="isSidebarOpen = !isSidebarOpen" class="relative flex w-full">
|
||||
<div wire:loading wire:target.except="nodeForm.name, nodeForm.content"
|
||||
class="fixed inset-0 z-40 bg-black bg-opacity-40"></div>
|
||||
<div x-show="false" @click="isSidebarOpen = false" class="fixed inset-0 z-20 bg-black bg-opacity-50"
|
||||
x-transition:enter="ease-out duration-300" x-transition:leave="ease-in duration-200"></div>
|
||||
|
||||
<div class="absolute top-0 left-0 z-[5] flex flex-col h-full overflow-hidden overflow-y-auto transition-all w-60 bg-light-base-200 dark:bg-base-950"
|
||||
:class="{ 'translate-x-0': isSidebarOpen, '-translate-x-full hidden': !isSidebarOpen }">
|
||||
<div class="sticky top-0 z-[5] flex justify-between p-4 bg-light-base-200 dark:bg-base-950">
|
||||
<h3>{{ $vault->name }}</h3>
|
||||
|
||||
<div class="flex items-center">
|
||||
<x-menu>
|
||||
<x-menu.button>
|
||||
<x-icons.bars3 class="w-5 h-5" />
|
||||
</x-menu.button>
|
||||
|
||||
<x-menu.items>
|
||||
<x-menu.close>
|
||||
<x-menu.item @click="$wire.dispatchTo('modals.add-node', 'open-modal')">
|
||||
<x-icons.documentPlus class="w-4 h-4" />
|
||||
|
||||
{{ __('New note') }}
|
||||
</x-menu.item>
|
||||
|
||||
<x-menu.item
|
||||
@click="$wire.dispatchTo('modals.add-node', 'open-modal', { isFile: false })">
|
||||
<x-icons.folderPlus class="w-4 h-4" />
|
||||
{{ __('New folder') }}
|
||||
</x-menu.item>
|
||||
|
||||
<x-modal wire:model="showEditModal">
|
||||
<x-modal.open>
|
||||
<x-menu.item>
|
||||
<x-icons.pencilSquare class="w-4 h-4" />
|
||||
|
||||
{{ __('Edit vault') }}
|
||||
</x-menu.item>
|
||||
</x-modal.open>
|
||||
|
||||
<x-modal.panel title="{{ __('Edit vault') }}">
|
||||
<x-form wire:submit="update" class="flex flex-col gap-6">
|
||||
<x-form.input name="form.name" label="{{ __('Name') }}"
|
||||
type="name" required autofocus />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<x-form.submit label="{{ __('Edit') }}" target="edit" />
|
||||
</div>
|
||||
</x-form>
|
||||
</x-modal.panel>
|
||||
</x-modal>
|
||||
|
||||
<x-menu.itemLink href="/vaults" wire:navigate>
|
||||
<x-icons.xMark class="w-4 h-4" />
|
||||
|
||||
Close vault
|
||||
</x-menu.item>
|
||||
</x-menu.close>
|
||||
</x-menu.items>
|
||||
</x-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<livewire:vault.tree-view :$vault />
|
||||
</div>
|
||||
|
||||
<div class="absolute top-0 bottom-0 right-0 flex flex-col w-full overflow-y-auto transition-all text-start md:pl-60"
|
||||
:class="{ 'md:pl-60': isSidebarOpen, '': !isSidebarOpen }">
|
||||
@if ($selectedFile)
|
||||
<div class="sticky top-0 z-[5] p-4 bg-light-base-50 dark:bg-base-900">
|
||||
<div class="flex justify-between">
|
||||
<input type="text" wire:model.live.debounce.500ms="nodeForm.name"
|
||||
class="flex flex-grow p-0 text-lg bg-transparent border-0 focus:ring-0 focus:outline-0" />
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<span wire:loading.flex wire:target="nodeForm.name, nodeForm.content"
|
||||
class="flex items-center">
|
||||
<x-icons.spinner class="w-4 h-4 animate-spin" />
|
||||
</span>
|
||||
|
||||
@if ($nodeForm->extension == 'md')
|
||||
<button type="button" x-show="isEditMode" @click="toggleEditMode"
|
||||
title="{{ __('Click to read') }}">
|
||||
<x-icons.bookOpen class="w-5 h-5" />
|
||||
</button>
|
||||
<button type="button" x-show="!isEditMode" @click="toggleEditMode"
|
||||
title="{{ __('Click to edit') }}">
|
||||
<x-icons.codeBracket class="w-5 h-5" />
|
||||
</button>
|
||||
@endif
|
||||
|
||||
<button type="button" wire:click="closeFile" title="{{ __('Close file') }}">
|
||||
<x-icons.xMark class="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@error('nodeForm.name')
|
||||
<p class="text-sm text-error-500" aria-live="assertive">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="flex flex-grow px-4">
|
||||
@if ($nodeForm->extension == 'md')
|
||||
<textarea wire:model.live.debounce.500ms="nodeForm.content" x-show="isEditMode" id="noteEdit"
|
||||
data-id="{{ $selectedFile }}" class="w-full h-full p-0 bg-transparent border-0 focus:ring-0 focus:outline-0"></textarea>
|
||||
|
||||
<div x-show="!isEditMode" x-html="html" id="noteView" class="w-full h-full markdown-body">
|
||||
</div>
|
||||
@elseif (in_array($nodeForm->extension, ['jpg', 'jpeg', 'png', 'gif']))
|
||||
<div>
|
||||
<img src="{{ $selectedFilePath }}" />
|
||||
</div>
|
||||
@elseif (in_array($nodeForm->extension, ['pdf']))
|
||||
<object type="application/pdf" data="{{ $selectedFilePath }}"
|
||||
class="w-full h-full"></object>
|
||||
@elseif (in_array($nodeForm->extension, ['webp', 'mp4', 'avi']))
|
||||
<video class="w-full" controls>
|
||||
<source src="{{ $selectedFilePath }}" />
|
||||
{{ __('Your browser does not support the video tag') }}
|
||||
</video>
|
||||
@elseif (in_array($nodeForm->extension, ['mp3', 'flac']))
|
||||
<div class="flex items-start justify-center w-full">
|
||||
<audio class="w-full" controls>
|
||||
<source src="{{ $selectedFilePath }}">
|
||||
{{ __('Your browser does not support the audio tag') }}
|
||||
</audio>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@else
|
||||
<div class="flex items-center justify-center w-full h-full gap-2">
|
||||
<x-form.button @click="$wire.dispatchTo('modals.search-node', 'open-modal')">
|
||||
<x-icons.magnifyingGlass class="w-4 h-4" />
|
||||
<span class="hidden text-sm font-medium md:block">{{ __('Open file') }}</span>
|
||||
</x-form.button>
|
||||
|
||||
<x-form.button primary @click="$wire.dispatchTo('modals.add-node', 'open-modal')">
|
||||
<x-icons.plus class="w-4 h-4" />
|
||||
<span class="hidden text-sm font-medium md:block">{{ __('New note') }}</span>
|
||||
</x-form.button>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-layouts.appMain>
|
||||
|
||||
<livewire:modals.add-node :$vault />
|
||||
<livewire:modals.edit-node :$vault />
|
||||
<livewire:modals.search-node :$vault />
|
||||
</div>
|
||||
|
||||
@assets
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
@endassets
|
||||
|
||||
<script>
|
||||
let markedRender = new marked.Renderer;
|
||||
markedRender.parser = new marked.Parser;
|
||||
let renderListitem = markedRender.listitem.bind(markedRender);
|
||||
|
||||
function markdown() {
|
||||
let el = document.getElementById('noteEdit');
|
||||
let markdown = '';
|
||||
|
||||
if (!el) {
|
||||
return markdown;
|
||||
}
|
||||
|
||||
renderer = {
|
||||
image(token) {
|
||||
// external images
|
||||
if (token.href.startsWith('http://') || token.href.startsWith('https://')) {
|
||||
return '<img src="' + token.href + '" alt="' + token.text + '" />';
|
||||
}
|
||||
|
||||
// internal images
|
||||
return '<img src="/files/{{ $vault->id }}?path=' + token.href + '&node=' + node + '" alt="' +
|
||||
token.text + '" />';
|
||||
},
|
||||
link(token) {
|
||||
// external links
|
||||
if (token.href.startsWith('http://') || token.href.startsWith('https://')) {
|
||||
return '<a href="' + token.href + '" target="_blank">' + token.text + '</a>';
|
||||
}
|
||||
|
||||
// internal links
|
||||
return '<a href="" wire:click.prevent="openFilePath(\'' + token.href + '\')">' + token.text +
|
||||
'</a>';
|
||||
},
|
||||
listitem(token) {
|
||||
let html = renderListitem(token);
|
||||
|
||||
if (token.task) {
|
||||
html = html.replace('<li>', '<li class="task-list-item">');
|
||||
html = html.replace('<input ', '<input class="task-list-item-checkbox" ');
|
||||
}
|
||||
|
||||
return html;
|
||||
},
|
||||
};
|
||||
marked.use({
|
||||
renderer
|
||||
});
|
||||
markdown = marked.parse(el.value);
|
||||
|
||||
return markdown;
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,9 @@
|
||||
<div class="flex flex-grow px-4">
|
||||
<x-treeView>
|
||||
@if (count($nodes))
|
||||
@include('components.vault.treeViewNode', ['nodes' => $nodes, 'root' => true])
|
||||
@else
|
||||
<p>{{ __('Your vault is empty.') }}</p>
|
||||
@endif
|
||||
</x-treeView>
|
||||
</div>
|
||||