fix(quiet-outline): fix residual decorators after change settings

For rendering reasons, the old class names will not be removed, so
they need to be handled by oneself.
This commit is contained in:
dragonish 2025-05-28 20:31:11 +08:00
parent ab58b4f1a2
commit c7657ab7d1
No known key found for this signature in database
GPG key ID: 6F42FA9E807A5177
4 changed files with 205 additions and 144 deletions

View file

@ -40,7 +40,7 @@ export type DecoratorOptions =
| OrderedDecoratorOptions
| UnorderedDecoratorOptions;
interface HeadingDecoratorSettings {
export interface HeadingDecoratorSettings {
enabledInEachNote?: boolean;
ordered: boolean;
@ -106,6 +106,8 @@ export const outlineHeadingDecoratorClassName =
"outline-custom-heading-decorator";
export const quietOutlineHeadingDecoratorClassName =
"quiet-outline-custom-heading-decorator";
export const quietOutlineContainerClassName =
"quiet-outline-custom-heading-container";
export const fileExplorerHeadingDecoratorClassName =
"file-explorer-custom-heading-decorator";
export const beforeDecoratorClassName = "before-heading-decorator";

View file

@ -7,7 +7,6 @@ import {
afterDecoratorClassName,
afterInsideDecoratorClassName,
outlineHeadingDecoratorClassName,
quietOutlineHeadingDecoratorClassName,
fileExplorerHeadingDecoratorClassName,
compareMarkdownText,
} from "./data";
@ -198,82 +197,6 @@ export function cancelOutlineDecorator(element: HTMLElement): void {
}
}
/**
* Query the heading level of an HTML element that is part of the quiet outline.
*
* @param element
* @returns The heading level or `-1` if not found.
*/
export function queryHeadingLevelByQuietOutlineElement(
element: HTMLElement
): number {
const classList = element.classList;
if (classList.contains("level-1")) {
return 1;
} else if (classList.contains("level-2")) {
return 2;
} else if (classList.contains("level-3")) {
return 3;
} else if (classList.contains("level-4")) {
return 4;
} else if (classList.contains("level-5")) {
return 5;
} else if (classList.contains("level-6")) {
return 6;
}
return -1;
}
/**
* Decorate an HTML element that is part of the quiet outline with a heading decorator.
*
* @param element The HTML element to decorate.
* @param content The content to decorate with.
* @param opacity The opacity of the decorator.
* @param position The position of the decorator.
*/
export function decorateQuietOutlineElement(
element: HTMLElement,
content: string,
opacity: OpacityOptions,
position: PostionOptions
): void {
if (content) {
const nodeContent = element.querySelector<HTMLElement>(
".n-tree-node-content"
);
if (nodeContent) {
nodeContent.dataset.headingDecorator = content;
nodeContent.dataset.decoratorOpacity = `${opacity}%`;
nodeContent.classList.add(
quietOutlineHeadingDecoratorClassName,
getPositionClassName(position, true)
);
}
}
}
/**
* Cancel the decorator from an HTML element that is part of the quiet outline.
*
* @param element The HTML element to cancel the decorator.
*/
export function cancelQuietOutlineDecorator(element: HTMLElement): void {
const nodeContent = element.querySelector<HTMLElement>(
".n-tree-node-content"
);
if (nodeContent) {
delete nodeContent.dataset.headingDecorator;
delete nodeContent.dataset.decoratorOpacity;
nodeContent.classList.remove(
quietOutlineHeadingDecoratorClassName,
beforeDecoratorClassName,
afterDecoratorClassName
);
}
}
/**
* Get the level of a file heading item based on its margin-left value.
*

173
components/quiet-outline.ts Normal file
View file

@ -0,0 +1,173 @@
import type { HeadingDecoratorSettings } from "../common/data";
import {
quietOutlineHeadingDecoratorClassName,
quietOutlineContainerClassName,
beforeDecoratorClassName,
afterDecoratorClassName,
getOrderedCustomIdents,
getUnorderedLevelHeadings,
} from "../common/data";
import { Querier, Counter } from "../common/counter";
export function quietOutlineHandler(
settings: HeadingDecoratorSettings,
container: HTMLElement,
headingELements: NodeListOf<HTMLElement>
): void {
const {
opacity,
position,
ordered,
orderedDelimiter,
orderedTrailingDelimiter,
orderedStyleType,
orderedSpecifiedString,
orderedCustomIdents,
orderedIgnoreSingle,
orderedIgnoreMaximum = 6,
orderedBasedOnExisting,
orderedAllowZeroLevel,
unorderedLevelHeadings,
} = settings;
container.classList.add(quietOutlineContainerClassName);
let ignoreTopLevel = 0;
if (ordered && (orderedIgnoreSingle || orderedBasedOnExisting)) {
const queier = new Querier(orderedAllowZeroLevel);
for (const eleIndex in headingELements) {
const level = queryHeadingLevelByQuietOutlineElement(
headingELements[eleIndex]
);
queier.handler(level);
ignoreTopLevel = queier.query(orderedIgnoreSingle, orderedIgnoreMaximum);
if (ignoreTopLevel === 0) {
break;
}
}
}
const counter = new Counter({
ordered,
delimiter: orderedDelimiter,
trailingDelimiter: orderedTrailingDelimiter,
styleType: orderedStyleType,
customIdents: getOrderedCustomIdents(orderedCustomIdents),
specifiedString: orderedSpecifiedString,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
levelHeadings: getUnorderedLevelHeadings(unorderedLevelHeadings),
});
headingELements.forEach((headingEle) => {
const level = queryHeadingLevelByQuietOutlineElement(headingEle);
const decoratorContent = counter.decorator(level);
decorateQuietOutlineElement(
headingEle,
decoratorContent,
opacity,
position
);
});
}
/**
* Cancel the decoration for all headings in a container.
*
* @param container The container element that contains the headings.
*/
export function cancelQuietOutlineDecoration(container: HTMLElement): void {
if (container.classList.contains(quietOutlineContainerClassName)) {
container.classList.remove(quietOutlineContainerClassName);
const headingElements =
container.querySelectorAll<HTMLElement>(".n-tree-node");
headingElements.forEach((ele) => {
cancelQuietOutlineDecorator(ele);
});
}
}
/**
* Query the heading level of an HTML element that is part of the quiet outline.
*
* @param element
* @returns The heading level or `-1` if not found.
*/
function queryHeadingLevelByQuietOutlineElement(element: HTMLElement): number {
const classList = element.classList;
if (classList.contains("level-1")) {
return 1;
} else if (classList.contains("level-2")) {
return 2;
} else if (classList.contains("level-3")) {
return 3;
} else if (classList.contains("level-4")) {
return 4;
} else if (classList.contains("level-5")) {
return 5;
} else if (classList.contains("level-6")) {
return 6;
}
return -1;
}
/**
* Decorate an HTML element that is part of the quiet outline with a heading decorator.
*
* @param element The HTML element to decorate.
* @param content The content to decorate with.
* @param opacity The opacity of the decorator.
* @param position The position of the decorator.
*/
function decorateQuietOutlineElement(
element: HTMLElement,
content: string,
opacity: OpacityOptions,
position: PostionOptions
): void {
if (content) {
const nodeContent = element.querySelector<HTMLElement>(
".n-tree-node-content"
);
if (nodeContent) {
nodeContent.dataset.headingDecorator = content;
nodeContent.dataset.decoratorOpacity = `${opacity}%`;
//? Remove potential residual class names
nodeContent.classList.remove(
position === "after"
? beforeDecoratorClassName
: afterDecoratorClassName
);
nodeContent.classList.add(
quietOutlineHeadingDecoratorClassName,
position === "after"
? afterDecoratorClassName
: beforeDecoratorClassName
);
}
}
}
/**
* Cancel the decorator from an HTML element that is part of the quiet outline.
*
* @param element The HTML element to cancel the decorator.
*/
function cancelQuietOutlineDecorator(element: HTMLElement): void {
const nodeContent = element.querySelector<HTMLElement>(
".n-tree-node-content"
);
if (nodeContent) {
delete nodeContent.dataset.headingDecorator;
delete nodeContent.dataset.decoratorOpacity;
nodeContent.classList.remove(
quietOutlineHeadingDecoratorClassName,
beforeDecoratorClassName,
afterDecoratorClassName
);
}
}

95
main.ts
View file

@ -41,9 +41,6 @@ import {
getTreeItemText,
getFileHeadingItemLevel,
compareHeadingText,
queryHeadingLevelByQuietOutlineElement,
decorateQuietOutlineElement,
cancelQuietOutlineDecorator,
} from "./common/dom";
import {
HeadingViewPlugin,
@ -53,6 +50,10 @@ import {
} from "./components/view";
import { ViewChildComponent } from "./components/child";
import { FolderSuggest } from "./components/suggest";
import {
quietOutlineHandler,
cancelQuietOutlineDecoration,
} from "./components/quiet-outline";
interface ObsidianEditor extends Editor {
cm: EditorView;
@ -611,6 +612,12 @@ export default class HeadingPlugin extends Plugin {
view,
viewContent,
() => {
const containerElement =
viewContent.querySelector<HTMLElement>(".n-tree");
if (!containerElement) {
return;
}
const headingELements =
viewContent.querySelectorAll<HTMLElement>(".n-tree-node");
if (headingELements.length === 0) {
@ -633,82 +640,38 @@ export default class HeadingPlugin extends Plugin {
frontmatter
);
const {
enabledInEachNote,
opacity,
position,
ordered,
orderedDelimiter,
orderedTrailingDelimiter,
orderedStyleType,
orderedSpecifiedString,
orderedCustomIdents,
orderedIgnoreSingle,
orderedIgnoreMaximum = 6,
orderedBasedOnExisting,
orderedAllowZeroLevel,
unorderedLevelHeadings,
} = this.settings.quietOutlineSettings;
const { enabledInEachNote } = this.settings.quietOutlineSettings;
let enabled = true;
if (metadataEnabled == null) {
if (enabledInEachNote != undefined && !enabledInEachNote) {
return;
enabled = false;
}
if (this.getEnabledFromBlacklist(file.path)) {
return;
enabled = false;
}
} else if (!metadataEnabled) {
enabled = false;
}
if (!enabled) {
cancelQuietOutlineDecoration(containerElement);
return;
}
let ignoreTopLevel = 0;
if (ordered && (orderedIgnoreSingle || orderedBasedOnExisting)) {
const queier = new Querier(orderedAllowZeroLevel);
for (const eleIndex in headingELements) {
const level = queryHeadingLevelByQuietOutlineElement(
headingELements[eleIndex]
);
queier.handler(level);
ignoreTopLevel = queier.query(
orderedIgnoreSingle,
orderedIgnoreMaximum
);
if (ignoreTopLevel === 0) {
break;
}
}
}
const counter = new Counter({
ordered,
delimiter: orderedDelimiter,
trailingDelimiter: orderedTrailingDelimiter,
styleType: orderedStyleType,
customIdents: getOrderedCustomIdents(orderedCustomIdents),
specifiedString: orderedSpecifiedString,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
levelHeadings: getUnorderedLevelHeadings(unorderedLevelHeadings),
});
headingELements.forEach((headingEle) => {
const level = queryHeadingLevelByQuietOutlineElement(headingEle);
const decoratorContent = counter.decorator(level);
decorateQuietOutlineElement(
headingEle,
decoratorContent,
opacity,
position
);
});
quietOutlineHandler(
this.settings.quietOutlineSettings,
containerElement,
headingELements
);
},
() => {
const headingElements =
viewContent.querySelectorAll<HTMLElement>(".n-tree-node");
headingElements.forEach((ele) => {
cancelQuietOutlineDecorator(ele);
});
const containerElement =
viewContent.querySelector<HTMLElement>(".n-tree");
if (containerElement) {
cancelQuietOutlineDecoration(containerElement);
}
}
);