mirror of
https://github.com/dragonish/obsidian-heading-decorator.git
synced 2026-07-22 05:42:05 +00:00
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:
parent
ab58b4f1a2
commit
c7657ab7d1
4 changed files with 205 additions and 144 deletions
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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
173
components/quiet-outline.ts
Normal 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
95
main.ts
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue