mirror of
https://github.com/groldsf/obsidian_check_plugin.git
synced 2026-07-22 05:37:48 +00:00
refactor(settings): Make createFragmentWithHTML an instance method
This commit is contained in:
parent
f3f0685bdc
commit
8ac4b60509
2 changed files with 131 additions and 78 deletions
|
|
@ -25,4 +25,16 @@ export abstract class BaseSettingComponent implements ISettingComponent {
|
|||
public setChangeListener(listener: () => void): void {
|
||||
this.onChangeCallback = listener;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DocumentFragment from an HTML string.
|
||||
* Useful for setting complex descriptions with HTML content.
|
||||
* @param html The HTML string.
|
||||
* @returns A DocumentFragment containing the parsed HTML.
|
||||
*/
|
||||
protected createFragmentWithHTML(html: string): DocumentFragment {
|
||||
return createFragment((documentFragment) => {
|
||||
documentFragment.createDiv().innerHTML = html;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,89 +3,130 @@ import { CheckboxSyncPluginSettings } from 'src/types';
|
|||
import { BaseSettingComponent } from 'src/ui/basedClasses/BaseSettingComponent';
|
||||
import { ValidationError } from 'src/ui/validation/types';
|
||||
|
||||
|
||||
export class PathGlobsSettingComponent extends BaseSettingComponent {
|
||||
private textInput: TextAreaComponent;
|
||||
private textInput: TextAreaComponent;
|
||||
|
||||
getSettingKey(): keyof CheckboxSyncPluginSettings {
|
||||
return 'pathGlobs';
|
||||
}
|
||||
getSettingKey(): keyof CheckboxSyncPluginSettings {
|
||||
return 'pathGlobs';
|
||||
}
|
||||
|
||||
private arrayToMultilineString(value: string[] | undefined | null): string {
|
||||
if (Array.isArray(value)) {
|
||||
try {
|
||||
return value.join('\n');
|
||||
} catch (e) {
|
||||
console.error(`[${this.getSettingKey()}] Error joining array to multiline string: `, e);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
private arrayToMultilineString(value: string[] | undefined | null): string {
|
||||
if (Array.isArray(value)) {
|
||||
try {
|
||||
return value.join('\n');
|
||||
} catch (e) {
|
||||
console.error(`[${this.getSettingKey()}] Error joining array to multiline string: `, e);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
render(container: HTMLElement, currentValue: any): void {
|
||||
const multilineStringValue = this.arrayToMultilineString(currentValue as string[] | undefined);
|
||||
render(container: HTMLElement, currentValue: any): void {
|
||||
const multilineStringValue = this.arrayToMultilineString(currentValue as string[] | undefined);
|
||||
|
||||
this.setting = new Setting(container)
|
||||
.setName('Ignored Files & Folders')
|
||||
.setDesc('List files or folders that the plugin should IGNORE. ' +
|
||||
'This uses "glob patterns" (similar to .gitignore syntax) to match paths.\n' +
|
||||
'- Each pattern on a new line. If this list is empty, no files are ignored (plugin processes all).\n' +
|
||||
'- Example to IGNORE: `My Folder/` or `*.log`\n' +
|
||||
'- Example to PROCESS an item within an IGNORED path: `!My Folder/My Important File.md`\n' +
|
||||
'- The last rule that matches a file determines its fate.'
|
||||
)
|
||||
.addTextArea(text => {
|
||||
this.textInput = text;
|
||||
text.setPlaceholder(
|
||||
'# Lines starting with # are comments and will be ignored\n' +
|
||||
'Drafts/\n' +
|
||||
'*.tmp\n' +
|
||||
'!Drafts/ReadyToPublish.md\n\n' +
|
||||
'# To process ONLY files in "Projects" folder:\n' +
|
||||
'*\n' +
|
||||
'!Projects/**'
|
||||
);
|
||||
text.inputEl.setAttr('rows', 10); // Увеличил немного для плейсхолдера
|
||||
text.inputEl.style.width = '100%';
|
||||
|
||||
text.setValue(multilineStringValue);
|
||||
|
||||
text.onChange(() => {
|
||||
this.onChangeCallback();
|
||||
});
|
||||
});
|
||||
}
|
||||
const descHtml =
|
||||
`<p>List files or folders that the plugin should IGNORE.
|
||||
This uses "glob patterns" (similar to <code>.gitignore</code> syntax) to match paths.</p>
|
||||
<ul>
|
||||
<li>Each pattern on a new line. If this list is empty, no files are ignored (plugin processes all).</li>
|
||||
<li>Example to IGNORE: <code>My Folder/</code> or <code>*.log</code></li>
|
||||
<li>Example to PROCESS an item within an IGNORED path: <code>!My Folder/My Important File.md</code></li>
|
||||
<li>The last rule that matches a file determines its fate.</li>
|
||||
<li>For more on glob syntax, search "glob patterns online" or see
|
||||
<a href="https://en.wikipedia.org/wiki/Glob_(programming)" target="_blank" rel="noopener noreferrer">Glob (programming) on Wikipedia</a>.
|
||||
</li>
|
||||
</ul>`;
|
||||
|
||||
getValueFromUi(): string[] {
|
||||
if (this.textInput) {
|
||||
const rawValue = this.textInput.getValue();
|
||||
return rawValue
|
||||
.split('\n')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s.length > 0 && !s.startsWith('#')); // Игнорируем пустые строки и комментарии
|
||||
}
|
||||
throw new Error(`[${this.getSettingKey()}] Cannot get value from UI: TextArea component not rendered or available.`);
|
||||
}
|
||||
this.setting = new Setting(container)
|
||||
.setName('Ignored Files & Folders')
|
||||
// Используем createFragmentWithHTML для описания
|
||||
.setDesc(this.createFragmentWithHTML(descHtml))
|
||||
.addTextArea(text => {
|
||||
this.textInput = text;
|
||||
text.setPlaceholder(
|
||||
'# Lines starting with # are comments and will be ignored\n' +
|
||||
'Drafts/\n' +
|
||||
'*.tmp\n' +
|
||||
'!Drafts/ReadyToPublish.md\n\n' +
|
||||
'# To process ONLY files in "Projects" folder:\n' +
|
||||
'*\n' +
|
||||
'!Projects/**'
|
||||
);
|
||||
|
||||
setValueInUi(value: any): void {
|
||||
if (this.textInput) {
|
||||
if (value === undefined) {
|
||||
this.textInput.setValue('');
|
||||
return;
|
||||
}
|
||||
// Ожидаем, что value это string[]. Если нет, будет ошибка времени выполнения при .join()
|
||||
this.textInput.setValue((value as string[]).join('\n'));
|
||||
} else {
|
||||
throw new Error(`[${this.getSettingKey()}] Cannot set value in UI: TextArea component not rendered or available.`);
|
||||
}
|
||||
}
|
||||
text.setValue(multilineStringValue);
|
||||
|
||||
validate(): ValidationError | null {
|
||||
try {
|
||||
this.getValueFromUi();
|
||||
} catch (e: any) {
|
||||
return { field: this.getSettingKey(), message: e.message };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
text.onChange(() => {
|
||||
this.onChangeCallback();
|
||||
});
|
||||
});
|
||||
|
||||
// Применяем стиль "многострочной настройки" как в Tasks плагине
|
||||
this.makeMultilineTextSetting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Изменяет стиль Setting, чтобы описание было над TextArea, а TextArea занимала всю ширину.
|
||||
*/
|
||||
private makeMultilineTextSetting(): void {
|
||||
if (!this.setting) return;
|
||||
|
||||
const { settingEl, infoEl, controlEl } = this.setting;
|
||||
const textAreaEl: HTMLTextAreaElement | null = controlEl.querySelector('textarea');
|
||||
|
||||
if (textAreaEl === null) {
|
||||
// Это не настройка с TextArea, ничего не делаем
|
||||
return;
|
||||
}
|
||||
|
||||
settingEl.style.display = 'block';
|
||||
// infoEl может не существовать, если .setName() и .setDesc() не вызывались или были очищены.
|
||||
// В нашем случае они вызываются, так что infoEl должен быть.
|
||||
if (infoEl) {
|
||||
infoEl.style.marginBottom = 'var(--size-4-2)'; // Небольшой отступ под описанием
|
||||
infoEl.style.marginRight = '0px'; // Убираем отступ справа, если он был
|
||||
}
|
||||
|
||||
// Для controlEl (контейнер для textarea)
|
||||
controlEl.style.width = '100%'; // Заставляем контейнер контрола быть на всю ширину
|
||||
|
||||
// Для самой textarea
|
||||
textAreaEl.style.width = '100%'; // Занимает всю ширину controlEl
|
||||
textAreaEl.style.minHeight = '120px'; // Минимальная высота для нескольких строк
|
||||
textAreaEl.rows = 8;
|
||||
}
|
||||
|
||||
|
||||
getValueFromUi(): string[] {
|
||||
if (this.textInput) {
|
||||
const rawValue = this.textInput.getValue();
|
||||
return rawValue
|
||||
.split('\n')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s.length > 0 && !s.startsWith('#'));
|
||||
}
|
||||
throw new Error(`[${this.getSettingKey()}] Cannot get value from UI: TextArea component not rendered or available.`);
|
||||
}
|
||||
|
||||
setValueInUi(value: any): void {
|
||||
if (this.textInput) {
|
||||
if (value === undefined) {
|
||||
this.textInput.setValue('');
|
||||
return;
|
||||
}
|
||||
this.textInput.setValue((value as string[]).join('\n'));
|
||||
} else {
|
||||
throw new Error(`[${this.getSettingKey()}] Cannot set value in UI: TextArea component not rendered or available.`);
|
||||
}
|
||||
}
|
||||
|
||||
validate(): ValidationError | null {
|
||||
try {
|
||||
this.getValueFromUi();
|
||||
} catch (e: any) {
|
||||
return { field: this.getSettingKey(), message: e.message };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue