feat(wysiwyg): Add language selection for code blocks, block settings menu, fix cursor navigation, and update demo to use dees-panel

- Added modal to select programming language when creating code blocks
- Added settings button (3 dots) on each block for configuration
- Fixed cursor not jumping to new block after pressing Enter
- Special handling for code blocks: Enter creates new line, Shift+Enter creates new block
- Display language indicator on code blocks
- Updated demo to use dees-panel instead of demo-section divs
- Added language selection in block settings modal for existing code blocks
This commit is contained in:
2025-06-23 21:28:58 +00:00
parent 302777feff
commit 7ce282c500
4 changed files with 284 additions and 59 deletions

View File

@@ -1,5 +1,6 @@
import { DeesInputBase } from '../dees-input-base.js';
import { demoFunc } from '../dees-input-wysiwyg.demo.js';
import { DeesModal } from '../dees-modal.js';
import {
customElement,
@@ -204,6 +205,22 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
onCompositionEnd: () => this.isComposing = false,
onMouseUp: (e: MouseEvent) => this.handleTextSelection(e),
})}
${block.type !== 'divider' ? html`
<div
class="block-settings"
@click="${(e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
this.showBlockSettingsModal(block);
}}"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<circle cx="12" cy="5" r="2"></circle>
<circle cx="12" cy="12" r="2"></circle>
<circle cx="12" cy="19" r="2"></circle>
</svg>
</div>
` : ''}
</div>
`;
}
@@ -315,6 +332,23 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
this.updateValue();
this.requestUpdate();
return;
} else if (detectedType.type === 'code') {
// For code blocks, ask for language
this.showLanguageSelectionModal().then(language => {
if (language) {
block.type = 'code';
block.content = '';
block.metadata = { language };
// Clear the DOM element immediately
target.textContent = '';
// Force update
this.updateValue();
this.requestUpdate();
}
});
return;
} else {
// For all other block types
block.type = detectedType.type;
@@ -638,11 +672,22 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
}
}
private insertBlock(type: IBlock['type']) {
private async insertBlock(type: IBlock['type']) {
const currentBlockIndex = this.blocks.findIndex(b => b.id === this.selectedBlockId);
const currentBlock = this.blocks[currentBlockIndex];
if (currentBlock && currentBlock.content.startsWith('/')) {
// If it's a code block, ask for language
if (type === 'code') {
const language = await this.showLanguageSelectionModal();
if (!language) {
// User cancelled
this.closeSlashMenu();
return;
}
currentBlock.metadata = { language };
}
currentBlock.type = type;
currentBlock.content = '';
@@ -1027,4 +1072,142 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
}
}, 10);
}
private async showLanguageSelectionModal(): Promise<string | null> {
return new Promise((resolve) => {
let selectedLanguage: string | null = null;
DeesModal.createAndShow({
heading: 'Select Programming Language',
content: html`
<style>
.language-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
padding: 16px;
}
.language-button {
padding: 12px;
background: var(--dees-color-box);
border: 1px solid var(--dees-color-line-bright);
border-radius: 4px;
cursor: pointer;
text-align: center;
transition: all 0.2s;
}
.language-button:hover {
background: var(--dees-color-box-highlight);
border-color: var(--dees-color-primary);
}
</style>
<div class="language-grid">
${['JavaScript', 'TypeScript', 'Python', 'Java', 'C++', 'C#', 'Go', 'Rust', 'HTML', 'CSS', 'SQL', 'Shell', 'JSON', 'YAML', 'Markdown', 'Plain Text'].map(lang => html`
<div class="language-button" @click="${(e: MouseEvent) => {
selectedLanguage = lang.toLowerCase();
// Find and click the hidden OK button to close the modal
const modal = (e.target as HTMLElement).closest('dees-modal');
if (modal) {
const okButton = modal.shadowRoot?.querySelector('.bottomButton.ok') as HTMLElement;
if (okButton) okButton.click();
}
}}">${lang}</div>
`)}
</div>
`,
menuOptions: [
{
name: 'Cancel',
action: async (modal) => {
modal.destroy();
resolve(null);
}
},
{
name: 'OK',
action: async (modal) => {
modal.destroy();
resolve(selectedLanguage);
}
}
]
});
});
}
private async showBlockSettingsModal(block: IBlock): Promise<void> {
let content: TemplateResult;
if (block.type === 'code') {
const currentLanguage = block.metadata?.language || 'plain text';
content = html`
<style>
.settings-section {
margin-bottom: 16px;
}
.settings-label {
font-weight: 500;
margin-bottom: 8px;
}
.language-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.language-button {
padding: 8px;
background: var(--dees-color-box);
border: 1px solid var(--dees-color-line-bright);
border-radius: 4px;
cursor: pointer;
text-align: center;
transition: all 0.2s;
}
.language-button:hover {
background: var(--dees-color-box-highlight);
border-color: var(--dees-color-primary);
}
.language-button.selected {
background: var(--dees-color-primary);
color: white;
}
</style>
<div class="settings-section">
<div class="settings-label">Programming Language</div>
<div class="language-grid">
${['JavaScript', 'TypeScript', 'Python', 'Java', 'C++', 'C#', 'Go', 'Rust', 'HTML', 'CSS', 'SQL', 'Shell', 'JSON', 'YAML', 'Markdown', 'Plain Text'].map(lang => html`
<div class="language-button ${currentLanguage === lang.toLowerCase() ? 'selected' : ''}"
@click="${(e: MouseEvent) => {
if (!block.metadata) block.metadata = {};
block.metadata.language = lang.toLowerCase();
this.updateValue();
this.requestUpdate();
// Find and click the close button
const modal = (e.target as HTMLElement).closest('dees-modal');
if (modal) {
const closeButton = modal.shadowRoot?.querySelector('.bottomButton') as HTMLElement;
if (closeButton) closeButton.click();
}
}}">${lang}</div>
`)}
</div>
</div>
`;
} else {
content = html`<div style="padding: 16px;">No settings available for this block type.</div>`;
}
DeesModal.createAndShow({
heading: 'Block Settings',
content,
menuOptions: [
{
name: 'Close',
action: async (modal) => {
modal.destroy();
}
}
]
});
}
}