feat(input): extract datepicker popup into a window-layer overlay and enhance the code editor modal status UI

This commit is contained in:
2026-04-05 00:24:12 +00:00
parent 976039798a
commit 9bfb6446af
8 changed files with 1005 additions and 918 deletions

View File

@@ -508,23 +508,15 @@ export class DeesInputCode extends DeesInputBase<string> {
const toolbar = modal.shadowRoot?.querySelector('.modal-toolbar');
if (!toolbar) return;
// Update language button text
const langBtn = toolbar.querySelector('.language-button span');
if (langBtn) langBtn.textContent = getLanguageLabel();
// Update word wrap button
const wrapBtn = toolbar.querySelector('.wrap-btn') as HTMLElement;
if (wrapBtn) {
wrapBtn.classList.toggle('active', modalWordWrap === 'on');
}
if (wrapBtn) wrapBtn.classList.toggle('active', modalWordWrap === 'on');
// Update line numbers button
const linesBtn = toolbar.querySelector('.lines-btn') as HTMLElement;
if (linesBtn) {
linesBtn.classList.toggle('active', modalShowLineNumbers);
}
if (linesBtn) linesBtn.classList.toggle('active', modalShowLineNumbers);
// Update copy button
const copyBtn = toolbar.querySelector('.copy-btn') as HTMLElement;
const copyIcon = copyBtn?.querySelector('dees-icon') as any;
if (copyBtn && copyIcon) {
@@ -532,13 +524,28 @@ export class DeesInputCode extends DeesInputBase<string> {
copyIcon.icon = modalCopySuccess ? 'lucide:Check' : 'lucide:Copy';
}
// Update dropdown visibility
const dropdown = toolbar.querySelector('.language-dropdown') as HTMLElement;
if (dropdown) {
dropdown.style.display = modalLanguageDropdownOpen ? 'block' : 'none';
}
if (dropdown) dropdown.style.display = modalLanguageDropdownOpen ? 'block' : 'none';
};
// Helper to update footer UI
const updateFooterUI = (modal: DeesModal) => {
const footer = modal.shadowRoot?.querySelector('.modal-footer');
if (!footer) return;
const cursorEl = footer.querySelector('.footer-cursor');
const linesEl = footer.querySelector('.footer-lines');
const langEl = footer.querySelector('.footer-lang');
if (cursorEl) cursorEl.textContent = `Ln ${modalCursorLine}, Col ${modalCursorCol}`;
if (linesEl) linesEl.textContent = `${modalLineCount} line${modalLineCount !== 1 ? 's' : ''}`;
if (langEl) langEl.textContent = getLanguageLabel();
};
let modalCursorLine = 1;
let modalCursorCol = 1;
let modalLineCount = currentValue.split('\n').length;
const modal = await DeesModal.createAndShow({
heading: this.label || 'Code Editor',
width: 'fullscreen',
@@ -549,9 +556,7 @@ export class DeesInputCode extends DeesInputBase<string> {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: ${cssManager.bdTheme('hsl(0 0% 97%)', 'hsl(0 0% 7%)')};
border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
padding: 4px 12px;
gap: 8px;
}
.modal-toolbar .toolbar-left {
@@ -644,9 +649,30 @@ export class DeesInputCode extends DeesInputBase<string> {
}
.modal-editor-wrapper {
position: relative;
height: calc(100vh - 175px);
height: calc(100vh - 200px);
width: 100%;
}
.modal-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
height: 28px;
font-size: 11px;
color: ${cssManager.bdTheme('hsl(0 0% 45%)', 'hsl(0 0% 55%)')};
border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
}
.modal-footer .footer-left,
.modal-footer .footer-right {
display: flex;
align-items: center;
gap: 12px;
}
.modal-footer .footer-separator {
width: 1px;
height: 12px;
background: ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')};
}
</style>
<div class="modal-toolbar">
<div class="toolbar-left">
@@ -687,6 +713,16 @@ export class DeesInputCode extends DeesInputBase<string> {
.wordWrap=${modalWordWrap}
></dees-workspace-monaco>
</div>
<div class="modal-footer">
<div class="footer-left">
<span class="footer-cursor">Ln ${modalCursorLine}, Col ${modalCursorCol}</span>
<div class="footer-separator"></div>
<span class="footer-lines">${modalLineCount} line${modalLineCount !== 1 ? 's' : ''}</span>
</div>
<div class="footer-right">
<span class="footer-lang">${getLanguageLabel()}</span>
</div>
</div>
`,
menuOptions: [
{
@@ -698,7 +734,6 @@ export class DeesInputCode extends DeesInputBase<string> {
{
name: 'Save & Close',
action: async (modalRef) => {
// Get the editor content from the modal
modalEditorElement = modalRef!.shadowRoot?.querySelector('dees-workspace-monaco') as DeesWorkspaceMonaco;
if (modalEditorElement) {
const editor = await modalEditorElement.editorDeferred.promise;
@@ -715,17 +750,61 @@ export class DeesInputCode extends DeesInputBase<string> {
await new Promise(resolve => setTimeout(resolve, 100));
modalEditorElement = modal.shadowRoot?.querySelector('dees-workspace-monaco') as DeesWorkspaceMonaco;
// Apply custom Monaco theme for matching background
if (modalEditorElement) {
const editor = await modalEditorElement.editorDeferred.promise;
const domtoolsInstance = await modalEditorElement.domtoolsPromise;
const applyModalTheme = (isBright: boolean) => {
const bg = isBright ? '#ffffff' : '#0a0a0a';
(window as any).monaco?.editor?.defineTheme?.('dees-light', {
base: 'vs',
inherit: true,
rules: [],
colors: { 'editor.background': bg },
});
(window as any).monaco?.editor?.defineTheme?.('dees-dark', {
base: 'vs-dark',
inherit: true,
rules: [],
colors: { 'editor.background': bg },
});
editor.updateOptions({ theme: isBright ? 'dees-light' : 'dees-dark' });
};
applyModalTheme(domtoolsInstance.themeManager.goBrightBoolean);
domtoolsInstance.themeManager.themeObservable.subscribe((goBright: boolean) => {
applyModalTheme(goBright);
});
// Track cursor position
editor.onDidChangeCursorPosition((e) => {
modalCursorLine = e.position.lineNumber;
modalCursorCol = e.position.column;
updateFooterUI(modal);
});
// Track line count
const model = editor.getModel();
if (model) {
modalLineCount = model.getLineCount();
updateFooterUI(modal);
model.onDidChangeContent(() => {
modalLineCount = model.getLineCount();
updateFooterUI(modal);
});
}
}
// Wire up toolbar event handlers
const toolbar = modal.shadowRoot?.querySelector('.modal-toolbar');
if (toolbar) {
// Language button click
const langBtn = toolbar.querySelector('.language-button');
langBtn?.addEventListener('click', () => {
modalLanguageDropdownOpen = !modalLanguageDropdownOpen;
updateToolbarUI(modal);
});
// Language option clicks
const langOptions = toolbar.querySelectorAll('.language-option');
langOptions.forEach((option) => {
option.addEventListener('click', async () => {
@@ -734,23 +813,21 @@ export class DeesInputCode extends DeesInputBase<string> {
modalLanguage = newLang;
modalLanguageDropdownOpen = false;
// Update editor language
const editor = await modalEditorElement.editorDeferred.promise;
const model = editor.getModel();
if (model) {
(window as any).monaco.editor.setModelLanguage(model, newLang);
const editorModel = editor.getModel();
if (editorModel) {
(window as any).monaco.editor.setModelLanguage(editorModel, newLang);
}
// Update selected state
langOptions.forEach(opt => opt.classList.remove('selected'));
option.classList.add('selected');
updateToolbarUI(modal);
updateFooterUI(modal);
}
});
});
// Word wrap button
const wrapBtn = toolbar.querySelector('.wrap-btn');
wrapBtn?.addEventListener('click', async () => {
modalWordWrap = modalWordWrap === 'on' ? 'off' : 'on';
@@ -761,7 +838,6 @@ export class DeesInputCode extends DeesInputBase<string> {
updateToolbarUI(modal);
});
// Line numbers button
const linesBtn = toolbar.querySelector('.lines-btn');
linesBtn?.addEventListener('click', async () => {
modalShowLineNumbers = !modalShowLineNumbers;
@@ -772,7 +848,6 @@ export class DeesInputCode extends DeesInputBase<string> {
updateToolbarUI(modal);
});
// Copy button
const copyBtn = toolbar.querySelector('.copy-btn');
copyBtn?.addEventListener('click', async () => {
if (modalEditorElement) {
@@ -792,7 +867,6 @@ export class DeesInputCode extends DeesInputBase<string> {
}
});
// Close dropdown when clicking outside
document.addEventListener('click', (e) => {
if (modalLanguageDropdownOpen && !langBtn?.contains(e.target as Node)) {
modalLanguageDropdownOpen = false;