v1.0.4 popover ai

main
parent dc2e7441d6
commit 895c6d8599

@ -47,7 +47,7 @@ func NewApp() *App {
// startup вызывается при запуске приложения
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
runtime.WindowSetTitle(ctx, "v1.0.2")
runtime.WindowSetTitle(ctx, "v1.0.3")
runtime.WindowSetPosition(ctx, 1500, 80)
}

@ -9,6 +9,8 @@
import Servers from "./Servers.svelte";
import Utility from "./Utility.svelte";
import Projects from "./Projects.svelte";
import Popover from "./components/Popover.svelte";
import { popovers } from "./stores/popover.js";
// import {BrowserOpenURL} from '../wailsjs/runtime/runtime.js'
// Определение интерфейса для типа Disk
@ -100,6 +102,11 @@
</svg>
</button>
<!-- Контейнер для popovers -->
{#each $popovers as { content, triggerRect, options }}
<Popover {content} {triggerRect} {options} />
{/each}
<style>
/*
* Цветовая схема приложения:

@ -1,5 +1,8 @@
<script>
// @ts-nocheck
import { StartFarDisks } from "../wailsjs/go/main/App.js";
import PopoverTrigger from "./components/PopoverTrigger.svelte";
/** @type {Array<import('./App.svelte').Disk>} */
export let disks = [];
@ -7,13 +10,30 @@
<div class="container mx-auto pt-4 mb-4">
<div class="disk-panel text-neutral-300">
{#each disks as disk}
<div>
{disk.Name}
<a role="button" href="#top" on:click={StartFarDisks} class={disk.normal ? "info" : "danger"}>
{disk.Free} Gb
</a>
</div>
{#each disks as disk, i}
<PopoverTrigger
maxWidth="200px"
align={i === 0 ? "left" : i === disks.length - 1 ? "right" : "center"}
>
<div slot="trigger">
{disk.Name}
<a role="button" href="#top" on:click={StartFarDisks} class={disk.normal ? "info" : "danger"}>
{disk.Free} Gb
</a>
</div>
<div slot="content" class="popover-content">
<div class="title">Диск {disk.Name}</div>
<div class="info-row">
<span class="label">Свободно:</span>
<span class="value">{disk.Free} Gb</span>
</div>
<div class="info-row">
<span class="label">Состояние:</span>
<span class="value">{disk.normal ? 'нормальное' : 'требует внимания'}</span>
</div>
</div>
</PopoverTrigger>
{/each}
</div>
</div>
@ -32,4 +52,21 @@
a:hover {
text-decoration: underline;
}
:global(.popover-content) {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
:global(.popover-content .title) {
font-weight: bold;
margin-bottom: 0.5rem;
}
:global(.popover-content .info-row) {
display: flex;
justify-content: space-between;
gap: 1rem;
}
:global(.popover-content .label) {
color: #888;
}
</style>

@ -0,0 +1,48 @@
<script>
import { fade } from 'svelte/transition';
export let content;
export let triggerRect;
export let options = {};
$: style = {
position: 'fixed',
top: `${triggerRect.bottom}px`,
left: options.align === "left" ? `${triggerRect.left}px` :
options.align === "right" ? `${triggerRect.right}px` :
`${triggerRect.left + triggerRect.width / 2}px`,
transform: options.align === "left" ? "none" :
options.align === "right" ? "translateX(-100%)" :
"translateX(-50%)",
maxWidth: options.maxWidth || "300px"
};
</script>
<div
class="custom-popover"
style:position={style.position}
style:top={style.top}
style:left={style.left}
style:transform={style.transform}
style:max-width={style.maxWidth}
transition:fade={{ duration: 150 }}
>
{@html content}
</div>
<style>
.custom-popover {
background: #1a1a1a;
color: white;
padding: 0.5rem;
border-radius: 0.25rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
z-index: 1000;
border: 1px solid #333;
margin-top: 0.5rem;
word-wrap: break-word;
white-space: normal;
overflow-wrap: break-word;
hyphens: auto;
box-sizing: border-box;
}
</style>

@ -0,0 +1,50 @@
<script>
import { showPopover, hidePopover } from '../stores/popover.js';
import { onMount } from 'svelte';
let triggerElement;
export let maxWidth = "300px";
export let align = "center";
let contentElement;
let currentPopoverId = null;
function handleMouseEnter() {
const rect = triggerElement.getBoundingClientRect();
const content = contentElement.innerHTML;
currentPopoverId = showPopover(content, rect, { maxWidth, align });
}
function handleMouseLeave() {
if (currentPopoverId !== null) {
hidePopover(currentPopoverId);
currentPopoverId = null;
}
}
onMount(() => {
return () => {
if (currentPopoverId !== null) {
hidePopover(currentPopoverId);
}
};
});
</script>
<div
bind:this={triggerElement}
on:mouseenter={handleMouseEnter}
on:mouseleave={handleMouseLeave}
>
<slot name="trigger" />
</div>
<div bind:this={contentElement} style="display: none;">
<slot name="content" />
</div>
<style>
.relative {
position: relative;
}
</style>

@ -0,0 +1,15 @@
import { writable } from 'svelte/store';
export const popovers = writable([]);
let popoverId = 0;
export function showPopover(content, triggerRect, options = {}) {
const id = popoverId++;
popovers.update(items => [...items, { id, content, triggerRect, options }]);
return id;
}
export function hidePopover(id) {
popovers.update(items => items.filter(item => item.id !== id));
}

@ -23,4 +23,31 @@ body {
#app {
height: 100vh;
text-align: center;
}
}
/* Стили popover */
.popover {
position: absolute;
top: calc(100% + 4px); /* Отступ вниз */
right: 0; /* Выравнивание по правой границе */
padding: 10px;
background: #f8f9fa;
border: 1px solid #ccc;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
border-radius: 5px;
/* Подготовка для анимации */
opacity: 0;
transform: translateY(-10px);
pointer-events: none;
/* Длительность анимации 300ms */
transition: opacity 300ms ease, transform 300ms ease;
}
/* Анимация при открытии popover */
.popover:popover-open {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}

Loading…
Cancel
Save