mirror of
https://github.com/kunalja/VariantEditor.git
synced 2026-07-22 13:00:32 +00:00
relative position the modal attempt 1
This commit is contained in:
parent
83bce6b663
commit
19864160e9
3 changed files with 122 additions and 40 deletions
52
src/main.ts
52
src/main.ts
|
|
@ -268,32 +268,38 @@ export default class VariantEditor extends Plugin {
|
|||
this.app.workspace.updateOptions();
|
||||
|
||||
// Open the text input modal after highlighting with the original text
|
||||
new TextInputModal(this.app, selectedWord, (variantText: string, activeIndex?: number, commitVariant?: boolean) => {
|
||||
if (variantText && variantText.trim()) {
|
||||
if (commitVariant) {
|
||||
// If committing, just replace with the selected variant text directly
|
||||
editor.replaceRange(variantText, from, to);
|
||||
new Notice(`Committed variant: "${variantText}"`);
|
||||
} else {
|
||||
// Create the variant syntax: {{original | variant1 | variant2}}^n
|
||||
const variants = variantText.split('|').map(v => v.trim()).filter(v => v);
|
||||
|
||||
// Ensure we have at least one variant (the original text)
|
||||
if (variants.length > 0) {
|
||||
// Use the provided activeIndex or default to 0
|
||||
const activeIdx = typeof activeIndex === 'number' ? activeIndex : 0;
|
||||
const variantSyntax = `{{${variants.join(' | ')}}}^${activeIdx}`;
|
||||
new TextInputModal(
|
||||
this.app,
|
||||
selectedWord,
|
||||
(variantText: string, activeIndex?: number, commitVariant?: boolean) => {
|
||||
if (variantText && variantText.trim()) {
|
||||
if (commitVariant) {
|
||||
// If committing, just replace with the selected variant text directly
|
||||
editor.replaceRange(variantText, from, to);
|
||||
new Notice(`Committed variant: "${variantText}"`);
|
||||
} else {
|
||||
// Create the variant syntax: {{original | variant1 | variant2}}^n
|
||||
const variants = variantText.split('|').map(v => v.trim()).filter(v => v);
|
||||
|
||||
// Replace the selected text with the variant syntax
|
||||
editor.replaceRange(variantSyntax, from, to);
|
||||
new Notice(`Created variant with ${variants.length} options (${variants[activeIdx]} active)`);
|
||||
// Ensure we have at least one variant (the original text)
|
||||
if (variants.length > 0) {
|
||||
// Use the provided activeIndex or default to 0
|
||||
const activeIdx = typeof activeIndex === 'number' ? activeIndex : 0;
|
||||
const variantSyntax = `{{${variants.join(' | ')}}}^${activeIdx}`;
|
||||
|
||||
// Replace the selected text with the variant syntax
|
||||
editor.replaceRange(variantSyntax, from, to);
|
||||
new Notice(`Created variant with ${variants.length} options (${variants[activeIdx]} active)`);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear highlighting after creating variant
|
||||
this.clearHighlight();
|
||||
}
|
||||
|
||||
// Clear highlighting after creating variant
|
||||
this.clearHighlight();
|
||||
}
|
||||
}).open();
|
||||
},
|
||||
// Pass the cursor position to position the modal relative to the highlighted text
|
||||
from
|
||||
).open();
|
||||
|
||||
} catch (e) {
|
||||
console.error('Error in highlightSelection:', e);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { App, Modal, Setting, ButtonComponent } from 'obsidian';
|
||||
import { App, Modal, Setting, ButtonComponent, EditorPosition } from 'obsidian';
|
||||
|
||||
/**
|
||||
* Modal for text input that appears after highlighting
|
||||
|
|
@ -10,17 +10,34 @@ export class TextInputModal extends Modal {
|
|||
private onSubmit: (result: string, activeIndex?: number, commitVariant?: boolean) => void;
|
||||
private originalText: string;
|
||||
private variantContainer: HTMLElement;
|
||||
private cursorPosition: EditorPosition | null;
|
||||
|
||||
constructor(app: App, originalText: string, onSubmit: (result: string, activeIndex?: number, commitVariant?: boolean) => void) {
|
||||
constructor(
|
||||
app: App,
|
||||
originalText: string,
|
||||
onSubmit: (result: string, activeIndex?: number, commitVariant?: boolean) => void,
|
||||
cursorPosition: EditorPosition | null = null
|
||||
) {
|
||||
super(app);
|
||||
this.originalText = originalText;
|
||||
this.onSubmit = onSubmit;
|
||||
// Initialize with the original text as the first variant
|
||||
this.variants = [originalText];
|
||||
this.cursorPosition = cursorPosition;
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
const {contentEl, modalEl} = this;
|
||||
|
||||
// Add a class for styling
|
||||
modalEl.addClass('variant-editor-modal');
|
||||
|
||||
// Position the modal relative to the cursor if we have cursor position
|
||||
if (this.cursorPosition) {
|
||||
// We need to position the modal after it's rendered
|
||||
setTimeout(() => {
|
||||
this.positionModalRelativeToCursor(modalEl);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
contentEl.createEl('h2', {text: 'Create Variants'});
|
||||
|
||||
|
|
@ -100,25 +117,73 @@ export class TextInputModal extends Modal {
|
|||
.map((v, i) => ({ text: v.trim(), originalIndex: i }))
|
||||
.filter(v => v.text.length > 0);
|
||||
|
||||
if (nonEmptyVariantsWithIndices.length > 0) {
|
||||
// Find the new index of the active variant after filtering
|
||||
let newActiveIndex = 0;
|
||||
for (let i = 0; i < nonEmptyVariantsWithIndices.length; i++) {
|
||||
if (nonEmptyVariantsWithIndices[i].originalIndex === this.activeVariantIndex) {
|
||||
newActiveIndex = i;
|
||||
break;
|
||||
if (nonEmptyVariantsWithIndices.length >= 2) {
|
||||
// Find the new index of the active variant after filtering
|
||||
let newActiveIndex = 0;
|
||||
for (let i = 0; i < nonEmptyVariantsWithIndices.length; i++) {
|
||||
if (nonEmptyVariantsWithIndices[i].originalIndex === this.activeVariantIndex) {
|
||||
newActiveIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Join variants with pipe character for the expected format and pass the corrected active index
|
||||
const nonEmptyVariants = nonEmptyVariantsWithIndices.map(v => v.text);
|
||||
this.onSubmit(nonEmptyVariants.join('|'), newActiveIndex);
|
||||
}
|
||||
|
||||
this.close();
|
||||
// Join variants with pipe character for the expected format and pass the corrected active index
|
||||
const nonEmptyVariants = nonEmptyVariantsWithIndices.map(v => v.text);
|
||||
this.onSubmit(nonEmptyVariants.join('|'), newActiveIndex);
|
||||
}
|
||||
})
|
||||
.buttonEl.addClass('variant-editor-button');
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions the modal relative to the cursor position
|
||||
*/
|
||||
private positionModalRelativeToCursor(modalEl: HTMLElement) {
|
||||
if (!this.cursorPosition) return;
|
||||
|
||||
// Get the active editor view
|
||||
const activeLeaf = this.app.workspace.activeLeaf;
|
||||
if (!activeLeaf || !activeLeaf.view) return;
|
||||
|
||||
// Get the editor element
|
||||
const editorEl = activeLeaf.view.containerEl.querySelector('.cm-editor');
|
||||
if (!editorEl) return;
|
||||
|
||||
// Find the line element for the cursor position
|
||||
const lineElements = editorEl.querySelectorAll('.cm-line');
|
||||
if (!lineElements || lineElements.length <= this.cursorPosition.line) return;
|
||||
|
||||
const lineEl = lineElements[this.cursorPosition.line];
|
||||
if (!lineEl) return;
|
||||
|
||||
// Get the position of the line element
|
||||
const lineRect = lineEl.getBoundingClientRect();
|
||||
|
||||
// Get the modal dimensions
|
||||
const modalRect = modalEl.getBoundingClientRect();
|
||||
|
||||
// Position the modal below the line with some padding
|
||||
const padding = 10;
|
||||
let top = lineRect.bottom + padding;
|
||||
|
||||
// Make sure the modal doesn't go off the bottom of the screen
|
||||
const viewportHeight = window.innerHeight;
|
||||
if (top + modalRect.height > viewportHeight) {
|
||||
// Position above the line instead
|
||||
top = lineRect.top - modalRect.height - padding;
|
||||
}
|
||||
|
||||
// Center horizontally relative to the line
|
||||
const left = lineRect.left + (lineRect.width / 2) - (modalRect.width / 2);
|
||||
|
||||
// Apply the position
|
||||
modalEl.style.position = 'fixed';
|
||||
modalEl.style.top = `${Math.max(0, top)}px`;
|
||||
modalEl.style.left = `${Math.max(0, left)}px`;
|
||||
modalEl.style.transform = 'none';
|
||||
}
|
||||
|
||||
private renderVariantInputs() {
|
||||
// Clear existing inputs
|
||||
this.variantContainer.empty();
|
||||
|
|
|
|||
13
styles.css
13
styles.css
|
|
@ -163,11 +163,22 @@
|
|||
}
|
||||
|
||||
.variant-editor-hint {
|
||||
margin-bottom: 15px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* Style the variant editor modal */
|
||||
.variant-editor-modal {
|
||||
width: 400px !important;
|
||||
max-width: 90vw !important;
|
||||
max-height: 80vh !important;
|
||||
overflow-y: auto !important;
|
||||
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15) !important;
|
||||
border: 1px solid var(--background-modifier-border) !important;
|
||||
}
|
||||
|
||||
.variant-editor-textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--interactive-accent);
|
||||
|
|
|
|||
Loading…
Reference in a new issue