This commit is contained in:
1 parent
a3322f9750
commit
6dac534f4e
2 files changed
+84
-18
No files matched your search
@@ -53,6 +53,30 @@
|
|||||||
let isAddingTag = $state(false);
|
let isAddingTag = $state(false);
|
||||||
let showTagDropdown = $state(false);
|
let showTagDropdown = $state(false);
|
||||||
|
|
||||||
|
let tagInputContainer = $state<HTMLDivElement>();
|
||||||
|
let tagDropdownTop = $state(0);
|
||||||
|
let tagDropdownBottom = $state(0);
|
||||||
|
let tagDropdownLeft = $state(0);
|
||||||
|
let tagDropdownWidth = $state(0);
|
||||||
|
let tagDropdownUpwards = $state(false);
|
||||||
|
|
||||||
|
function updateTagDropdownPosition() {
|
||||||
|
if (tagInputContainer) {
|
||||||
|
const rect = tagInputContainer.getBoundingClientRect();
|
||||||
|
const spaceBelow = window.innerHeight - rect.bottom;
|
||||||
|
const spaceAbove = rect.top;
|
||||||
|
if (spaceBelow < 200 && spaceAbove > spaceBelow) {
|
||||||
|
tagDropdownUpwards = true;
|
||||||
|
tagDropdownBottom = window.innerHeight - rect.top;
|
||||||
|
} else {
|
||||||
|
tagDropdownUpwards = false;
|
||||||
|
tagDropdownTop = rect.bottom;
|
||||||
|
}
|
||||||
|
tagDropdownLeft = rect.left;
|
||||||
|
tagDropdownWidth = rect.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let filteredTags = $derived(
|
let filteredTags = $derived(
|
||||||
(() => {
|
(() => {
|
||||||
const input = newTagInput.trim().toLowerCase();
|
const input = newTagInput.trim().toLowerCase();
|
||||||
@@ -121,6 +145,7 @@
|
|||||||
<Alert message={form.error} />
|
<Alert message={form.error} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<svelte:window onresize={() => (showTagDropdown = false)} />
|
||||||
<form method="POST" action="?/update" id="post-form" class="post-form">
|
<form method="POST" action="?/update" id="post-form" class="post-form">
|
||||||
<!-- 正文由页面统一持有并始终提交,避免切到「预览」时编辑器卸载导致正文丢失 -->
|
<!-- 正文由页面统一持有并始终提交,避免切到「预览」时编辑器卸载导致正文丢失 -->
|
||||||
<input type="hidden" name="content" value={content} />
|
<input type="hidden" name="content" value={content} />
|
||||||
@@ -172,7 +197,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-sidebar">
|
<div class="form-sidebar" onscroll={() => (showTagDropdown = false)}>
|
||||||
<EditorSidebarTabs {outline} bind:activeTab={sidebarTab} />
|
<EditorSidebarTabs {outline} bind:activeTab={sidebarTab} />
|
||||||
|
|
||||||
<!-- 设置面板始终挂载(仅切换显隐),确保别名/分类/系列/标签等字段在提交时始终存在 -->
|
<!-- 设置面板始终挂载(仅切换显隐),确保别名/分类/系列/标签等字段在提交时始终存在 -->
|
||||||
@@ -244,14 +269,17 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tag-input-container">
|
<div class="tag-input-container" bind:this={tagInputContainer}>
|
||||||
<TextInput
|
<TextInput
|
||||||
type="text"
|
type="text"
|
||||||
class="tag-input-new"
|
class="tag-input-new"
|
||||||
placeholder="+ 添加{$page.data.aliases?.tag || '标签'}..."
|
placeholder="+ 添加{$page.data.aliases?.tag || '标签'}..."
|
||||||
bind:value={newTagInput}
|
bind:value={newTagInput}
|
||||||
onkeydown={handleAddTag}
|
onkeydown={handleAddTag}
|
||||||
onfocus={() => (showTagDropdown = true)}
|
onfocus={() => {
|
||||||
|
updateTagDropdownPosition();
|
||||||
|
showTagDropdown = true;
|
||||||
|
}}
|
||||||
onblur={() => setTimeout(() => (showTagDropdown = false), 200)}
|
onblur={() => setTimeout(() => (showTagDropdown = false), 200)}
|
||||||
disabled={isAddingTag}
|
disabled={isAddingTag}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@@ -259,7 +287,16 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{#if showTagDropdown && filteredTags.length > 0}
|
{#if showTagDropdown && filteredTags.length > 0}
|
||||||
<div class="tag-autocomplete-dropdown">
|
<div
|
||||||
|
class="tag-autocomplete-dropdown"
|
||||||
|
style="top: {tagDropdownUpwards
|
||||||
|
? 'auto'
|
||||||
|
: `${tagDropdownTop}px`}; bottom: {tagDropdownUpwards
|
||||||
|
? `${tagDropdownBottom}px`
|
||||||
|
: 'auto'}; left: {tagDropdownLeft}px; width: {tagDropdownWidth}px; margin-top: {tagDropdownUpwards
|
||||||
|
? '0'
|
||||||
|
: '4px'}; margin-bottom: {tagDropdownUpwards ? '4px' : '0'};"
|
||||||
|
>
|
||||||
{#each filteredTags as tag (tag.id || tag)}
|
{#each filteredTags as tag (tag.id || tag)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -474,10 +511,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tag-autocomplete-dropdown {
|
.tag-autocomplete-dropdown {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
border: 1px solid var(--text-color);
|
border: 1px solid var(--text-color);
|
||||||
box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.15);
|
box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.15);
|
||||||
@@ -486,7 +520,6 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-top: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-dropdown-item {
|
.tag-dropdown-item {
|
||||||
|
|||||||
@@ -71,6 +71,30 @@
|
|||||||
let isAddingTag = $state(false);
|
let isAddingTag = $state(false);
|
||||||
let showTagDropdown = $state(false);
|
let showTagDropdown = $state(false);
|
||||||
|
|
||||||
|
let tagInputContainer = $state<HTMLDivElement>();
|
||||||
|
let tagDropdownTop = $state(0);
|
||||||
|
let tagDropdownBottom = $state(0);
|
||||||
|
let tagDropdownLeft = $state(0);
|
||||||
|
let tagDropdownWidth = $state(0);
|
||||||
|
let tagDropdownUpwards = $state(false);
|
||||||
|
|
||||||
|
function updateTagDropdownPosition() {
|
||||||
|
if (tagInputContainer) {
|
||||||
|
const rect = tagInputContainer.getBoundingClientRect();
|
||||||
|
const spaceBelow = window.innerHeight - rect.bottom;
|
||||||
|
const spaceAbove = rect.top;
|
||||||
|
if (spaceBelow < 200 && spaceAbove > spaceBelow) {
|
||||||
|
tagDropdownUpwards = true;
|
||||||
|
tagDropdownBottom = window.innerHeight - rect.top;
|
||||||
|
} else {
|
||||||
|
tagDropdownUpwards = false;
|
||||||
|
tagDropdownTop = rect.bottom;
|
||||||
|
}
|
||||||
|
tagDropdownLeft = rect.left;
|
||||||
|
tagDropdownWidth = rect.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let filteredTags = $derived(
|
let filteredTags = $derived(
|
||||||
(() => {
|
(() => {
|
||||||
const input = newTagInput.trim().toLowerCase();
|
const input = newTagInput.trim().toLowerCase();
|
||||||
@@ -139,6 +163,7 @@
|
|||||||
<Alert message={form.error} />
|
<Alert message={form.error} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<svelte:window onresize={() => (showTagDropdown = false)} />
|
||||||
<form method="POST" id="post-form" class="post-form">
|
<form method="POST" id="post-form" class="post-form">
|
||||||
<!-- 正文由页面统一持有并始终提交,避免切到「预览」时编辑器卸载导致正文丢失 -->
|
<!-- 正文由页面统一持有并始终提交,避免切到「预览」时编辑器卸载导致正文丢失 -->
|
||||||
<input type="hidden" name="content" value={content} />
|
<input type="hidden" name="content" value={content} />
|
||||||
@@ -190,7 +215,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-sidebar">
|
<div class="form-sidebar" onscroll={() => (showTagDropdown = false)}>
|
||||||
<EditorSidebarTabs {outline} bind:activeTab={sidebarTab} />
|
<EditorSidebarTabs {outline} bind:activeTab={sidebarTab} />
|
||||||
|
|
||||||
<!-- 设置面板始终挂载(仅切换显隐),确保别名/分类/系列/标签等字段在提交时始终存在 -->
|
<!-- 设置面板始终挂载(仅切换显隐),确保别名/分类/系列/标签等字段在提交时始终存在 -->
|
||||||
@@ -274,14 +299,17 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tag-input-container">
|
<div class="tag-input-container" bind:this={tagInputContainer}>
|
||||||
<TextInput
|
<TextInput
|
||||||
type="text"
|
type="text"
|
||||||
class="tag-input-new"
|
class="tag-input-new"
|
||||||
placeholder="+ 添加{$page.data.aliases?.tag || '标签'}..."
|
placeholder="+ 添加{$page.data.aliases?.tag || '标签'}..."
|
||||||
bind:value={newTagInput}
|
bind:value={newTagInput}
|
||||||
onkeydown={handleAddTag}
|
onkeydown={handleAddTag}
|
||||||
onfocus={() => (showTagDropdown = true)}
|
onfocus={() => {
|
||||||
|
updateTagDropdownPosition();
|
||||||
|
showTagDropdown = true;
|
||||||
|
}}
|
||||||
onblur={() => setTimeout(() => (showTagDropdown = false), 200)}
|
onblur={() => setTimeout(() => (showTagDropdown = false), 200)}
|
||||||
disabled={isAddingTag}
|
disabled={isAddingTag}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@@ -289,7 +317,16 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{#if showTagDropdown && filteredTags.length > 0}
|
{#if showTagDropdown && filteredTags.length > 0}
|
||||||
<div class="tag-autocomplete-dropdown">
|
<div
|
||||||
|
class="tag-autocomplete-dropdown"
|
||||||
|
style="top: {tagDropdownUpwards
|
||||||
|
? 'auto'
|
||||||
|
: `${tagDropdownTop}px`}; bottom: {tagDropdownUpwards
|
||||||
|
? `${tagDropdownBottom}px`
|
||||||
|
: 'auto'}; left: {tagDropdownLeft}px; width: {tagDropdownWidth}px; margin-top: {tagDropdownUpwards
|
||||||
|
? '0'
|
||||||
|
: '4px'}; margin-bottom: {tagDropdownUpwards ? '4px' : '0'};"
|
||||||
|
>
|
||||||
{#each filteredTags as tag (tag.id || tag)}
|
{#each filteredTags as tag (tag.id || tag)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -504,10 +541,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tag-autocomplete-dropdown {
|
.tag-autocomplete-dropdown {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
border: 1px solid var(--text-color);
|
border: 1px solid var(--text-color);
|
||||||
box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.15);
|
box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.15);
|
||||||
@@ -516,7 +550,6 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-top: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-dropdown-item {
|
.tag-dropdown-item {
|
||||||
|
|||||||
Reference in new issue
Block a user