Files
OSIT-AE-App-Svelte/src/lib/app_components/e_app_sys_menu.svelte
Scott Idem 95508458f6 ui: hide Session Search nav on its own page, dark mode file list, font size cycler
- event_page_menu: set events__session_search=false — the Session Search nav
  link was redundantly appearing on the Session Search page itself

- element_manage_event_file_li: replace hardcoded gray hover colors with
  theme-aware surface tokens (hover:bg-surface-100-900, border-surface-200-800)
  and add transition-colors; fixes light-on-light in dark mode for the file
  list table rows and Event File Purpose select element

- font size cycler (default → larger → smaller → default):
  - ae_stores: add font_size_mode: 'default' to ae_loc defaults
  - app.css: html.font-size-larger (112.5%) and html.font-size-smaller (87.5%)
  - +layout.svelte: DOM effect applies/removes font-size-* class on <html>
  - e_app_sys_menu: compact A / A+ / A− button cycles the mode
2026-03-06 21:35:06 -05:00

599 lines
23 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
// *** Import Svelte specific
// import { tick } from 'svelte';
// import { goto, invalidateAll } from '$app/navigation';
// *** Import other supporting libraries
import {
// ArrowBigRight,
// Bug,
CircleX,
// Eye, EyeOff,
// Key,
// LogIn, LogOut, LockKeyhole,
// Mail, MailCheck,
Menu,
// RefreshCw, RefreshCcwDot,
ShieldEllipsis,
ShieldMinus,
ShieldPlus,
ShieldUser,
// ToggleLeft, ToggleRight,
User,
UserCheck,
X
} from '@lucide/svelte';
// *** Import Aether specific variables and functions
// import { ae_util } from '$lib/ae_utils/ae_utils';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores';
import Element_access_type from '$lib/app_components/e_app_access_type.svelte';
import Element_app_cfg from '$lib/app_components/e_app_cfg.svelte';
import Element_sign_in_out from '$lib/app_components/e_app_sign_in_out.svelte';
import Element_theme from '$lib/app_components/e_app_theme.svelte';
// *** Setup Svelte properties
interface Props {
log_lvl?: number;
data: any;
hide?: null | boolean;
expand?: boolean;
}
let {
log_lvl = $bindable(0),
data = null,
hide = $bindable(false),
expand = $bindable(false)
}: Props = $props();
let trigger_clear_access: null | boolean = $state(null);
</script>
<!-- App System Menu -->
<!-- min-h-full
max-h-max
min-w-full
max-w-max -->
<!-- <section
class="
ae_app__menu
hidden-print
flex flex-col
items-end justify-center
gap-1
absolute right-0 bottom-16
hover:bottom-6
bg-white dark:bg-gray-800
border border-transparent
hover:border hover:border-gray-200 hover:dark:border-gray-700
rounded-lg
p-2
*:hover:inline
opacity-40
hover:opacity-100
duration-50 delay-1000 hover:delay-100 hover:ease-out
transition hover:transition-all
z-10 hover:z-20
"
class:hidden={!expand_btn}
> -->
<!-- && !expand -->
<!-- !$ae_loc
?.sys_menu?.expand_btn -->
<!-- bg-blue-100/60 dark:bg-blue-800/50 -->
<!-- class:hover:border-transparent={!expand} -->
<!-- mx-1 my-2 -->
<!-- We need to be able to hide the menu button in certain situations. Mainly iframes. -->
<section
class="
ae_app__sys_menu
hidden-print
z-50
absolute bottom-6 right-0
opacity-90
hover:opacity-100
w-min
max-w-md
flex flex-col-reverse gap-1
items-end justify-center
text-sm sm:text-sm md:text-md lg:text-md xl:text-md 2xl:text-lg
bg-blue-200/90
border-2 rounded-lg
border-blue-300/20 dark:border-blue-700/20
hover:border-blue-500/20 hover:dark:border-blue-500/20
transition-all
delay-500 hover:delay-200
duration-500 hover:duration-200
ease-in-out
"
class:top-0={expand && (1 as any) == 3}
class:opacity-100={expand}
class:w-full={expand}
class:hidden={hide}
class:border-transparent={!expand}
class:bg-transparent={!expand}
>
<!-- class:hidden={!expand} -->
<!-- class:preset-filled-warning-100-900={expand} -->
<div
class:opacity-50={!expand}
class:opacity-100={expand}
class:light:bg-blue-500={expand}
class:dark:bg-blue-500={expand}
class="
hidden-print
flex flex-col items-end justify-end
gap-1
overflow-y-auto
p-1
w-full
light:bg-blue-200/10
dark:bg-blue-800/10
hover:opacity-100
relative
transition-all
delay-1000 hover:delay-500
duration-200 hover:duration-200
ease-in-out
"
title="
ID: {$ae_loc?.person_id ?? '-- not set --'} / {$ae_loc?.user_id ?? '-- not set --'}
Name: {$ae_loc?.person?.full_name ?? '-- not set --'}
Username: {$ae_loc?.user?.username ?? '-- not set --'}
Email: {$ae_loc?.user?.email ?? '-- not set --'}
Access Type: {$ae_loc?.access_type ?? '-- not set --'}
"
>
{#if $ae_loc?.person_id}
<div class="flex flex-row gap-1 items-center justify-end transition-all w-full group">
<User size="1em" class="mx-1 inline-block text-gray-500" />
<span class:hidden={!expand} class="group-hover:inline-block">
{$ae_loc?.person?.informal_name ?? $ae_loc?.person?.given_name}
</span>
</div>
{/if}
{#if $ae_loc?.user_id}
<!-- This is currently not set to show if not expanded. Saving space. -->
<div
class:hidden={!expand}
class="flex flex-row gap-1 items-center justify-end transition-all w-full group"
>
<ShieldUser size="1em" class="mx-1 inline-block text-gray-500" />
<span class:hidden={!expand} class="group-hover:inline-block">
{$ae_loc?.user?.username ?? '-- not set --'}
</span>
</div>
{/if}
<div class="flex flex-row gap-1 items-center justify-end transition-all w-full group">
{#if $ae_loc.access_type && $ae_loc.access_type != 'anonymous'}
<span
class="flex flex-row-reverse gap-1 group text-base"
title={`Current access type/level: ${$ae_loc.access_type}`}
>
<!-- <span class="fas fa-unlock mx-1"></span> -->
<!-- <ShieldPlus class="inline-block" /> -->
{#if $ae_loc.access_type == 'super'}
<span class="fas fa-hat-wizard m-1"></span>
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Super</span
>
{:else if $ae_loc.access_type == 'manager'}
<span class="fas fa-user-shield m-1"></span>
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Manager</span
>
{:else if $ae_loc.access_type == 'administrator'}
<span class="fas fa-user-ninja m-1"></span>
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Administrator</span
>
{:else if $ae_loc.access_type == 'trusted'}
<span class="fas fa-user-check m-1"></span>
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Trusted Access</span
>
{:else if $ae_loc.access_type == 'public'}
Public
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Access</span
>
{:else if $ae_loc.access_type == 'authenticated'}
Authenticated
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Access</span
>
{:else if $ae_loc.access_type == 'anonymous'}
Anonymous Access
{:else}
Unknown Access
{/if}
</span>
{#if $ae_loc?.user_access_type && $ae_loc?.access_type == $ae_loc?.user_access_type}
<button
type="button"
onclick={() => {
// handle_clear_access();
// trigger_clear_access = true;
// $ae_loc.app_cfg.show_element__passcode_input = !$ae_loc.app_cfg.show_element__passcode_input;
if (!expand) {
expand = true;
$ae_sess.sys_menu.expand = true;
$ae_loc.sys_menu.hide_access_type = false;
$ae_loc.sys_menu.expand_access_type = true;
// $ae_loc.sys_menu.expand_btn = false;
// $ae_loc.app_cfg.show_element__access_type = true;
// $ae_loc.app_cfg.show_element__passcode_input = true;
} else {
// expand = true;
// $ae_loc.sys_menu.expand = false;
$ae_loc.sys_menu.hide_access_type = false;
$ae_loc.sys_menu.expand_access_type = true;
// $ae_loc.sys_menu.expand_btn = true;
}
}}
class="btn btn-sm text-xs variant-outline-surface hover:variant-ghost-warning transition-all group"
title={`Current user access level: "${$ae_loc.user_access_type}". Click use passcode for a different access level.`}
>
<!-- <span class="fas fa-lock mx-1"></span> -->
<!-- <ShieldMinus /> -->
<ShieldEllipsis size="2em" class="inline-block" />
<span class="hidden group-hover:inline-block">Passcode?</span>
</button>
{:else}
<button
type="button"
onclick={() => {
trigger_clear_access = true;
if (expand) {
$ae_loc.sys_menu.hide_access_type = false;
// $ae_loc.sys_menu.expand_access_type = false;
expand = false;
// $ae_loc.sys_menu.expand = true;
// $ae_loc.sys_menu.expand_btn = false;
$ae_loc.app_cfg.show_element__access_type = true;
$ae_sess.app_cfg.show_element__passcode_input = true;
// await tick();
// console.log('Layout button click: Focus on passcode input!');
// /** @type {HTMLElement | null} */
// const to_focus = document.getElementById('access_passcode_input');
// to_focus?.focus();
} else {
$ae_loc.sys_menu.hide_access_type = false;
$ae_loc.sys_menu.expand_access_type = true;
// $ae_loc.sys_menu.expand = false;
// $ae_loc.sys_menu.expand_btn = true;
}
}}
class="
btn btn-sm text-xs
flex-row-reverse
variant-outline-surface hover:variant-ghost-warning
transition-all group
"
title={`Current access level: "${$ae_loc.access_type}". Click to clear the temporary access level.`}
>
<!-- <span class="fas fa-lock mx-1"></span> Lock? -->
<ShieldMinus class="inline-block" />
<span class="hidden group-hover:inline-block"> Clear? </span>
</button>
{/if}
{:else}
<button
type="button"
onclick={async () => {
expand = true;
$ae_sess.sys_menu.expand = true;
$ae_loc.sys_menu.hide_access_type = false;
$ae_loc.sys_menu.expand_access_type = true;
$ae_sess.app_cfg.show_element__passcode_input = true;
$ae_sess.sys_menu.focus_passcode_input = true;
// $ae_loc.sys_menu.expand = true;
// $ae_loc.sys_menu.expand_btn = false;
// $ae_loc.app_cfg.show_element__access_type = true;
// $ae_loc.app_cfg.show_element__passcode_input = true;
// await tick();
console.log('Layout button click: Focus on passcode input!');
/** @type {HTMLElement | null} */
const to_focus = document.getElementById('access_passcode_input');
to_focus?.focus();
}}
class="
btn btn-sm text-xs
flex-row-reverse
variant-outline-surface hover:variant-ghost-success
transition-all group
"
title="Anonymous public access is currently set. You must Sign In or use a passcode to change your access level."
>
<!-- <span class="fas fa-lock mx-1 lock_icon"></span> -->
<!-- <span class="">Unlock?</span> -->
<ShieldUser class="inline-block" />
<span class="hidden group-hover:inline-block">Auth?</span>
</button>
{/if}
</div>
{#if $ae_loc.edit_mode}
<button
type="button"
onclick={() => {
$ae_loc.edit_mode = false;
// dispatch_edit_mode_changed();
}}
class="
btn btn-base text-sm
preset-tonal-warning
preset-outlined-warning-800-200
hover:preset-tonal-success
transition-all group
min-w-22 md:min-w-30 w-full max-w-fit
gap-1
"
title="Click to turn off edit mode. Edit mode is currently on."
>
<span class="fas fa-toggle-on m-1 inline-block"></span>
<span class="text-xs">Edit</span>
<span class="hidden group-hover:inline-block group-hover:text-xs"> Off </span>
</button>
{:else if $ae_loc.authenticated_access}
<button
type="button"
onclick={() => {
$ae_loc.edit_mode = true;
// dispatch_edit_mode_changed();
}}
class="
btn btn-base text-sm
preset-tonal-surface
preset-outlined-warning-400-600
hover:preset-tonal-warning
transition-all group
min-w-22 md:min-w-30 w-full max-w-fit
gap-1
"
title="Click to torn on/enable edit mode. Edit mode is currently off/disabled."
>
<span class="fas fa-toggle-off m-1 inline-block"></span>
<span class="text-xs">Edit</span>
<span class="hidden group-hover:inline-block group-hover:text-xs"> On? </span>
</button>
{/if}
<!-- Font Size Cycler: default → larger → smaller → default -->
<button
type="button"
class:w-full={expand}
class="
btn btn-base text-sm
preset-tonal-surface
hover:preset-tonal-primary
px-1 py-1
min-w-22 md:min-w-30 w-full max-w-fit
transition-all group
"
title="Cycle font size: default → larger → smaller"
onclick={() => {
const mode = $ae_loc.font_size_mode;
if (!mode || mode === 'default') {
$ae_loc.font_size_mode = 'larger';
} else if (mode === 'larger') {
$ae_loc.font_size_mode = 'smaller';
} else {
$ae_loc.font_size_mode = 'default';
}
}}
>
{#if !$ae_loc.font_size_mode || $ae_loc.font_size_mode === 'default'}
<span class="font-bold text-sm leading-none">A</span>
<span class="hidden group-hover:inline-block text-xs">Font: Normal</span>
{:else if $ae_loc.font_size_mode === 'larger'}
<span class="font-bold text-base leading-none">A+</span>
<span class="hidden group-hover:inline-block text-xs">Font: Larger</span>
{:else}
<span class="font-bold text-xs leading-none">A</span>
<span class="hidden group-hover:inline-block text-xs">Font: Smaller</span>
{/if}
</button>
<!-- <div> -->
<!-- class:visible={expand} -->
<!-- class:invisible={!expand} -->
<!-- class:hover:visible={true} -->
<!-- invisible -->
<button
type="button"
class:w-full={expand}
class="
btn btn-base text-sm
preset-filled-tertiary-400-600
preset-outlined-tertiary-400-600
hover:preset-filled-success active:preset-filled-success
px-1 py-1
min-w-22 md:min-w-30 w-full max-w-fit
transition-all group
"
title="Show/Hide the system menu"
onclick={async () => {
if (!expand) {
expand = true;
$ae_sess.sys_menu.expand = true;
// $ae_loc.sys_menu.expand = true;
// $ae_loc.sys_menu.expand_btn = false;
$ae_loc.app_cfg.show_element__access_type = true;
if ($ae_loc?.access_type == 'anonymous') {
$ae_sess.sys_menu.focus_passcode_input = true;
// $ae_sess.app_cfg.show_element__passcode_input = true;
} else {
// $ae_sess.app_cfg.show_element__passcode_input = false;
$ae_loc.sys_menu.expand_user = false; // Not in use yet
$ae_sess.show__sign_in_out__fields = false;
}
// $ae_loc.app_cfg.show_element__passcode_input = true;
// await tick();
// console.log('Layout button click: Focus on passcode input!');
// /** @type {HTMLElement | null} */
// const to_focus = document.getElementById('access_passcode_input');
// to_focus?.focus();
} else {
expand = false;
$ae_sess.sys_menu.expand = false;
// $ae_loc.sys_menu.expand = false;
// $ae_loc.sys_menu.expand_btn = true;
// $ae_loc.app_cfg.show_element__passcode_input = false;
}
// $ae_loc.sys_menu.expand_btn = !expand_btn;
}}
>
<!-- <span class=""> -->
{#if expand}
<CircleX class="m-1 inline-block" />
{:else}
<Menu class="m-1 inline-block" />
{/if}
<span class="text-xs hidden group-hover:inline-block"> Menu </span>
<!-- </span> -->
</button>
<!-- </div> -->
</div>
<!-- Show menu on right side of page -->
<!-- min-h-96 -->
<!-- absolute right-0 bottom-10 -->
<!-- opacity-50 -->
<!-- hover:opacity-100 -->
<!-- bg-white dark:bg-gray-800 -->
<div
class:preset-filled-warning-200-800={expand}
class:hidden={!expand}
class="
ae_app__sys_menu
hidden-print
flex flex-col
items-end
justify-end
gap-2
min-w-48
bg-white dark:bg-gray-800
border border-gray-200 dark:border-gray-700
rounded-lg
px-1 py-2
transition-all
delay-1000 hover:delay-100
duration-100 hover:duration-200
ease-in-out
z-20 hover:z-30
"
>
<button
type="button"
class:w-full={expand}
class="
btn btn-sm
preset-filled-tertiary-400-600
preset-outlined-tertiary-400-600
hover:preset-filled-success active:preset-filled-success
px-6 py-1
transition-all group
"
title="Show/Hide the system menu"
onclick={() => {
if (!expand) {
expand = true;
$ae_sess.sys_menu.expand = true;
// $ae_loc.sys_menu.expand_btn = false;
// $ae_loc.sys_menu.expand_access_type = true;
// $ae_sess.app_cfg.show_element__passcode_input = true;
$ae_sess.sys_menu.focus_passcode_input = true;
} else {
$ae_sess.sys_menu.expand = false;
$ae_loc.sys_menu.expand_user = false; // Not in use yet
$ae_sess.show__sign_in_out__fields = false;
// $ae_loc.sys_menu.expand_btn = true;
}
// $ae_loc.sys_menu.expand_btn = !expand_btn;
// $ae_loc.sys_menu.expand = !expand;
}}
>
{#if expand}
<CircleX class="m-1 inline-block" />
<span class="hidden group-hover:inline-block"> Hide Menu </span>
{:else}
<Menu class="m-1 inline-block" />
<span class="hidden group-hover:inline-block"> Menu </span>
{/if}
</button>
<!-- {#if $ae_loc?.app_cfg?.show_element__cfg} -->
<span class:hidden={!$ae_loc.edit_mode}>
<Element_app_cfg
hide={$ae_loc.sys_menu.hide_app_cfg}
expand={$ae_loc.sys_menu.expand_app_cfg}
/>
</span>
<span class:hidden={!$ae_loc.edit_mode}>
<Element_theme
hide={$ae_loc.sys_menu.hide_app_cfg}
expand={$ae_loc.sys_menu.expand_app_cfg}
set_theme_mode={true}
set_theme_name={true}
/>
</span>
<!-- {/if} -->
{#if $ae_loc?.app_cfg?.show_element__sign_in_out}
<!-- hide={$ae_loc.sys_menu.hide_user} -->
<!-- expand={$ae_loc.sys_menu.expand_user} -->
<Element_sign_in_out
{data}
hidden={$ae_loc.iframe || !$ae_loc.app_cfg?.show_element__sign_in_out}
/>
{/if}
{#if !$ae_loc?.sys_menu?.hide_access_type && !$ae_loc?.iframe}
<!-- hidden={$ae_loc?.iframe && !$ae_loc?.trusted_access && !expand} -->
<Element_access_type
bind:hide={$ae_loc.sys_menu.hide_access_type}
bind:focus_input={$ae_sess.sys_menu.focus_passcode_input}
bind:expand={$ae_loc.sys_menu.expand_access_type}
bind:show_passcode_input={$ae_sess.app_cfg.show_element__passcode_input}
bind:trigger_clear_access
/>
{/if}
</div>
</section>