2023-11-25 06:17:45 +00:00
< script setup lang = "ts" >
2023-11-26 06:02:16 +00:00
import { ref , onMounted , reactive , nextTick , shallowRef } from "vue" ;
2023-11-25 06:18:18 +00:00
import { Modal } from "bootstrap" ;
2023-11-25 06:17:45 +00:00
import { setActivePinia } from "pinia" ;
import pinia from "../store/index" ;
setActivePinia ( pinia ) ;
2023-11-25 06:18:18 +00:00
import "../../node_modules/gridstack/dist/gridstack.min.css" ;
2023-11-25 06:17:45 +00:00
import { GridStack } from "gridstack" ;
import { settingsStore as settings } from "../store/settingsStore.js" ;
2023-12-09 06:18:16 +00:00
import active _heard _stations from "./grid/grid_active_heard_stations.vue" ;
import mini _heard _stations from "./grid/grid_active_heard_stations_mini.vue" ;
import active _stats from "./grid/grid_active_stats.vue" ;
import active _audio _level from "./grid/grid_active_audio.vue" ;
import active _rig _control from "./grid/grid_active_rig_control.vue" ;
import active _broadcats from "./grid/grid_active_broadcasts.vue" ;
import s _meter from "./grid/grid_s-meter.vue" ;
import dbfs _meter from "./grid/grid_dbfs.vue" ;
import grid _activities from "./grid/grid_activities.vue" ;
2023-11-25 06:17:45 +00:00
import { stateDispatcher } from "../js/eventHandler" ;
2023-11-26 06:02:16 +00:00
2023-11-25 06:17:45 +00:00
let count = ref ( 0 ) ;
let grid = null ; // DO NOT use ref(null) as proxies GS will break all logic when comparing structures... see https://github.com/gridstack/gridstack.js/issues/2115
let items = ref ( [ ] ) ;
2023-11-26 06:07:52 +00:00
class gridWidget {
2023-12-09 06:08:15 +00:00
//Contains the vue component
2023-11-26 06:02:16 +00:00
component2 ;
2023-12-09 06:08:15 +00:00
//Initial size and location if autoplace is false
2023-11-26 06:02:16 +00:00
size ;
2023-12-09 06:08:15 +00:00
//Text for dynamic button
2023-11-26 06:02:16 +00:00
text ;
2023-12-09 06:08:15 +00:00
//if true add when quick fill button is clicked
quickFill ;
//Auto place; true to add where ever it fits; false uses position information
autoPlace ;
2023-12-09 06:08:53 +00:00
constructor ( component , size , text , quickfill , autoPlace ) {
2023-11-26 06:02:16 +00:00
this . component2 = component ;
this . size = size ;
this . text = text ;
2023-12-09 06:08:53 +00:00
this . quickFill = quickfill ;
2023-12-09 06:08:15 +00:00
this . autoPlace = autoPlace ;
2023-11-26 06:02:16 +00:00
}
}
const gridWidgets = [
2023-11-26 06:07:52 +00:00
new gridWidget (
active _heard _stations ,
2023-12-09 06:08:15 +00:00
{ x : 0 , y : 0 , w : 16 , h : 40 } ,
2023-12-09 06:08:53 +00:00
"Heard stations" ,
true ,
true ,
2023-11-26 06:07:52 +00:00
) ,
new gridWidget (
active _stats ,
2023-12-09 06:08:15 +00:00
{ x : 16 , y : 26 , w : 8 , h : 69 } ,
2023-12-09 06:08:53 +00:00
"Stats (waterfall, etc)" ,
true ,
true ,
) ,
new gridWidget (
active _audio _level ,
{ x : 16 , y : 0 , w : 8 , h : 26 } ,
"Audio" ,
true ,
true ,
2023-11-26 06:07:52 +00:00
) ,
new gridWidget (
active _rig _control ,
2023-12-09 06:08:15 +00:00
{ x : 6 , y : 40 , w : 10 , h : 30 } ,
2023-12-09 06:08:53 +00:00
"Rig control" ,
true ,
true ,
) ,
new gridWidget (
active _broadcats ,
{ x : 6 , y : 70 , w : 10 , h : 25 } ,
"Broadcats" ,
true ,
true ,
2023-11-26 06:07:52 +00:00
) ,
2023-11-26 18:52:04 +00:00
new gridWidget (
mini _heard _stations ,
2023-12-09 06:08:15 +00:00
{ x : 1 , y : 1 , w : 6 , h : 54 } ,
2023-12-09 06:08:53 +00:00
"Mini Heard stations" ,
false ,
true ,
) ,
new gridWidget ( s _meter , { x : 1 , y : 1 , w : 4 , h : 8 } , "S-Meter" , false , true ) ,
new gridWidget (
dbfs _meter ,
{ x : 1 , y : 1 , w : 4 , h : 8 } ,
"Dbfs Meter" ,
false ,
true ,
) ,
new gridWidget (
grid _activities ,
{ x : 0 , y : 40 , w : 6 , h : 55 } ,
"Activities" ,
true ,
true ,
2023-11-26 18:52:04 +00:00
) ,
2023-11-26 06:02:16 +00:00
] ;
2023-11-25 06:18:18 +00:00
onMounted ( ( ) => {
grid = GridStack . init ( {
2023-12-09 06:08:15 +00:00
// DO NOT use grid.value = GridStack.init(), see above
2023-11-25 06:18:18 +00:00
float : true ,
2023-12-09 06:08:15 +00:00
cellHeight : "5px" ,
2023-11-26 06:02:16 +00:00
minRow : 50 ,
margin : 5 ,
2023-12-09 06:08:15 +00:00
column : 24 ,
2023-12-05 03:24:58 +00:00
draggable : {
scroll : true ,
} ,
2023-12-03 18:02:40 +00:00
resizable : {
2023-12-04 03:49:02 +00:00
handles : "se,sw" ,
} ,
2023-11-25 06:18:18 +00:00
} ) ;
2023-11-25 06:17:45 +00:00
2023-11-25 06:18:18 +00:00
grid . on ( "dragstop" , function ( event , element ) {
const node = element . gridstackNode ;
2023-12-09 06:08:53 +00:00
console . info (
` Moved # ${ node . id } to ${ node . x } . ${ node . y } . Dimensions: ${ node . w } x ${ node . h } ` ,
) ;
2023-11-25 06:18:18 +00:00
} ) ;
grid . on ( "change" , onChange ) ;
} ) ;
function onChange ( event , changeItems ) {
// update item position
changeItems . forEach ( ( item ) => {
var widget = items . value . find ( ( w ) => w . id == item . id ) ;
if ( ! widget ) {
2023-12-09 06:08:15 +00:00
console . error ( "Widget not found: " + item . id ) ;
2023-11-25 06:18:18 +00:00
return ;
}
widget . x = item . x ;
widget . y = item . y ;
widget . w = item . w ;
widget . h = item . h ;
} ) ;
}
2023-12-09 06:08:15 +00:00
2023-11-25 06:18:18 +00:00
function addNewWidget2 ( componentToAdd ) {
2023-11-26 06:07:52 +00:00
const node = items [ count . value ] || { ... componentToAdd . size } ;
2023-11-25 06:18:18 +00:00
node . id = "w_" + count . value ++ ;
2023-11-26 06:07:52 +00:00
node . component2 = shallowRef ( { ... componentToAdd . component2 } ) ;
2023-12-09 06:08:53 +00:00
node . autoPlace = componentToAdd . autoPlace ;
2023-11-25 06:18:18 +00:00
items . value . push ( node ) ;
nextTick ( ( ) => {
grid . makeWidget ( node . id ) ;
} ) ;
}
function remove ( widget ) {
var index = items . value . findIndex ( ( w ) => w . id == widget . id ) ;
items . value . splice ( index , 1 ) ;
const selector = ` # ${ widget . id } ` ;
grid . removeWidget ( selector , false ) ;
}
2023-12-09 06:08:15 +00:00
function clearAllItems ( ) {
grid . removeAll ( false ) ;
2023-12-09 06:08:53 +00:00
count . value = 0 ;
items . value = [ ] ;
2023-11-25 06:18:18 +00:00
}
function quickfill ( ) {
2023-11-26 06:02:16 +00:00
gridWidgets . forEach ( async ( gw ) => {
2023-12-09 06:08:53 +00:00
if ( gw . quickFill === true ) {
gw . autoPlace = false ;
2023-12-09 06:08:15 +00:00
await addNewWidget2 ( gw ) ;
//Reset autoplace value
2023-12-09 06:08:53 +00:00
gw . autoPlace = true ;
}
2023-11-26 06:07:52 +00:00
} ) ;
2023-11-25 06:18:18 +00:00
}
2023-11-25 06:17:45 +00:00
< / script >
< template >
2023-12-08 21:20:41 +00:00
< button
class = "btn btn-secondary fixed-middle-right rounded-0 rounded-start-4 p-1 pt-4 pb-4"
type = "button"
data - bs - toggle = "offcanvas"
data - bs - target = "#offcanvasGridItems"
aria - controls = "offcanvasGridItems"
>
2023-12-09 06:08:15 +00:00
< i class = "bi bi-grip-vertical h5" > < / i >
2023-12-08 21:20:41 +00:00
< / button >
2023-12-08 21:20:11 +00:00
2023-12-05 03:27:35 +00:00
< div class = "grid-container vh-100" >
< div class = "grid-stack" >
< div
v - for = "(w, indexs) in items"
class = "grid-stack-item"
: gs - x = "w.x"
: gs - y = "w.y"
: gs - w = "w.w"
: gs - h = "w.h"
: gs - id = "w.id"
: id = "w.id"
: key = "w.id"
2023-12-09 06:08:53 +00:00
: gs - auto - position = "w.autoPlace"
2023-12-05 03:27:35 +00:00
>
< div class = "grid-stack-item-content" >
< button
@ click = "remove(w)"
class = "btn-close grid-stack-floaty-btn"
> < / button >
< component :is = "w.component2" / >
< / div >
2023-11-25 06:17:45 +00:00
< / div >
< / div >
2023-11-25 06:18:18 +00:00
< / div >
2023-12-08 21:20:11 +00:00
2023-12-08 21:20:41 +00:00
< div
class = "offcanvas offcanvas-end"
data - bs - scroll = "true"
data - bs - backdrop = "true"
tabindex = "-1"
id = "offcanvasGridItems"
aria - labelledby = "offcanvasGridItemsLabel"
>
< div class = "offcanvas-header" >
< h5 class = "offcanvas-title" id = "offcanvasGridItemsLabel" >
2023-12-09 06:08:15 +00:00
Manage grid widgets
2023-12-08 21:20:41 +00:00
< / h5 >
< button
type = "button"
class = "btn-close"
data - bs - dismiss = "offcanvas"
aria - label = "Close"
> < / button >
2023-12-08 21:20:11 +00:00
< / div >
2023-12-08 21:20:41 +00:00
< div class = "offcanvas-body" >
2023-12-09 06:08:15 +00:00
< div >
2023-12-09 06:08:53 +00:00
< button
class = "btn btn-sm btn-outline-primary"
type = "button"
@ click = "quickfill"
>
2023-12-09 06:08:15 +00:00
Fill grid with common widgets
2023-12-09 06:08:53 +00:00
< / button >
& nbsp ;
< button
class = "btn btn-sm btn-outline-warning"
type = "button"
@ click = "clearAllItems"
>
2023-12-09 06:08:15 +00:00
Clear grid
< / button >
< / div >
2023-12-09 06:08:53 +00:00
< div > < / div >
2023-12-08 21:20:41 +00:00
< div class = "accordion" id = "accordionExample" >
<!-- Heard Stations -- >
< div class = "accordion-item" >
< h2 class = "accordion-header" id = "headingHeardStations" >
< button
class = "accordion-button"
type = "button"
data - bs - toggle = "collapse"
data - bs - target = "#collapseHeardStations"
aria - expanded = "true"
aria - controls = "collapseHeardStations"
>
< strong > Heard Stations < / strong >
< / button >
< / h2 >
< div
id = "collapseHeardStations"
class = "accordion-collapse collapse show"
aria - labelledby = "headingHeardStations"
data - bs - parent = "#accordionExample"
>
< div class = "accordion-body" >
< button
type = "button"
@ click = "addNewWidget2(gridWidgets[0])"
class = "btn btn-outline-secondary"
data - bs - dismiss = "modal"
>
Heard station list
< / button >
< / div >
< / div >
< / div >
2023-12-08 21:20:11 +00:00
2023-12-08 21:20:41 +00:00
<!-- Activities -- >
< div class = "accordion-item" >
< h2 class = "accordion-header" id = "headingActivities" >
< button
class = "accordion-button collapsed"
type = "button"
data - bs - toggle = "collapse"
data - bs - target = "#collapseActivities"
aria - expanded = "false"
aria - controls = "collapseActivities"
>
< strong > Activities < / strong >
< / button >
< / h2 >
< div
id = "collapseActivities"
class = "accordion-collapse collapse"
aria - labelledby = "headingActivities"
data - bs - parent = "#accordionExample"
>
< div class = "accordion-body" >
<!-- Content for Activities -- >
< / div >
< / div >
< / div >
2023-12-08 21:20:11 +00:00
2023-12-08 21:20:41 +00:00
<!-- Radio Control -- >
< div class = "accordion-item" >
< h2 class = "accordion-header" id = "headingRadioControl" >
< button
class = "accordion-button collapsed"
type = "button"
data - bs - toggle = "collapse"
data - bs - target = "#collapseRadioControl"
aria - expanded = "false"
aria - controls = "collapseRadioControl"
>
< strong > Radio Control < / strong >
< / button >
< / h2 >
< div
id = "collapseRadioControl"
class = "accordion-collapse collapse"
aria - labelledby = "headingRadioControl"
data - bs - parent = "#accordionExample"
>
< div class = "accordion-body" >
< button
type = "button"
@ click = "addNewWidget2(gridWidgets[3])"
class = "btn btn-outline-secondary"
data - bs - dismiss = "modal"
>
Rig Control
< / button >
< / div >
< / div >
< / div >
2023-12-08 21:20:11 +00:00
2023-12-08 21:20:41 +00:00
<!-- Audio Control -- >
< div class = "accordion-item" >
< h2 class = "accordion-header" id = "headingAudioControl" >
< button
class = "accordion-button collapsed"
type = "button"
data - bs - toggle = "collapse"
data - bs - target = "#collapseAudioControl"
aria - expanded = "false"
aria - controls = "collapseAudioControl"
>
< strong > Audio Control < / strong >
< / button >
< / h2 >
< div
id = "collapseAudioControl"
class = "accordion-collapse collapse"
aria - labelledby = "headingAudioControl"
data - bs - parent = "#accordionExample"
>
< div class = "accordion-body" >
< button
type = "button"
@ click = "addNewWidget2(gridWidgets[2])"
class = "btn btn-outline-secondary"
data - bs - dismiss = "modal"
>
Audio
< / button >
< / div >
< / div >
< / div >
2023-12-08 21:20:11 +00:00
2023-12-08 21:20:41 +00:00
<!-- Statistics -- >
< div class = "accordion-item" >
< h2 class = "accordion-header" id = "headingStatistics" >
< button
class = "accordion-button collapsed"
type = "button"
data - bs - toggle = "collapse"
data - bs - target = "#collapseStatistics"
aria - expanded = "false"
aria - controls = "collapseStatistics"
>
< strong > Statistics < / strong >
< / button >
< / h2 >
< div
id = "collapseStatistics"
class = "accordion-collapse collapse"
aria - labelledby = "headingStatistics"
data - bs - parent = "#accordionExample"
>
< div class = "accordion-body" >
< button
type = "button"
@ click = "addNewWidget2(gridWidgets[1])"
class = "btn btn-outline-secondary"
data - bs - dismiss = "modal"
>
Stats ( waterfall , etc )
< / button >
< / div >
< / div >
< / div >
2023-12-08 21:20:11 +00:00
2023-12-08 21:20:41 +00:00
<!-- Broadcasts -- >
< div class = "accordion-item" >
< h2 class = "accordion-header" id = "headingBroadcasts" >
< button
class = "accordion-button collapsed"
type = "button"
data - bs - toggle = "collapse"
data - bs - target = "#collapseBroadcasts"
aria - expanded = "false"
aria - controls = "collapseBroadcasts"
>
< strong > Broadcasts < / strong >
< / button >
< / h2 >
< div
id = "collapseBroadcasts"
class = "accordion-collapse collapse"
aria - labelledby = "headingBroadcasts"
data - bs - parent = "#accordionExample"
>
< div class = "accordion-body" >
< button
type = "button"
@ click = "addNewWidget2(gridWidgets[4])"
class = "btn btn-outline-secondary"
data - bs - dismiss = "modal"
>
Broadcasts
< / button >
< / div >
< / div >
< / div >
2023-12-08 21:20:11 +00:00
< / div >
< / div >
< / div >
2023-11-25 06:17:45 +00:00
< / template >
2023-12-08 21:20:11 +00:00
2023-11-25 06:17:45 +00:00
< style >
2023-12-08 21:20:11 +00:00
. fixed - middle - right {
2023-12-08 21:20:41 +00:00
position : fixed ; /* Fixed/sticky position */
top : 50 % ; /* Position at the middle of the viewport */
right : 0 px ; /* Place the button 20px from the right */
transform : translateY ( - 50 % ) ; /* Adjust for exact vertical centering */
z - index : 999 ; /* Ensure it's on top of other elements */
2023-12-08 21:20:11 +00:00
}
2023-11-25 06:17:45 +00:00
. grid - stack - item {
text - align : center ;
overflow : auto ;
z - index : 50 ;
}
2023-11-25 06:18:18 +00:00
. grid - stack - floaty - btn {
position : absolute ;
right : 0 px ;
z - index : 1000 ;
float : right ;
2023-11-26 18:32:42 +00:00
top : 6 px ;
2023-11-25 06:17:45 +00:00
}
2023-12-05 03:24:58 +00:00
. grid - container {
2023-12-05 03:27:35 +00:00
overflow - y : auto ;
}
2023-12-09 06:08:15 +00:00
. gs - 24 > . grid - stack - item {
width : 4.167 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "1" ] {
left : 4.167 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "2" ] {
width : 8.333 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "2" ] {
left : 8.333 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "3" ] {
width : 12.5 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "3" ] {
left : 12.5 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "4" ] {
width : 16.667 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "4" ] {
left : 16.667 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "5" ] {
width : 20.833 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "5" ] {
left : 20.833 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "6" ] {
width : 25 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "6" ] {
left : 25 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "7" ] {
width : 29.167 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "7" ] {
left : 29.167 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "8" ] {
width : 33.333 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "8" ] {
left : 33.333 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "9" ] {
width : 37.5 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "9" ] {
left : 37.5 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "10" ] {
width : 41.667 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "10" ] {
left : 41.667 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "11" ] {
width : 45.833 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "11" ] {
left : 45.833 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "12" ] {
width : 50 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "12" ] {
left : 50 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "13" ] {
width : 54.167 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "13" ] {
left : 54.167 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "14" ] {
width : 58.333 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "14" ] {
left : 58.333 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "15" ] {
width : 62.5 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "15" ] {
left : 62.5 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "16" ] {
width : 66.667 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "16" ] {
left : 66.667 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "17" ] {
width : 70.833 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "17" ] {
left : 70.833 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "18" ] {
width : 75 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "18" ] {
left : 75 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "19" ] {
width : 79.167 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "19" ] {
left : 79.167 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "20" ] {
width : 83.333 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "20" ] {
left : 83.333 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "21" ] {
width : 87.5 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "21" ] {
left : 87.5 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "22" ] {
width : 91.667 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "22" ] {
left : 91.667 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "23" ] {
width : 95.833 % ;
}
. gs - 24 > . grid - stack - item [ gs - x = "23" ] {
left : 95.833 % ;
}
. gs - 24 > . grid - stack - item [ gs - w = "24" ] {
width : 100 % ;
}
2023-11-25 06:18:18 +00:00
< / style >