4in1_ws_web/skin/files.phps
2024-03-14 20:44:30 +00:00

249 lines
8.0 KiB
PHP

<?php
namespace skin\files;
use BookFileType;
use BookItem;
use FilesCollection;
use FilesItemInterface;
use SkinContext;
use SkinString;
use function svg;
function index($ctx,
array $collections,
array $books,
array $misc) {
return <<<HTML
{$ctx->bc([
['text' => $ctx->lang('files_archives')]
])}
<div class="files-list">
{$ctx->for_each($collections,
fn(FilesItemInterface $file) => $ctx->file(
file: $file,
disabled: !$file->isAvailable()))}
</div>
{$ctx->bc([
['text' => $ctx->lang('files_books')]
], mt: true)}
<div class="files-list">
{$ctx->for_each($books, fn(FilesItemInterface $file) => $ctx->file(file: $file))}
</div>
{$ctx->bc([
['text' => $ctx->lang('files_misc')]
], mt: true)}
<div class="files-list">
{$ctx->for_each($misc, fn(FilesItemInterface $file) => $ctx->file(file: $file))}
</div>
HTML;
}
function folder($ctx, BookItem $folder, array $files) {
$svg = svg();
$svg->folder_20(preload_symbol: true);
$svg->file_20(preload_symbol: true);
return <<<HTML
{$ctx->bc([
['text' => $ctx->lang('files'), 'url' => '/files/'],
['text' => $folder->title]
])}
<div class="files-list">
<div id="files_list">
{$ctx->collection_files($files)}
</div>
</div>
HTML;
}
function collection(SkinContext $ctx,
FilesCollection $collection,
array $files,
?array $parents,
int $search_results_per_page,
int $search_min_query_length,
?string $search_query = null,
?int $search_count = null,
?array $text_excerpts = null) {
$widgets = skin('widgets');
$svg = svg();
$svg->folder_20(preload_symbol: true);
$svg->file_20(preload_symbol: true);
$bc = [
['text' => $ctx->lang('files'), 'url' => '/files/'],
];
if ($parents) {
$bc[] = ['text' => $ctx->lang('files_'.$collection->value.'_collection_short'), 'url' => "/files/{$collection->value}/"];
for ($i = 0; $i < count($parents); $i++) {
$parent = $parents[$i];
$bc_item = ['text' => $parent->getTitle()];
if ($i < count($parents)-1)
$bc_item['url'] = $parent->getUrl();
$bc[] = $bc_item;
}
} else {
$bc[] = ['text' => $ctx->lang('files_'.$collection->value.'_collection')];
}
$do_show_search = empty($parents);
$do_show_more = $search_count > 0 && count($files) < $search_count;
$html = <<<HTML
{$ctx->bc($bc)}
{$ctx->if_true($do_show_search, fn() => $ctx->collection_search($search_count, $search_query, $ctx->lang('files_'.$collection->value.'_search_ph')))}
<div class="files-list">
<div id="files_list">
{$ctx->collection_files($files, $search_query, $text_excerpts)}
</div>
<div class="files-list-show-more no-select" id="files_show_more"{$ctx->if_not($do_show_more, ' style="display: none"')}>
<span class="files-list-show-more-label">{$ctx->lang('files_show_more')}</span>
{$widgets->spinner('files_show_more_spinner')}
</div>
</div>
<div id="files_list_hidden" style="display: none"></div>
HTML;
if ($do_show_search) {
$opts = [
'container' => 'files_list',
'per_page' => $search_results_per_page,
'min_query_length' => $search_min_query_length,
'base_url' => "/files/{$collection->value}/",
'query' => $search_query,
'count' => $search_count,
'collection_name' => $collection->value,
'inited_with_search' => !!$search_query
];
$opts = jsonEncode($opts);
$js = <<<JAVASCRIPT
cur.search = new FileSearch({$opts});
JAVASCRIPT;
return [$html, $js];
} else {
return $html;
}
}
function collection_files($ctx,
array $files,
?string $search_query = null,
?array $text_excerpts = null) {
return $ctx->for_each($files, fn(FilesItemInterface $f) => $ctx->file(
file: $f,
unsafe_query: $search_query,
text_excerpts: $text_excerpts));
}
function collection_search(SkinContext $ctx, $count, $query, ?string $placeholder = null) {
$icons = svg();
$widgets = skin('widgets');
$clear_dsp = $query ? 'block' : 'none';
return <<<HTML
<div class="files-search-wrap">
<div class="files-search" id="files_search">
<div class="files-search-icon">{$icons->search_20()}</div>
<input type="text" value="{$query}" placeholder="{$ctx->if_then_else($placeholder !== null, $placeholder, 'Enter your request..')}" id="files_search_input">
<div class="files-search-clear-icon" id="files_search_clear_icon" style="display: {$clear_dsp}">{$icons->clear_16()}</div>
</div>
<div class="files-search-results-info" id="files_search_info" style="display: {$clear_dsp}">
<div class="files-search-results-info-inner">
<div class="files-search-results-info-spinner">{$widgets->spinner()}</div>
<span id="files_search_info_text">{$ctx->if_then_else($query, fn() => $ctx->lang_num('files_search_results_count', $count), '&nbsp;')}
</div>
</div>
</div>
HTML;
}
function file(SkinContext $ctx,
FilesItemInterface $file,
?SkinString $unsafe_query = null,
bool $disabled = false,
?array $text_excerpts = null,) {
$icons = svg();
if ($file instanceof BookItem && $file->fileType == BookFileType::BOOK)
$icon = $icons->book_20();
else
$icon = $file->isFile() ? $icons->file_20() : $icons->folder_20();
$class = 'files-list-item clearfix';
if ($disabled)
$class .= ' is-disabled';
$mapper = function($s) use ($unsafe_query) {
if ($unsafe_query !== null) {
return hl_matched($s, $unsafe_query);
} else {
return htmlescape($s);
}
};
$title = $file->getTitleHtml();
if ($title === null) {
// we don't apply $mapper to already htmlescaped string
$title = $mapper($file->getTitle());
}
$meta = $file->getMeta($unsafe_query);
$meta_is_inline = $meta['inline'] ?? false;
$meta_items = $meta['items'] ?? [];
$url = htmlescape($file->getUrl());
$subtitle = $file->getSubtitle();
return <<<HTML
<a href="{$url}" class="{$class}" data-id="{$file->getId()}"{$ctx->if_true($file->isTargetBlank(), ' target="_blank"')}>
<div class="files-list-item-icon">{$icon}</div>
<div class="files-list-item-info">
<div class="files-list-item-title">
<span class="files-list-item-title-label">{$title}</span>
{$ctx->if_true($file->isFolder() && $file->isTargetBlank(), fn() => '<span class="files-list-item-title-label-external-icon">'.$icons->arrow_up_right_out_square_outline_12().'</span>')}
{$ctx->if_true($subtitle, fn() => '<span class="files-list-item-subtitle">'.htmlescape($subtitle).'</span>')}
{$ctx->if_true($meta_is_inline, $ctx->for_each($meta_items, fn($s) => '<div class="files-list-item-meta-item">'.$s.'</div>'))}
</div>
{$ctx->if_true($meta_items && !$meta_is_inline, $ctx->meta($meta_items))}
{$ctx->if_true(is_array($text_excerpts) && isset($text_excerpts[$file->getId()]),
fn() => $ctx->text_excerpt($text_excerpts[$file->getId()]['excerpt'], $text_excerpts[$file->getId()]['index'], $unsafe_query))}
</div>
</a>
HTML;
}
/**
* @param SkinContext $ctx
* @param string[] $meta strings are already html-safe
* @return string
*/
function meta($ctx, array $meta) {
return <<<HTML
<div class="files-list-item-meta">
{$ctx->for_each($meta, fn($s) => '<div class="files-list-item-meta-item">'.$s.'</div>')}
</div>
HTML;
}
function text_excerpt($ctx, $unsafe_excerpt, $index, $unsafe_query) {
if ($index > 0)
$unsafe_excerpt = '...'.$unsafe_excerpt;
$unsafe_excerpt .= '...';
$text = hl_matched($unsafe_excerpt, $unsafe_query);
return <<<HTML
<div class="files-list-item-text-excerpt">{$text}</div>
HTML;
}