125 lines
2.9 KiB
Vue
125 lines
2.9 KiB
Vue
<template>
|
||
<div class="shared-control-panel" :class="{ 'is-open': open }">
|
||
<btn
|
||
class="toggle-btn"
|
||
@click="open = !open"
|
||
:aria-expanded="open"
|
||
aria-label="Toggle control panel"
|
||
>
|
||
<!-- <span class="toggle-btn-inner"> -->
|
||
<span class="toggle-icon">{{ open ? '×' : '⊞' }}</span>
|
||
<!-- <span class="toggle-label">{{ title }}</span> -->
|
||
<!-- </span> -->
|
||
</btn>
|
||
|
||
<div class="panel-body">
|
||
<div class="panel-inner">
|
||
<slot />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import Btn from './Btn.vue'
|
||
import { ref } from 'vue'
|
||
|
||
const props = defineProps({
|
||
title: {
|
||
type: String,
|
||
default: 'Controls',
|
||
},
|
||
defaultOpen: {
|
||
type: Boolean,
|
||
default: true,
|
||
},
|
||
})
|
||
|
||
const open = ref(props.defaultOpen)
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.shared-control-panel {
|
||
--t-duration: 400ms;
|
||
--t-ease: var(--ease-out-quart);
|
||
--panel-width: #{columns(4)};
|
||
position: fixed;
|
||
top: 0;
|
||
right: 0;
|
||
padding: var(--layout-margin) 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: rs(8px);
|
||
height: 100dvh;
|
||
pointer-events: none;
|
||
|
||
.toggle-btn {
|
||
pointer-events: all;
|
||
position: absolute;
|
||
top: var(--layout-margin);
|
||
right: 0;
|
||
z-index: 5;
|
||
border-top-right-radius: 0;
|
||
border-bottom-right-radius: 0;
|
||
cursor: pointer;
|
||
|
||
.toggle-btn-inner {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: rs(8px);
|
||
}
|
||
.toggle-icon {
|
||
@include label;
|
||
}
|
||
.toggle-label {
|
||
white-space: nowrap;
|
||
@include label;
|
||
}
|
||
}
|
||
|
||
.panel-body {
|
||
pointer-events: all;
|
||
width: var(--panel-width);
|
||
height: 100%;
|
||
background: var(--theme-bg);
|
||
border: 1px solid var(--grey-400);
|
||
border-radius: rs(10px);
|
||
overflow: hidden;
|
||
transform: translateX(100%);
|
||
transition: transform var(--t-duration) var(--t-ease);
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding-top: rs(50px);
|
||
}
|
||
|
||
.panel-inner {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
padding: rs(24px) rs(20px);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: rs(20px);
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--grey-400) transparent;
|
||
|
||
&::-webkit-scrollbar {
|
||
width: rs(4px);
|
||
}
|
||
&::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
&::-webkit-scrollbar-thumb {
|
||
background: var(--grey-400);
|
||
border-radius: rs(2px);
|
||
}
|
||
}
|
||
|
||
&.is-open {
|
||
.panel-body {
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
}
|
||
</style>
|