Hello,
I like to edit my content with a sticky sidebar, but when there’s too much fields within, the sidebar becomes bigger than the available vertical space and the stick behaviour needs a lot of scrolling to access some fields.
So I thought about adding tabs, to reduce the vertical space. In occurence, I have a ‘meta’ and ‘files’ tab in my sidebar.
As there’s no need to store content, I first tried making a panel section, as recommended by the extensions reference. But by analysing some of the panel code, I came to realise that I could easily re-use the when
conditional field and section blueprint features by making it a field with a value.
So I came up with a quite simple solution that wraps the new (k3.5, yet undocumented?) k-tabs
ui component in a panel field, using the active tab as field value.
I’m a aware that this is quite a hacky use of the panel API, but it works great with very few code.
Here’s the code I’m using :
// index.js
panel.plugin('anonimousse/tabsfield', {
fields: {
tabs: {
data() {
return {
// Set initial tab and load it
tabsData: [],
tab: null,
//disabled: true, // Disables saving the value (edit: not needed)
};
},
// Blueprint values
props: {
tabs: Array, // tabs property from blueprint file
},
created: function(){
this.tabsData = [];
// Strip keys from blueprint data, and sanitize
Object.keys(this.tabs).forEach( key => {
this.tabsData.push( this.tabs[key] );
this.tabsData[this.tabsData.length-1]['columns']=[];
});
// Set tab by priority : tab set by k-tabs, tab in hash, first tab
let defaultTab = this.tab ?? this.$route.hash.replace('#','');
if( !defaultTab && this.tabsData.length > 0 ) defaultTab = this.tabsData[0].name;
this.setTab( defaultTab );
},
watch: {
'$route'() {
// Set tab to new route
this.setTab( this.$route.hash.replace('#','') ?? this.tabsData[0].name );
},
},
methods: {
setTab(newTab){
// Only watch known tabs (ignores unknown tabs)
for (let i = 0; i < this.tabsData.length; i++){
if( this.tabsData[i].name == newTab ){
this.tab = newTab;
// Sends the value to the kirby store and registers the field virtual new value
this.$emit("input", newTab);
// Dirty: replace original value with changed value to fool kirby's changes indicator
this.$store.getters["content/model"]('pages/test/fr').originals.sidebarview = newTab;
break;
}
};
},
},
template: `<div><k-tabs theme="notice" :tabs="tabsData" :tab="tab" /></div>`
}
}
});
// index.php
Kirby::plugin('anonimousse/tabsfield', [
'fields' => [
'tabs' => [
'props' => [
'value' => null, // Null values are not saved in content folder, seemingly
//'disabled'=>true, // Edit: not needed
],
'computed' => [],
],
],
// Blueprint - Sidebar sections
// [...]
sections:
sidebarswitch:
type: fields
fields:
sidebarview:
type: tabs
tabs:
metainfo:
name: meta
icon: tag
label: Info
files:
name: files
icon: file
label: Files
sidebarfields:
type: fields
when:
sidebarview: meta
fields:
author:
type: text
sidebarfiles:
type: files
when:
sidebarview: files
// [...]
Here’s a screenshot of the result:
Now, everything works as expected, but if you’ve read my comments in the code, there are some tricky workarounds. For learning purposes, I’m wondering if there are better ways to achieve this ?
Particularly how to prevent a field from being written to the content file, and the originals
being synced with values
to prevent the “changes indicator” in the panel.