187 lines
5.5 KiB
Svelte
187 lines
5.5 KiB
Svelte
<svelte:options customElement={{ props: {} }} />
|
|
|
|
<script lang="ts">
|
|
import type {Snippet} from 'svelte';
|
|
import {Render} from './index';
|
|
|
|
interface Item {
|
|
key: string;
|
|
icon?: string | Snippet;
|
|
title: string | Snippet;
|
|
badge?: string | Snippet;
|
|
titleHelp?: string;
|
|
description?: string | Snippet;
|
|
content: string | Snippet;
|
|
}
|
|
|
|
let { items, onChange, activeKeys = $bindable([]), ...rest }: {
|
|
items: Item[],
|
|
onChange?: (item: Item, activeKeys: string[]) => void,
|
|
activeKeys?: string[],
|
|
[key: string]: any
|
|
} = $props();
|
|
|
|
function handlerOnChange(item: Item) {
|
|
if (activeKeys.includes(item.key)) {
|
|
activeKeys = activeKeys.filter(key => key !== item.key);
|
|
} else {
|
|
activeKeys.push(item.key);
|
|
activeKeys = activeKeys;
|
|
}
|
|
onChange?.(item, activeKeys);
|
|
}
|
|
|
|
</script>
|
|
<div style={rest.style} class="tf-collapse {rest.class}">
|
|
{#each items as item, index}
|
|
<div class="tf-collapse-item">
|
|
<div class="tf-collapse-item-title tf-node-drag-handle" role="button" tabindex={index}
|
|
onclick={() => handlerOnChange(item)}
|
|
onkeydown={(event) => {
|
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
event.preventDefault();
|
|
handlerOnChange(item);
|
|
}
|
|
}}
|
|
>
|
|
{#if item.icon}
|
|
<span class="tf-collapse-item-title-icon">
|
|
<Render target={item.icon} />
|
|
</span>
|
|
{/if}
|
|
|
|
<Render target={item.title} />
|
|
{#if item.badge}
|
|
<span class="tf-collapse-item-title-badge">
|
|
<Render target={item.badge} />
|
|
</span>
|
|
{/if}
|
|
{#if item.titleHelp}
|
|
<span
|
|
class="tf-collapse-item-title-help"
|
|
data-help={item.titleHelp}
|
|
aria-label="节点使用说明"
|
|
>
|
|
?
|
|
</span>
|
|
{/if}
|
|
|
|
<span class="tf-collapse-item-title-arrow {activeKeys.includes(item.key) ? 'rotate-90' : ''}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path
|
|
d="M13.1717 12.0007L8.22192 7.05093L9.63614 5.63672L16.0001 12.0007L9.63614 18.3646L8.22192 16.9504L13.1717 12.0007Z"></path></svg>
|
|
</span>
|
|
</div>
|
|
{#if item.description}
|
|
<div class="tf-collapse-item-description">
|
|
<Render target={item.description} />
|
|
</div>
|
|
{/if}
|
|
|
|
{#if activeKeys.includes(item.key)}
|
|
<div class="tf-collapse-item-content">
|
|
<Render target={item.content} />
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
|
|
|
|
<style>
|
|
/* 定义旋转的 CSS 类 */
|
|
.rotate-90 {
|
|
transform: rotate(90deg);
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.tf-collapse-item-title-help {
|
|
margin-left: 6px;
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
border: 1px solid var(--tf-border-color-strong);
|
|
color: var(--tf-text-secondary);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 11px;
|
|
line-height: 1;
|
|
cursor: default;
|
|
user-select: none;
|
|
background: var(--tf-bg-surface);
|
|
position: relative;
|
|
}
|
|
|
|
.tf-collapse-item-title-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-height: 18px;
|
|
padding: 1px 6px;
|
|
margin-left: 8px;
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
line-height: 1.2;
|
|
color: var(--tf-primary-color);
|
|
white-space: nowrap;
|
|
background: var(--tf-primary-soft-bg);
|
|
border: 1px solid var(--tf-primary-soft-border);
|
|
border-radius: 999px;
|
|
}
|
|
|
|
.tf-collapse-item-title-help:hover {
|
|
border-color: var(--tf-border-color-strong);
|
|
color: var(--tf-text-primary);
|
|
background: var(--tf-bg-hover);
|
|
}
|
|
|
|
.tf-node-drag-handle {
|
|
cursor: grab;
|
|
}
|
|
|
|
:global(.svelte-flow__node.dragging) .tf-node-drag-handle {
|
|
cursor: grabbing;
|
|
}
|
|
|
|
.tf-collapse-item-title-help::after {
|
|
content: attr(data-help);
|
|
position: absolute;
|
|
left: calc(100% + 10px);
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
min-width: 260px;
|
|
max-width: 420px;
|
|
white-space: pre-line;
|
|
background: var(--tf-tip-bg);
|
|
color: var(--tf-tip-text);
|
|
border-radius: 8px;
|
|
padding: 10px 12px;
|
|
font-size: 12px;
|
|
line-height: 1.45;
|
|
box-shadow: var(--tf-shadow-medium);
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
pointer-events: none;
|
|
z-index: 120;
|
|
}
|
|
|
|
.tf-collapse-item-title-help::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: calc(100% + 4px);
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
border-top: 6px solid transparent;
|
|
border-bottom: 6px solid transparent;
|
|
border-right: 6px solid var(--tf-tip-bg);
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
z-index: 121;
|
|
}
|
|
|
|
.tf-collapse-item-title-help:hover::after,
|
|
.tf-collapse-item-title-help:hover::before {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
}
|
|
</style>
|