primevue esm & search & favorites

This commit is contained in:
cgfhtw
2023-10-19 09:48:42 +02:00
parent 65bf35fa3e
commit 645972d549
11 changed files with 177 additions and 12 deletions
+3
View File
@@ -63,6 +63,9 @@ $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
$route['studentenverwaltung/(:any)/(:any)'] = 'Studentenverwaltung/index';
$route['public/js/components/primevue/(:any)/(:any)'] = 'public/Primevue/index/$1/$2';
$route['public/js/components/primevue/(:any)/(:any)/(:any)'] = 'public/Primevue/index/$2/$3/$1';
// load routes from extensions
$subdir = 'application/config/extensions';
$dirlist = scandir($subdir);
@@ -67,6 +67,7 @@ class Verband extends FHC_Controller
$this->StudiengangModel->addSelect('erhalter_kz');
$this->StudiengangModel->addSelect('typ');
$this->StudiengangModel->addSelect('kurzbz');
$this->StudiengangModel->addSelect('studiengang_kz');
$this->StudiengangModel->addOrder('erhalter_kz');
$this->StudiengangModel->addOrder('typ');
@@ -0,0 +1,46 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Primevue extends FHC_Controller
{
/**
* @return void
*/
public function index($path, $file, $extrapath = null)
{
if ($extrapath)
$path = $extrapath . '/' . $path;
$target = FHCPATH . 'vendor/npm-asset/primevue/' . $path . '/' . $file;
if (!file_exists($target))
return show_404();
$newUrl = '../../';
if ($extrapath)
$newUrl .= '../';
$ext = substr($file, -7) == '.min.js' ? '.esm.min.js' : '.esm.js';
$contents = file_get_contents($target);
$contents = preg_replace_callback('/import([^;]*)from\s*[\'"]vue[\'"];/i', function ($matches) {
return 'let ' . str_replace(' as ', ': ', $matches[1]) . ' = Vue;';
}, $contents);
$contents = preg_replace_callback('/(import[^;]*[\'"])(primevue[^\'"]+)([\'"])/i', function ($matches) use ($ext, $newUrl) {
if (is_file(FHCPATH . 'vendor/npm-asset/' . $matches[2])) {
$newUrl .= $matches[2];
} else {
$testfile = $matches[2] . '/index' . $ext;
if (file_exists(FHCPATH . 'vendor/npm-asset/' . $testfile))
$newUrl .= $testfile;
else
$newUrl .= $matches[2] . strrchr($matches[2], '/') . $ext;
}
return $matches[1] . $newUrl . $matches[3];
}, $contents);
$this->output->set_content_type('text/javascript');
$this->output->set_output($contents);
}
}
+4 -2
View File
@@ -5,7 +5,7 @@
'bootstrap5' => true,
'fontawesome6' => true,
'vue3' => true,
'primevue3' => true,
#'primevue3' => true,
'filtercomponent' => true,
'tabulator5' => true,
'phrases' => [],
@@ -14,7 +14,8 @@
'public/css/components/vue-datepicker.css'
],
'customJSs' => [
'vendor/npm-asset/primevue/toast/toast.min.js'
#'vendor/npm-asset/primevue/tree/tree.min.js',
#'vendor/npm-asset/primevue/toast/toast.min.js'
],
'customJSModules' => [
'public/js/apps/Studentenverwaltung.js'
@@ -28,6 +29,7 @@
<router-view
default-semester="<?= $variables['semester_aktuell']; ?>"
active-addons="<?= defined('ACTIVE_ADDONS') ? ACTIVE_ADDONS : ''; ?>"
stv-root="<?= site_url('Studentenverwaltung'); ?>"
cis-root="<?= CIS_ROOT; ?>"
:permissions="<?= htmlspecialchars(json_encode($permissions)); ?>"
:config="<?= htmlspecialchars(json_encode(['generateAlias' => !defined('GENERATE_ALIAS_STUDENT') ? true : GENERATE_ALIAS_STUDENT])); ?>"
@@ -41,6 +41,7 @@
{
generateCSSsInclude('vendor/fortawesome/font-awesome6/css/fontawesome.min.css');
generateCSSsInclude('vendor/fortawesome/font-awesome6/css/solid.min.css');
generateCSSsInclude('vendor/fortawesome/font-awesome6/css/regular.min.css'); // NOTE(chris): for favorites in stv
generateCSSsInclude('vendor/fortawesome/font-awesome6/css/brands.min.css');
}
+1 -1
View File
@@ -422,7 +422,7 @@
"nategood/httpful": "0.2.*",
"netcarver/textile": "3.7.*",
"nicolaskruchten/pivottable": "2.23.0",
"npm-asset/primevue": "3.15.*",
"npm-asset/primevue": "3.29.*",
"npm-asset/primeicons": "5.0.0",
"olifolkerd/tabulator4": "4.9.*",
+23
View File
@@ -1,5 +1,13 @@
@import './components/searchbar.css';
@import './components/verticalsplit.css';
@import '../../vendor/npm-asset/primevue/resources/themes/bootstrap4-light-blue/theme.css';
@import '../../vendor/npm-asset/primevue/resources/primevue.min.css';
@import '../../vendor/npm-asset/primeicons/primeicons.css';
.navbar-dark .navbar-brand:focus {
box-shadow: 0 0 0 .25rem rgba(13,110,253,.25);
z-index: 3;
}
.searchbar {
margin-right: 0!important;
@@ -23,6 +31,10 @@
flex: 1 1 auto;
}
#sidebarMenu {
width: 0%;
}
@media (min-width: 768px) {
#sidebarMenu {
visibility: visible!important;
@@ -39,6 +51,8 @@
overflow: auto;
}
}
.stv-verband .p-treetable-tbody > tr > td {
display: flex;
padding-top: .25rem!important;
@@ -51,6 +65,15 @@
flex: 1 1 auto;
}
.stv-verband.p-tree {
/*overflow: auto;*/
}
.stv-verband.p-tree .p-treenode-icon {
display: none;
}
.stv-list {
display: flex;
flex-direction: column;
+4 -1
View File
@@ -18,6 +18,8 @@
import FhcStudentenverwaltung from "../components/Stv/Studentenverwaltung.js";
import fhcapifactory from "./api/fhcapifactory.js";
import PvConfig from "../../../index.ci.php/public/js/components/primevue/config/config.esm.min.js";
Vue.$fhcapi = fhcapifactory;
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -35,6 +37,7 @@ const app = Vue.createApp();
app
.use(router)
.use(primevue.config.default)
.use(PvConfig)
.use(ErrorHandling)
.mount('#main');
@@ -36,6 +36,7 @@ export default {
defaultSemester: String,
config: Object,
permissions: Object,
stvRoot: String,
cisRoot: String,
activeAddons: String // semicolon separated list of active addons
},
@@ -107,7 +108,7 @@ export default {
},
template: `
<header class="navbar navbar-expand-lg navbar-dark bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-4 col-lg-3 col-xl-2 me-0 px-3" href="<?= site_url('Studentenverwaltung'); ?>">FHC 4.0</a>
<a class="navbar-brand col-md-4 col-lg-3 col-xl-2 me-0 px-3" :href="stvRoot">FHC 4.0</a>
<button class="navbar-toggler d-md-none m-1 collapsed" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
<fhc-searchbar :searchoptions="searchbaroptions" :searchfunction="searchfunction" class="searchbar w-100"></fhc-searchbar>
</header>
@@ -2,11 +2,15 @@ import VueDatePicker from '../../../vueDatepicker.js.php';
import FormUploadImage from '../../../Form/Upload/Image.js';
import {CoreRESTClient} from '../../../../RESTClient.js';
import PvToast from "../../../../../../index.ci.php/public/js/components/primevue/toast/toast.esm.min.js";
export default {
components: {
VueDatePicker,
FormUploadImage,
PvToast: primevue.toast
PvToast
},
inject: {
showBpk: {
@@ -1,16 +1,25 @@
import {CoreRESTClient} from '../../../RESTClient.js';
import PvTree from "../../../../../index.ci.php/public/js/components/primevue/tree/tree.esm.min.js";
import PvTreetable from "../../../../../index.ci.php/public/js/components/primevue/treetable/treetable.esm.min.js";
import PvColumn from "../../../../../index.ci.php/public/js/components/primevue/column/column.esm.min.js";
export default {
components: {
TreeTable: primevue.treetable,
TreeColumn: primevue.column
PvTree,
PvTreetable,
PvColumn
},
emits: [
'selectVerband'
],
data() {
return {
debug_version: 1, // NOTE(chris): switch between tree/treetable and filter/favorites...
loading: true,
selected: {}, // NOTE(chris): tree only
nodes: []
}
},
@@ -18,6 +27,12 @@ export default {
onExpandTreeNode(node) {
if (!node.children) {
if (node.data.link) {
let activeEl = null;
this.$nextTick(() => {
this.$nextTick(() => {
activeEl = document.activeElement;
});
});
this.loading = true;
CoreRESTClient
.get("components/stv/verband/" + node.data.link)
@@ -25,6 +40,21 @@ export default {
.then(result => {
const subNodes = result.map(this.mapResultToTreeData);
node.children = subNodes;
let treeitem = this.$refs.tree.$el.querySelector('[data-tree-item-key="' + node.key + '"]');
if (!this.debug_version) // NOTE(chris): tree only
treeitem = treeitem.closest('[role="treeitem"]');
else // NOTE(chris): treetable only
treeitem = treeitem.closest('[role="row"]');
this.$nextTick(() => {
if (activeEl == document.activeElement)
treeitem.dispatchEvent(new KeyboardEvent('keydown', {
code: 'ArrowDown',
key: 'ArrowDown'
}));
});
this.loading = false;
})
.catch(error => {
@@ -40,7 +70,8 @@ export default {
mapResultToTreeData(el) {
const cp = {
key: ("" + el.link).replace('/', '-'),
data: el
data: el,
label: el.name
};
if (el.children)
@@ -49,6 +80,15 @@ export default {
cp.leaf = el.leaf || false;
return cp;
},
onFilterKeydown(data) {
// NOTE(chris): tree only
// TODO(chris): we can online search here
},
markFav(key) {
// NOTE(chris): treetable only
// TODO(chris): IMPLEMENT
// TODO(chris): make clickable with keyboard
}
},
mounted() {
@@ -64,7 +104,48 @@ export default {
});
},
template: `
<tree-table class="stv-verband p-treetable-sm" :value="nodes" lazy @node-expand="onExpandTreeNode" selection-mode="single" @node-select="onSelectTreeNode" scrollable scroll-height="flex">
<tree-column field="name" header="Verband" expander></tree-column>
</tree-table>`
<div v-if="!debug_version" class="overflow-auto" tabindex="-1">
<pv-tree
ref="tree"
class="stv-verband p-0"
v-model:selectionKeys="selected"
:value="nodes"
selection-mode="single"
@node-select="onSelectTreeNode"
@node-expand="onExpandTreeNode"
:loading="loading"
filter
:pt="{input:{onKeydown:onFilterKeydown}}"
>
<template #default="{node}">
<span :data-tree-item-key="node.key" :title="node.data.studiengang_kz">{{node.label}}</span>
</template>
</pv-tree>
</div>
<div v-else class="overflow-auto" tabindex="-1">
<pv-treetable
ref="tree"
class="stv-verband p-treetable-sm"
:value="nodes"
lazy
@node-expand="onExpandTreeNode"
selection-mode="single"
@node-select="onSelectTreeNode"
scrollable
scroll-height="flex"
>
<pv-column field="name" header="Verband" expander>
<template #body="{node}">
<span :data-tree-item-key="node.key" :title="node.data.studiengang_kz">
{{node.data.name}}
</span>
</template>
</pv-column>
<pv-column field="fav" header="FAV" headerStyle="flex: 0 0 auto" style="flex: 0 0 auto">
<template #body="{node}">
<i :class="node.data.fav ? 'fa-solid' : 'fa-regular'" class="fa-star" @click="markFav(node.key)"></i>
</template>
</pv-column>
</pv-treetable>
</div>`
};