Melissa Agenda: breakpoint compact + drawer mobile teleportado
Dois pontos de quebra agora: - <xl (<=1279px) "compact": view-switcher (Dia/Semana/Mes/Lista) sai da toolbar e entra no menu "Acoes" com check icon no ativo. Filtros tambem migram pra dentro pra nao inflar a barra. - <lg (<=1023px) "mobile": .ma-side e .ma-widgets viajam pra fora do .ma-page via Teleport, num <aside class="ma-mobile-drawer"> sempre presente no DOM (v-show controla display) — garante target valido desde o mount. Botao "Menu" mobile-only aparece a esquerda do header. Backdrop entre drawer e .ma-page com Transition de fade. Bonus styles.scss: fix borda dupla do FullCalendar. .fc-scrollgrid em light mode mantinha borda externa que somada com a borda das celulas da ponta dava 2px na borda do calendario. Zera o contorno do contairner — celulas (td/th) ja desenham a grade visual. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -279,13 +279,18 @@
|
|||||||
background: var(--surface-hover);
|
background: var(--surface-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bordas do FullCalendar — solução pra "borda dupla":
|
||||||
|
- células (td/th) MANTÊM a borda → forma a grade visual
|
||||||
|
- contêiner .fc-scrollgrid ZERA → sem isso, a borda externa fica
|
||||||
|
dobrada (1px do contêiner + 1px da célula da ponta = 2px na borda) */
|
||||||
.app-dark .fc-theme-standard td,
|
.app-dark .fc-theme-standard td,
|
||||||
.app-dark .fc-theme-standard th {
|
.app-dark .fc-theme-standard th {
|
||||||
border: 1px solid var(--surface-border);
|
border: 1px solid var(--surface-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fc-theme-standard .fc-scrollgrid,
|
||||||
.app-dark .fc-theme-standard .fc-scrollgrid {
|
.app-dark .fc-theme-standard .fc-scrollgrid {
|
||||||
border: 1px solid var(--surface-border);
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-dark .fc-timegrid-event-harness-inset .fc-timegrid-event,
|
.app-dark .fc-timegrid-event-harness-inset .fc-timegrid-event,
|
||||||
|
|||||||
@@ -68,27 +68,40 @@ const onlySessionsOptions = [
|
|||||||
{ label: 'Tudo', value: false }
|
{ label: 'Tudo', value: false }
|
||||||
];
|
];
|
||||||
|
|
||||||
// ── Drawer mobile ────────────────────────────────────────────
|
// ── Breakpoints ──────────────────────────────────────────────
|
||||||
// Quando largura <1024px, .ma-side e .ma-widgets viram off-canvas
|
// Dois pontos:
|
||||||
// dentro de .ma-drawer. drawerOpen controla translateX via CSS.
|
// <xl (≤1279px) → "compacto" — filtros viajam pra dentro do botão
|
||||||
|
// "Ações" da toolbar
|
||||||
|
// <lg (≤1023px) → "mobile" — aside+widgets viram drawer off-canvas
|
||||||
|
// (Teleportado pra fora do .ma-page) e o botão "Menu"
|
||||||
|
// aparece à esquerda do título no header
|
||||||
const drawerOpen = ref(false);
|
const drawerOpen = ref(false);
|
||||||
const isMobile = ref(false);
|
const isMobile = ref(false);
|
||||||
|
const isCompact = ref(false);
|
||||||
|
|
||||||
let _mqMobile = null;
|
let _mqMobile = null;
|
||||||
function _onMqChange(e) {
|
let _mqCompact = null;
|
||||||
|
function _onMqMobileChange(e) {
|
||||||
isMobile.value = e.matches;
|
isMobile.value = e.matches;
|
||||||
// Cruzou pra desktop → fecha o drawer pra evitar layout zoado
|
if (!e.matches) drawerOpen.value = false; // saiu do mobile, fecha drawer
|
||||||
if (!e.matches) drawerOpen.value = false;
|
}
|
||||||
|
function _onMqCompactChange(e) {
|
||||||
|
isCompact.value = e.matches;
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (typeof window !== 'undefined' && window.matchMedia) {
|
if (typeof window !== 'undefined' && window.matchMedia) {
|
||||||
_mqMobile = window.matchMedia('(max-width: 1023px)');
|
_mqMobile = window.matchMedia('(max-width: 1023px)');
|
||||||
isMobile.value = _mqMobile.matches;
|
isMobile.value = _mqMobile.matches;
|
||||||
_mqMobile.addEventListener('change', _onMqChange);
|
_mqMobile.addEventListener('change', _onMqMobileChange);
|
||||||
|
|
||||||
|
_mqCompact = window.matchMedia('(max-width: 1279px)');
|
||||||
|
isCompact.value = _mqCompact.matches;
|
||||||
|
_mqCompact.addEventListener('change', _onMqCompactChange);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (_mqMobile) _mqMobile.removeEventListener('change', _onMqChange);
|
if (_mqMobile) _mqMobile.removeEventListener('change', _onMqMobileChange);
|
||||||
|
if (_mqCompact) _mqCompact.removeEventListener('change', _onMqCompactChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
function toggleDrawer() {
|
function toggleDrawer() {
|
||||||
@@ -108,6 +121,13 @@ function goSettings() {
|
|||||||
// pra não inflar a toolbar em telas pequenas.
|
// pra não inflar a toolbar em telas pequenas.
|
||||||
const mobileActionsRef = ref(null);
|
const mobileActionsRef = ref(null);
|
||||||
const mobileActionsItems = computed(() => [
|
const mobileActionsItems = computed(() => [
|
||||||
|
// Views (Dia/Semana/Mês/Lista) — view-switcher inline some em <xl,
|
||||||
|
// entra no menu aqui. Check icon marca o ativo.
|
||||||
|
{ label: 'Dia', icon: calendarView.value === 'dia' ? 'pi pi-check' : 'pi pi-calendar', command: () => setView('dia') },
|
||||||
|
{ label: 'Semana', icon: calendarView.value === 'semana' ? 'pi pi-check' : 'pi pi-calendar', command: () => setView('semana') },
|
||||||
|
{ label: 'Mês', icon: calendarView.value === 'mes' ? 'pi pi-check' : 'pi pi-calendar', command: () => setView('mes') },
|
||||||
|
{ label: 'Lista', icon: calendarView.value === 'lista' ? 'pi pi-check' : 'pi pi-list', command: () => setView('lista') },
|
||||||
|
{ separator: true },
|
||||||
{
|
{
|
||||||
label: onlySessions.value ? 'Mostrar tudo' : 'Apenas sessões',
|
label: onlySessions.value ? 'Mostrar tudo' : 'Apenas sessões',
|
||||||
icon: onlySessions.value ? 'pi pi-list' : 'pi pi-filter',
|
icon: onlySessions.value ? 'pi pi-list' : 'pi pi-filter',
|
||||||
@@ -818,22 +838,45 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<!-- Drawer host (multi-root, fora do .ma-page pra full viewport height
|
||||||
|
e single-scroll). Sempre presente no DOM (v-show controla display)
|
||||||
|
pra ser um Teleport target válido em todo momento.
|
||||||
|
O backdrop fica logo depois pra ficar entre drawer e .ma-page. -->
|
||||||
|
<aside
|
||||||
|
class="ma-mobile-drawer"
|
||||||
|
:class="{ 'is-open': drawerOpen }"
|
||||||
|
v-show="isMobile"
|
||||||
|
aria-label="Menu lateral da agenda"
|
||||||
|
>
|
||||||
|
<div id="ma-mobile-drawer-target" class="ma-mobile-drawer__scroll" />
|
||||||
|
</aside>
|
||||||
|
<Transition name="ma-drawer-fade">
|
||||||
|
<div
|
||||||
|
v-if="isMobile && drawerOpen"
|
||||||
|
class="ma-mobile-drawer__backdrop"
|
||||||
|
@click="fecharDrawer"
|
||||||
|
/>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
<section class="ma-page">
|
<section class="ma-page">
|
||||||
<header class="ma-page__head">
|
<header class="ma-page__head">
|
||||||
|
<!-- Menu — mobile only, abre o drawer com aside+widgets.
|
||||||
|
Posicionado à esquerda do header (primeiro elemento). Em
|
||||||
|
mobile o ícone+título do .ma-page__title some (substituído
|
||||||
|
pelo "Menu Agenda" — não duplica). Estilo primary filled. -->
|
||||||
|
<button
|
||||||
|
class="ma-menu-btn ma-menu-btn--mobile-only"
|
||||||
|
v-tooltip.bottom="'Pacientes & widgets'"
|
||||||
|
@click="toggleDrawer"
|
||||||
|
>
|
||||||
|
<i class="pi pi-bars" />
|
||||||
|
<span>Menu Agenda</span>
|
||||||
|
</button>
|
||||||
<div class="ma-page__title">
|
<div class="ma-page__title">
|
||||||
<i class="pi pi-calendar text-emerald-300" />
|
<i class="pi pi-calendar text-emerald-300" />
|
||||||
<span>Agenda</span>
|
<span>Agenda</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ma-page__actions">
|
<div class="ma-page__actions">
|
||||||
<!-- Pacientes — mobile only, abre o drawer com aside+widgets -->
|
|
||||||
<button
|
|
||||||
class="ma-head-btn ma-head-btn--mobile-only"
|
|
||||||
v-tooltip.bottom="'Pacientes & widgets'"
|
|
||||||
@click="toggleDrawer"
|
|
||||||
>
|
|
||||||
<i class="pi pi-users" />
|
|
||||||
<span>Pacientes</span>
|
|
||||||
</button>
|
|
||||||
<!-- Configurações da agenda — abre /configuracoes/agenda -->
|
<!-- Configurações da agenda — abre /configuracoes/agenda -->
|
||||||
<button
|
<button
|
||||||
class="ma-head-btn"
|
class="ma-head-btn"
|
||||||
@@ -850,21 +893,11 @@ defineExpose({
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="ma-body">
|
<div class="ma-body">
|
||||||
<!-- Backdrop: só visível em mobile com drawer aberto -->
|
<!-- ════ COL 1: Hoje + Pacientes ════
|
||||||
<Transition name="ma-drawer-fade">
|
Em desktop renderiza in-place. Em mobile (<lg) o Teleport
|
||||||
<div
|
move pra dentro de #ma-mobile-drawer (fora do .ma-page,
|
||||||
v-if="isMobile && drawerOpen"
|
fullheight, single-scroll). -->
|
||||||
class="ma-drawer__backdrop"
|
<Teleport to="#ma-mobile-drawer-target" :disabled="!isMobile">
|
||||||
@click="fecharDrawer"
|
|
||||||
/>
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<!-- ════ DRAWER (envolve COL 1 + COL 3) ════
|
|
||||||
Desktop: display:contents — children flow como flex items
|
|
||||||
do .ma-body (side, cal, widgets via order CSS).
|
|
||||||
Mobile (<lg): position:fixed off-canvas, slide da esquerda. -->
|
|
||||||
<div class="ma-drawer" :class="{ 'is-open': drawerOpen }">
|
|
||||||
<!-- ════ COL 1: Hoje + Pacientes ════ -->
|
|
||||||
<aside class="ma-side">
|
<aside class="ma-side">
|
||||||
<!-- Hoje (stats + lista de sessões) — movido da col 3 -->
|
<!-- Hoje (stats + lista de sessões) — movido da col 3 -->
|
||||||
<div class="ma-w ma-w--side">
|
<div class="ma-w ma-w--side">
|
||||||
@@ -988,6 +1021,7 @@ defineExpose({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
</Teleport>
|
||||||
|
|
||||||
<!-- ════ COL 2: Calendar central ════ -->
|
<!-- ════ COL 2: Calendar central ════ -->
|
||||||
<div class="ma-cal">
|
<div class="ma-cal">
|
||||||
@@ -1037,11 +1071,10 @@ defineExpose({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bloquear: ícone-only com Menu popup. Some em
|
<!-- Bloquear: ícone-only com Menu popup. Visível só
|
||||||
mobile (vai pra dentro de "Ações"). Disabled
|
em ≥xl. Em <xl vai pra dentro de "Ações". -->
|
||||||
em modo standalone (sem composable M). -->
|
|
||||||
<button
|
<button
|
||||||
class="ma-cal__icon ma-cal__icon--desktop-only"
|
class="ma-cal__icon ma-cal__btn--xl-only"
|
||||||
:disabled="!M"
|
:disabled="!M"
|
||||||
v-tooltip.top="'Bloquear horário/dia'"
|
v-tooltip.top="'Bloquear horário/dia'"
|
||||||
@click="openBloqueioMenu"
|
@click="openBloqueioMenu"
|
||||||
@@ -1050,19 +1083,21 @@ defineExpose({
|
|||||||
</button>
|
</button>
|
||||||
<Menu ref="bloqueioMenuRef" :model="bloqueioMenuItems" :popup="true" />
|
<Menu ref="bloqueioMenuRef" :model="bloqueioMenuItems" :popup="true" />
|
||||||
|
|
||||||
<!-- Ações — mobile only. Concentra timeMode +
|
<!-- Ações — aparece em <xl (1279px). Concentra
|
||||||
onlySessions + bloquear quando a toolbar fica
|
filtros (timeMode/onlySessions) + bloquear
|
||||||
apertada em telas <lg. -->
|
quando a toolbar fica apertada. Texto "Ações"
|
||||||
|
pra dar affordance, não só ícone. -->
|
||||||
<button
|
<button
|
||||||
class="ma-cal__icon ma-cal__icon--mobile-only"
|
class="ma-cal__btn ma-cal__btn--compact-only"
|
||||||
v-tooltip.top="'Ações'"
|
v-tooltip.top="'Ações da agenda'"
|
||||||
@click="openMobileActions"
|
@click="openMobileActions"
|
||||||
>
|
>
|
||||||
<i class="pi pi-ellipsis-v" />
|
<i class="pi pi-ellipsis-v" />
|
||||||
|
<span>Ações</span>
|
||||||
</button>
|
</button>
|
||||||
<Menu ref="mobileActionsRef" :model="mobileActionsItems" :popup="true" />
|
<Menu ref="mobileActionsRef" :model="mobileActionsItems" :popup="true" />
|
||||||
|
|
||||||
<div class="ma-cal__view">
|
<div class="ma-cal__view ma-cal__view--xl-only">
|
||||||
<button
|
<button
|
||||||
v-for="opt in [{v:'dia',l:'Dia'},{v:'semana',l:'Semana'},{v:'mes',l:'Mês'},{v:'lista',l:'Lista'}]"
|
v-for="opt in [{v:'dia',l:'Dia'},{v:'semana',l:'Semana'},{v:'mes',l:'Mês'},{v:'lista',l:'Lista'}]"
|
||||||
:key="opt.v"
|
:key="opt.v"
|
||||||
@@ -1095,8 +1130,10 @@ defineExpose({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ════ COL 3: Widgets direita ════ -->
|
<!-- ════ COL 3: Widgets direita ════
|
||||||
<aside class="ma-widgets">
|
Mesma lógica da COL 1 — Teleport pro drawer em mobile. -->
|
||||||
|
<Teleport to="#ma-mobile-drawer-target" :disabled="!isMobile">
|
||||||
|
<aside class="ma-widgets">
|
||||||
<!-- Mini calendar -->
|
<!-- Mini calendar -->
|
||||||
<div class="ma-w">
|
<div class="ma-w">
|
||||||
<div class="ma-w__head">
|
<div class="ma-w__head">
|
||||||
@@ -1152,7 +1189,7 @@ defineExpose({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
</div> <!-- /.ma-drawer (envolve side + cal + widgets) -->
|
</Teleport>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Popover + dialogs de cadastro (montados fora do scroll/aside
|
<!-- Popover + dialogs de cadastro (montados fora do scroll/aside
|
||||||
@@ -1286,20 +1323,26 @@ defineExpose({
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
position: relative; /* âncora do backdrop fixed-relative em mobile */
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drawer wrapper — display:contents desktop, virtual (não gera box).
|
/* Header — Menu (mobile, esquerda) + título (flex:1) + actions (direita).
|
||||||
Em mobile (<lg) só serve como marcador `.is-open` pra CSS abaixo. */
|
Title ganha flex:1 pra empurrar actions pra ponta. Em desktop sem o
|
||||||
.ma-drawer {
|
Menu, layout é title (flex:1) + actions na ponta. */
|
||||||
display: contents;
|
.ma-page__title {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.ma-page__title > span {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header actions cluster — Pacientes (mobile) + Configurações + Fechar. */
|
|
||||||
.ma-page__actions {
|
.ma-page__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.ma-head-btn {
|
.ma-head-btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -1319,17 +1362,70 @@ defineExpose({
|
|||||||
.ma-head-btn:hover { background: var(--m-bg-soft-hover); }
|
.ma-head-btn:hover { background: var(--m-bg-soft-hover); }
|
||||||
.ma-head-btn > i { font-size: 0.85rem; }
|
.ma-head-btn > i { font-size: 0.85rem; }
|
||||||
|
|
||||||
/* Filtros desktop (timeMode + onlySessions). PrimeVue SelectButton
|
/* Menu button (mobile, esquerda do header) — primary filled, igual ao
|
||||||
absorve seu próprio styling — só precisamos do gap. */
|
"+ Paciente"/"+ Agendar". Aparece só em <lg. Substitui o título
|
||||||
|
visualmente — "Menu Agenda" já tem o nome da seção. */
|
||||||
|
.ma-menu-btn {
|
||||||
|
height: 32px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: var(--m-accent);
|
||||||
|
border: 1px solid var(--m-accent);
|
||||||
|
color: white;
|
||||||
|
padding: 0 11px;
|
||||||
|
border-radius: 9px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 140ms ease, transform 140ms ease;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.ma-menu-btn:hover {
|
||||||
|
background: color-mix(in srgb, var(--m-accent) 88%, white);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.ma-menu-btn:active { transform: translateY(0); }
|
||||||
|
.ma-menu-btn > i { font-size: 0.85rem; }
|
||||||
|
|
||||||
|
/* Filtros desktop (timeMode + onlySessions). Visível em ≥xl,
|
||||||
|
somem em <xl (vão pra dentro do menu "Ações"). */
|
||||||
.ma-cal__filters {
|
.ma-cal__filters {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default: mobile-only fica oculto, desktop-only aparece (ambos os
|
/* Botão "Ações" da toolbar — mesmo estilo do .ma-menu-btn (primary
|
||||||
.ma-cal__icon de bloquear e ações). Inverte em @media abaixo. */
|
filled), texto + ícone. Aparece em <xl. Concentra view-switcher,
|
||||||
.ma-cal__icon--mobile-only,
|
filtros e bloquear quando a toolbar fica apertada. */
|
||||||
.ma-head-btn--mobile-only { display: none; }
|
.ma-cal__btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 11px;
|
||||||
|
background: var(--m-accent);
|
||||||
|
border: 1px solid var(--m-accent);
|
||||||
|
color: white;
|
||||||
|
border-radius: 9px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: background-color 140ms ease, transform 140ms ease;
|
||||||
|
}
|
||||||
|
.ma-cal__btn:hover {
|
||||||
|
background: color-mix(in srgb, var(--m-accent) 88%, white);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.ma-cal__btn:active { transform: translateY(0); }
|
||||||
|
.ma-cal__btn > i { font-size: 0.85rem; }
|
||||||
|
|
||||||
|
/* Default (desktop ≥xl): xl-only visível, compact-only oculto.
|
||||||
|
Inverte em @media (max-width: 1279px). */
|
||||||
|
.ma-cal__btn--compact-only,
|
||||||
|
.ma-menu-btn--mobile-only { display: none; }
|
||||||
|
|
||||||
/* ═══ COL 1: Aside Pacientes ═════════════════════════════════ */
|
/* ═══ COL 1: Aside Pacientes ═════════════════════════════════ */
|
||||||
.ma-side {
|
.ma-side {
|
||||||
@@ -2392,71 +2488,67 @@ html:not(.app-dark) .ma-cal__fc :deep(.fc-timegrid-now-indicator-line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════
|
||||||
Responsivo <lg (≤1023px)
|
Mobile drawer (off-canvas)
|
||||||
───────────────────────────────────────────────────────────────
|
───────────────────────────────────────────────────────────────
|
||||||
- .ma-cal vira fullwidth
|
Renderizado fora do .ma-page (multi-root template) → ocupa 100vh
|
||||||
- .ma-side e .ma-widgets viram drawer off-canvas (slide da esquerda)
|
real, single-scroll. Recebe .ma-side e .ma-widgets via Teleport
|
||||||
- .ma-drawer.is-open ativa o slide-in
|
quando isMobile (<lg). Em desktop fica display:none via v-show.
|
||||||
- Filtros desktop (timeMode/onlySessions/bloquear icon) somem;
|
|
||||||
"Ações" + Pacientes header buttons aparecem
|
|
||||||
═══════════════════════════════════════════════════════════════ */
|
═══════════════════════════════════════════════════════════════ */
|
||||||
@media (max-width: 1023px) {
|
.ma-mobile-drawer {
|
||||||
.ma-body {
|
position: fixed;
|
||||||
flex-direction: column;
|
top: 0;
|
||||||
}
|
left: 0;
|
||||||
|
height: 100vh; /* full viewport, sem encapsular no .ma-page */
|
||||||
/* Drawer: panels separados mas com transform sincronizado.
|
height: 100dvh; /* iOS toolbar dynamic */
|
||||||
Stack vertical: side ocupa 55% top, widgets 45% bottom. */
|
width: min(360px, 88vw);
|
||||||
.ma-drawer .ma-side,
|
z-index: 50;
|
||||||
.ma-drawer .ma-widgets {
|
background: var(--m-bg-medium, rgba(20, 20, 20, 0.92));
|
||||||
position: fixed;
|
backdrop-filter: blur(28px) saturate(160%);
|
||||||
left: 0;
|
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||||
width: min(360px, 88vw);
|
border-right: 1px solid var(--m-border);
|
||||||
z-index: 50;
|
transform: translateX(-100%);
|
||||||
background: var(--m-bg-medium, rgba(20, 20, 20, 0.92));
|
transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
backdrop-filter: blur(28px) saturate(160%);
|
color: var(--m-text);
|
||||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||||
border-right: 1px solid var(--m-border);
|
}
|
||||||
transform: translateX(-100%);
|
.ma-mobile-drawer.is-open {
|
||||||
transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1);
|
transform: translateX(0);
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
.ma-drawer .ma-side {
|
|
||||||
top: 0;
|
|
||||||
height: 55%;
|
|
||||||
border-bottom: 1px solid var(--m-border);
|
|
||||||
}
|
|
||||||
.ma-drawer .ma-widgets {
|
|
||||||
top: 55%;
|
|
||||||
height: 45%;
|
|
||||||
}
|
|
||||||
.ma-drawer.is-open .ma-side,
|
|
||||||
.ma-drawer.is-open .ma-widgets {
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calendar: full width, sem border-right (drawer agora é off-canvas) */
|
|
||||||
.ma-cal {
|
|
||||||
width: 100%;
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Toolbar mobile — filtros desktop somem, "Ações" e bloquear-mobile aparecem.
|
|
||||||
Pacientes button no header também. */
|
|
||||||
.ma-cal__filters,
|
|
||||||
.ma-cal__icon--desktop-only { display: none; }
|
|
||||||
.ma-cal__icon--mobile-only { display: grid; place-items: center; }
|
|
||||||
.ma-head-btn--mobile-only { display: inline-flex; }
|
|
||||||
|
|
||||||
/* Toolbar pode estourar com tantos elementos — permite wrap. */
|
|
||||||
.ma-cal__toolbar {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Backdrop: fica entre o drawer e o resto. Click fecha. */
|
/* Single-scroll container — o que faz o drawer ter UM scroll só.
|
||||||
.ma-drawer__backdrop {
|
Side e widgets dentro perdem seu próprio overflow no mobile. */
|
||||||
|
.ma-mobile-drawer__scroll {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 12px 12px 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--m-border-strong) transparent;
|
||||||
|
}
|
||||||
|
.ma-mobile-drawer__scroll::-webkit-scrollbar { width: 5px; }
|
||||||
|
.ma-mobile-drawer__scroll::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--m-border-strong);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quando teleportadas pra dentro do drawer, .ma-side e .ma-widgets
|
||||||
|
precisam não competir com o scroll do drawer e ocupar largura toda. */
|
||||||
|
.ma-mobile-drawer__scroll .ma-side,
|
||||||
|
.ma-mobile-drawer__scroll .ma-widgets {
|
||||||
|
width: 100%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: auto;
|
||||||
|
overflow: visible; /* perde o scroll próprio — o pai (.scroll) cuida */
|
||||||
|
border-right: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Backdrop: cobre tudo entre drawer e resto. Click fecha. */
|
||||||
|
.ma-mobile-drawer__backdrop {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(0, 0, 0, 0.45);
|
background: rgba(0, 0, 0, 0.45);
|
||||||
@@ -2472,4 +2564,51 @@ html:not(.app-dark) .ma-cal__fc :deep(.fc-timegrid-now-indicator-line) {
|
|||||||
.ma-drawer-fade-leave-to {
|
.ma-drawer-fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════
|
||||||
|
Responsivo <xl (≤1279px) — "compacto"
|
||||||
|
───────────────────────────────────────────────────────────────
|
||||||
|
- .ma-cal__filters (timeMode + onlySessions) saem da toolbar
|
||||||
|
(vão pro menu "Ações")
|
||||||
|
- .ma-cal__btn--xl-only (Bloquear-icon) some
|
||||||
|
- .ma-cal__btn--compact-only ("Ações") aparece
|
||||||
|
═══════════════════════════════════════════════════════════════ */
|
||||||
|
@media (max-width: 1279px) {
|
||||||
|
.ma-cal__filters,
|
||||||
|
.ma-cal__view--xl-only,
|
||||||
|
.ma-cal__btn--xl-only { display: none; }
|
||||||
|
.ma-cal__btn--compact-only { display: inline-flex; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════
|
||||||
|
Responsivo <lg (≤1023px) — "mobile"
|
||||||
|
───────────────────────────────────────────────────────────────
|
||||||
|
- .ma-side e .ma-widgets somem do .ma-body (Teleport os move pro
|
||||||
|
.ma-mobile-drawer fora do layout)
|
||||||
|
- .ma-cal vira fullwidth
|
||||||
|
- Botão "Menu" aparece à esquerda do header
|
||||||
|
- Toolbar pode wrap
|
||||||
|
═══════════════════════════════════════════════════════════════ */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.ma-body {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.ma-cal {
|
||||||
|
width: 100%;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Título some — "Menu Agenda" já carrega o nome da seção, evita
|
||||||
|
redundância e libera espaço pra Configurações + Fechar. */
|
||||||
|
.ma-page__title { display: none; }
|
||||||
|
|
||||||
|
/* Menu button (header esquerda) aparece */
|
||||||
|
.ma-menu-btn--mobile-only { display: inline-flex; }
|
||||||
|
|
||||||
|
/* Toolbar pode estourar com tantos elementos — permite wrap. */
|
||||||
|
.ma-cal__toolbar {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user