diff --git a/main.ts b/main.ts index 26c0c38..3739eb7 100644 --- a/main.ts +++ b/main.ts @@ -17,6 +17,13 @@ import { getObsidianFilesWithTagName, } from './src/services/obsidian.service'; import { renderPreviewFiles } from './src/components/RenderPreviewFiles'; +import { createBackslash } from './src/components/RegExpBackslash'; +import { + REGEXP_FLAGS, + RegExpFlag, + RegExpFlags, +} from './src/constants/RegExpFlags'; +import { RegExpFlagsSuggest } from './src/suggestions/RegExpFlagsSuggest'; interface BulkRenamePluginSettings { folderName: string; @@ -25,6 +32,10 @@ interface BulkRenamePluginSettings { replacePattern: string; tags: string[]; userRegExp: string; + regExpState: { + regExp: string; + flags: RegExpFlag[]; + }; viewType: 'tags' | 'folder' | 'regexp'; } @@ -34,6 +45,10 @@ const DEFAULT_SETTINGS: BulkRenamePluginSettings = { existingSymbol: '', replacePattern: '', userRegExp: '', + regExpState: { + regExp: '', + flags: [], + }, tags: [], viewType: 'folder', }; @@ -158,7 +173,7 @@ export class BulkRenameSettingsTab extends PluginSettingTab { .setName('Folder location') .setDesc('Find files within the folder') .addSearch((cb) => { - new FolderSuggest(this.app, cb.inputEl); + new FolderSuggest(this.app, cb.inputEl, this.plugin); cb.setPlaceholder('Example: folder1/') .setValue(this.plugin.settings.folderName) .onChange((newFolder) => { @@ -212,7 +227,9 @@ export class BulkRenameSettingsTab extends PluginSettingTab { new Setting(this.containerEl) .setName('RegExp') .setDesc('all files by titles will be found') - .addSearch((cb) => { + .addText((cb) => { + const backslash = createBackslash('/'); + cb.inputEl.insertAdjacentElement('beforebegin', backslash); // @ts-ignore cb.inputEl.addEventListener('keydown', (event) => { if (event.key !== 'Enter') { @@ -220,21 +237,40 @@ export class BulkRenameSettingsTab extends PluginSettingTab { } const target = event.target as HTMLInputElement; - this.plugin.settings.userRegExp = target.value; + this.plugin.settings.regExpState.regExp = target.value; this.plugin.saveSettings(); }); - cb.setPlaceholder('Example: #tag, #tag2') - .setValue(this.plugin.settings.userRegExp) + cb.setPlaceholder('Put your RegExp here') + .setValue(this.plugin.settings.regExpState.regExp) .onChange((newFolder) => { - this.plugin.settings.userRegExp = newFolder; + this.plugin.settings.regExpState.regExp = newFolder; this.plugin.saveSettings(); this.getFilesByRegExp(); }); // @ts-ignore - cb.containerEl.addClass('bulk_regexp'); - cb.inputEl.addClass('bulk_input'); + cb.inputEl.addClass('bulk_regexp'); cb.inputEl.onblur = this.reRenderPreview; - }); + }) + .addText((cb) => { + new RegExpFlagsSuggest(this.app, cb.inputEl, this.plugin); + const backslash = createBackslash('/'); + cb.inputEl.insertAdjacentElement('beforebegin', backslash); + cb.inputEl.addEventListener('keydown', (event) => { + // @ts-ignore + event.stopPropagation(); + event.stopImmediatePropagation(); + event.preventDefault(); + }); + cb.setPlaceholder('flags here') + // .setDisabled(true) + .setValue(this.plugin.settings.regExpState.flags.join('')) + .onChange((flag: RegExpFlag) => { + this.plugin.saveSettings(); + this.getFilesByRegExp(); + }); + cb.inputEl.addClass('bulk_regexp_flags'); + }) + .controlEl.addClass('bulk_regexp_control'); } renderReplaceSymbol() { diff --git a/src/components/RegExpBackslash.ts b/src/components/RegExpBackslash.ts new file mode 100644 index 0000000..1b5c42f --- /dev/null +++ b/src/components/RegExpBackslash.ts @@ -0,0 +1,6 @@ +export const createBackslash = (textContent = '=> => => =>') => { + const previewLabel = window.document.createElement('div'); + previewLabel.className = 'bulk_regexp_slash'; + previewLabel.textContent = textContent; + return previewLabel; +}; diff --git a/src/constants/RegExpFlags.ts b/src/constants/RegExpFlags.ts new file mode 100644 index 0000000..e44d0ff --- /dev/null +++ b/src/constants/RegExpFlags.ts @@ -0,0 +1,26 @@ +export type RegExpFlag = + | 'g' + | 'm' + | 'i' + | 'x' + | 's' + | 'u' + | 'U' + | 'A' + | 'J' + | 'D'; + +export const REGEXP_FLAGS = [ + 'g', + 'm', + 'i', + 'x', + 's', + 'u', + 'U', + 'A', + 'J', + 'D', +] as const; + +export type RegExpFlags = typeof REGEXP_FLAGS; diff --git a/src/suggestions/RegExpFlagsSuggest.ts b/src/suggestions/RegExpFlagsSuggest.ts new file mode 100644 index 0000000..eace9a5 --- /dev/null +++ b/src/suggestions/RegExpFlagsSuggest.ts @@ -0,0 +1,42 @@ +// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes + +import { TextInputSuggest } from './suggest'; +import { REGEXP_FLAGS, RegExpFlag } from '../constants/RegExpFlags'; + +export class RegExpFlagsSuggest extends TextInputSuggest { + // @ts-ignore TODO refactor types types + getSuggestions() { + return REGEXP_FLAGS; + } + + renderSuggestion = (flag: RegExpFlag, el: HTMLElement) => { + const { regExpState } = this.plugin.settings; + const hasFlag = regExpState.flags.includes(flag); + if (hasFlag) { + el.addClass('bulk-flag-selected'); + } else { + el.removeClass('bulk-flag-selected'); + } + el.setText(flag); + }; + + selectSuggestion = (flag: RegExpFlag, event: MouseEvent | KeyboardEvent) => { + const { regExpState } = this.plugin.settings; + const target = event.target as HTMLDivElement; + + const hasFlag = regExpState.flags.includes(flag); + if (hasFlag) { + regExpState.flags = regExpState.flags.filter((existingFlag) => { + return existingFlag !== flag; + }); + } else { + regExpState.flags = [...regExpState.flags, flag]; + } + target.classList.toggle('bulk-flag-selected'); + this.inputEl.value = regExpState.flags.join(''); + // this.inputEl.trigger('input'); + // this.settings.plugin.settings.regExpState.flags.push(file); + // this.inputEl.trigger('input'); + // this.close(); + }; +} diff --git a/src/suggestions/suggest.ts b/src/suggestions/suggest.ts index 53f69ad..3954250 100644 --- a/src/suggestions/suggest.ts +++ b/src/suggestions/suggest.ts @@ -2,6 +2,7 @@ import { App, ISuggestOwner, Scope } from 'obsidian'; import { createPopper, Instance as PopperInstance } from '@popperjs/core'; +import BulkRenamePlugin from '../../main'; const wrapAround = (value: number, size: number): number => { return ((value % size) + size) % size; @@ -104,6 +105,7 @@ class Suggest { export abstract class TextInputSuggest implements ISuggestOwner { protected app: App; + protected plugin: BulkRenamePlugin; protected inputEl: HTMLInputElement | HTMLTextAreaElement; private popper: PopperInstance; @@ -111,7 +113,12 @@ export abstract class TextInputSuggest implements ISuggestOwner { private suggestEl: HTMLElement; private suggest: Suggest; - constructor(app: App, inputEl: HTMLInputElement | HTMLTextAreaElement) { + constructor( + app: App, + inputEl: HTMLInputElement | HTMLTextAreaElement, + plugin: BulkRenamePlugin, + ) { + this.plugin = plugin; this.app = app; this.inputEl = inputEl; this.scope = new Scope(); @@ -191,7 +198,7 @@ export abstract class TextInputSuggest implements ISuggestOwner { this.suggestEl.detach(); } - abstract getSuggestions(inputStr: string): T[]; + abstract getSuggestions(inputStr?: string): T[]; abstract renderSuggestion(item: T, el: HTMLElement): void; - abstract selectSuggestion(item: T): void; + abstract selectSuggestion(item: T, evt: MouseEvent | KeyboardEvent): void; } diff --git a/styles.css b/styles.css index 0fa17f3..3210dbf 100644 --- a/styles.css +++ b/styles.css @@ -24,35 +24,28 @@ margin-right: 15px; } -.bulk_regexp { +.bulk_regexp_control { + border: 1px solid var(--background-modifier-border); +} + +.bulk_regexp_control > input { + border: none; +} + +.bulk_regexp, +.bulk_regexp_flags { width: 100%; } -.bulk_regexp > input { - color: green; - padding: 0 28px 0 14px; - font-size: 20px; - font-weight: 700; +.bulk_regexp_flags { + caret-color: transparent; } -.bulk_regexp:before { - content: '/'; - top: 2px; - left: -15px; +.bulk_regexp_slash { + font-size: 1.5em; + opacity: 0.5; } -.bulk_regexp:after { - content: '/'; - bottom: 5px; - right: -15px; -} - -.bulk_regexp:before, -.bulk_regexp:after { - color: #000080; - font-size: 30px; - /*background-color: #cccccc;*/ - display: block; - /*text-align: center;*/ - position: absolute; +.bulk-flag-selected { + background-color: lavender !important; }