mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-10 13:38:49 +08:00
fix(editor): single-letter tags in select/multi-select table cell (#14808)
### Summary of Changes Resolves #14715 and #14280. When a user types into a **Select/Multi-Select** table cell to create/choose a tag, that character is stashed on the cell container (setTagDraft) instead of going through valueSetFromString. Opening the tag picker reads it via consumeTagDraftFromTableCellHost. ### Verification - Added unit test to check that single-character input doesn't immediately call valueSetFromString. https://github.com/user-attachments/assets/432b2693-52f9-4ab4-a694-8440aea007a3 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Tag selection popups now initialize with draft text from keypresses in tag columns, improving user experience when editing tags. * **Tests** * Added comprehensive hotkey tests for single-select and multi-select tag column behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -69,8 +69,20 @@ export type TagManagerOptions = {
|
||||
options: ReadonlySignal<SelectTag[]>;
|
||||
onOptionsChange: (options: SelectTag[]) => void;
|
||||
onComplete?: () => void;
|
||||
initialDraftText?: string;
|
||||
};
|
||||
|
||||
// parent elements that can consume tag draft
|
||||
const TABLE_CELL_HOST_SELECTOR =
|
||||
'dv-table-view-cell-container, affine-database-virtual-cell-container';
|
||||
|
||||
export function consumeTagDraftFromTableCellHost(
|
||||
fromElement: Element
|
||||
): string | undefined {
|
||||
const host = fromElement.closest(TABLE_CELL_HOST_SELECTOR) as any;
|
||||
return host?.consumeTagDraft?.();
|
||||
}
|
||||
|
||||
class TagManager {
|
||||
changeTag = (option: Partial<SelectTag>) => {
|
||||
this.ops.onOptionsChange(
|
||||
@@ -427,6 +439,15 @@ export class MultiTagSelect extends SignalWatcher(
|
||||
);
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
const draft = this.initialDraftText;
|
||||
if (draft != null && draft !== '') {
|
||||
this.tagManager.text$.value = draft;
|
||||
this.initialDraftText = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected override firstUpdated() {
|
||||
const disposables = this.disposables;
|
||||
this.classList.add(tagSelectContainerStyle);
|
||||
@@ -471,6 +492,9 @@ export class MultiTagSelect extends SignalWatcher(
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor value!: ReadonlySignal<string[]>;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor initialDraftText: string | undefined;
|
||||
}
|
||||
|
||||
declare global {
|
||||
@@ -481,6 +505,9 @@ declare global {
|
||||
|
||||
const popMobileTagSelect = (target: PopupTarget, ops: TagSelectOptions) => {
|
||||
const tagManager = new TagManager(ops);
|
||||
if (ops.initialDraftText) {
|
||||
tagManager.text$.value = ops.initialDraftText;
|
||||
}
|
||||
const onInput = (e: InputEvent) => {
|
||||
tagManager.text$.value = (e.target as HTMLInputElement).value;
|
||||
};
|
||||
@@ -604,6 +631,7 @@ export const popTagSelect = (target: PopupTarget, ops: TagSelectOptions) => {
|
||||
component.onChange = ops.onChange;
|
||||
component.options = ops.options;
|
||||
component.onOptionsChange = ops.onOptionsChange;
|
||||
component.initialDraftText = ops.initialDraftText;
|
||||
component.onComplete = () => {
|
||||
ops.onComplete?.();
|
||||
remove();
|
||||
|
||||
Reference in New Issue
Block a user