diff --git a/changelog.md b/changelog.md index 19cf8ea..0c60a4e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # Changelog +## 2025-12-30 - 3.12.0 - feat(editor) +add code input component and editor-bare, replace dees-editor usage, and add modal contentPadding + +- Add new dees-input-code component (full-featured code editor input with modal, toolbar, language selector, copy and wrap toggles). +- Introduce dees-editor-bare component and remove the legacy dees-editor implementation; update editor markdown component to use dees-editor-bare. +- Export and include DeesInputCode in input index and include it in the unified form input types and dees-form usage. +- Add contentPadding property to DeesModal and apply it to the modal content area (configurable modal inner padding). +- Update element exports to point to dees-editor-bare and adjust related imports/usages. +- Bump devDependency @design.estate/dees-wcctools from ^3.3.0 to ^3.4.0 in package.json + ## 2025-12-30 - 3.11.2 - fix(tests) make WYSIWYG tests more robust and deterministic by initializing and attaching elements consistently, awaiting customElements/firstUpdated, adjusting selectors and assertions, and cleaning up DOM after tests diff --git a/package.json b/package.json index 5c704dc..8eb4f04 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "xterm-addon-fit": "^0.8.0" }, "devDependencies": { - "@design.estate/dees-wcctools": "^3.3.0", + "@design.estate/dees-wcctools": "^3.4.0", "@git.zone/tsbuild": "^4.0.2", "@git.zone/tsbundle": "^2.6.3", "@git.zone/tstest": "^3.1.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62c13a4..5431f85 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,8 +88,8 @@ importers: version: 0.8.0(xterm@5.3.0) devDependencies: '@design.estate/dees-wcctools': - specifier: ^3.3.0 - version: 3.3.0 + specifier: ^3.4.0 + version: 3.4.0 '@git.zone/tsbuild': specifier: ^4.0.2 version: 4.0.2 @@ -337,8 +337,8 @@ packages: '@design.estate/dees-wcctools@1.3.0': resolution: {integrity: sha512-+yd8c1gTIKNRQYCvG0xu6Am8dHsRm7ymluX2gnoBQN4aFOpZgIBi/v9CvGyPhTD1p/VRouIBz1wsUCejnwrFCA==} - '@design.estate/dees-wcctools@3.3.0': - resolution: {integrity: sha512-ZOxG5LkbLLsqDQWO+JCOjFkL77l9FuLDa7LBuZRkTSX0jRoYG6ICI1UoI9i6twxm4JKSzQ4iHjL/F5mHbQiKTg==} + '@design.estate/dees-wcctools@3.4.0': + resolution: {integrity: sha512-B263qJxK1Ob5ZmC+qj/utiuKZvdewIO6WwTfrTKF3X0Y24pcxoJVwJsDDcJID4kRd44EcNU9CP0FfWD2uYX9GQ==} '@emnapi/core@1.7.1': resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} @@ -4605,7 +4605,7 @@ snapshots: - supports-color - vue - '@design.estate/dees-wcctools@3.3.0': + '@design.estate/dees-wcctools@3.4.0': dependencies: '@design.estate/dees-domtools': 2.3.6 '@design.estate/dees-element': 2.1.3 diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index aef0078..e25bc28 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@design.estate/dees-catalog', - version: '3.11.2', + version: '3.12.0', description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.' } diff --git a/ts_web/elements/00group-editor/dees-editor/dees-editor.ts b/ts_web/elements/00group-editor/dees-editor-bare/dees-editor-bare.ts similarity index 88% rename from ts_web/elements/00group-editor/dees-editor/dees-editor.ts rename to ts_web/elements/00group-editor/dees-editor-bare/dees-editor-bare.ts index 9b72e96..036381c 100644 --- a/ts_web/elements/00group-editor/dees-editor/dees-editor.ts +++ b/ts_web/elements/00group-editor/dees-editor-bare/dees-editor-bare.ts @@ -15,14 +15,14 @@ import type * as monaco from 'monaco-editor'; declare global { interface HTMLElementTagNameMap { - 'dees-editor': DeesEditor; + 'dees-editor-bare': DeesEditorBare; } } -@customElement('dees-editor') -export class DeesEditor extends DeesElement { +@customElement('dees-editor-bare') +export class DeesEditorBare extends DeesElement { // DEMO - public static demo = () => html` `; + public static demo = () => html` `; // STATIC public static monacoDeferred: ReturnType; @@ -86,17 +86,17 @@ export class DeesEditor extends DeesElement { const container = this.shadowRoot.getElementById('container'); const monacoCdnBase = `https://cdn.jsdelivr.net/npm/monaco-editor@${MONACO_VERSION}`; - if (!DeesEditor.monacoDeferred) { - DeesEditor.monacoDeferred = domtools.plugins.smartpromise.defer(); + if (!DeesEditorBare.monacoDeferred) { + DeesEditorBare.monacoDeferred = domtools.plugins.smartpromise.defer(); const scriptUrl = `${monacoCdnBase}/min/vs/loader.js`; const script = document.createElement('script'); script.src = scriptUrl; script.onload = () => { - DeesEditor.monacoDeferred.resolve(); + DeesEditorBare.monacoDeferred.resolve(); }; document.head.appendChild(script); } - await DeesEditor.monacoDeferred.promise; + await DeesEditorBare.monacoDeferred.promise; (window as any).require.config({ paths: { vs: `${monacoCdnBase}/min/vs` }, diff --git a/ts_web/elements/00group-editor/dees-editor-bare/index.ts b/ts_web/elements/00group-editor/dees-editor-bare/index.ts new file mode 100644 index 0000000..91e0709 --- /dev/null +++ b/ts_web/elements/00group-editor/dees-editor-bare/index.ts @@ -0,0 +1 @@ +export * from './dees-editor-bare.js'; diff --git a/ts_web/elements/00group-editor/dees-editor/version.ts b/ts_web/elements/00group-editor/dees-editor-bare/version.ts similarity index 100% rename from ts_web/elements/00group-editor/dees-editor/version.ts rename to ts_web/elements/00group-editor/dees-editor-bare/version.ts diff --git a/ts_web/elements/00group-editor/dees-editor-markdown/dees-editor-markdown.ts b/ts_web/elements/00group-editor/dees-editor-markdown/dees-editor-markdown.ts index dc1b0a2..88add08 100644 --- a/ts_web/elements/00group-editor/dees-editor-markdown/dees-editor-markdown.ts +++ b/ts_web/elements/00group-editor/dees-editor-markdown/dees-editor-markdown.ts @@ -9,6 +9,7 @@ import { domtools } from '@design.estate/dees-element'; import { themeDefaultStyles } from '../../00theme.js'; +import { DeesEditorBare } from '../dees-editor-bare/dees-editor-bare.js'; const deferred = domtools.plugins.smartpromise.defer(); @@ -51,7 +52,7 @@ export class DeesEditorMarkdown extends DeesElement { return html`
- + >
@@ -86,8 +87,8 @@ const hello = 'yes' public async firstUpdated(_changedPropertiesArg) { await super.firstUpdated(_changedPropertiesArg); - const editor = this.shadowRoot.querySelector('dees-editor'); - + const editor = this.shadowRoot.querySelector('dees-editor-bare') as DeesEditorBare; + // lets care about wiring the markdown stuff. const markdownOutlet = this.shadowRoot.querySelector('dees-editormarkdownoutlet'); const smartmarkdownInstance = new domtools.plugins.smartmarkdown.SmartMarkdown(); diff --git a/ts_web/elements/00group-editor/dees-editor/index.ts b/ts_web/elements/00group-editor/dees-editor/index.ts deleted file mode 100644 index 91af439..0000000 --- a/ts_web/elements/00group-editor/dees-editor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './dees-editor.js'; diff --git a/ts_web/elements/00group-editor/index.ts b/ts_web/elements/00group-editor/index.ts index 86c7202..4fd543f 100644 --- a/ts_web/elements/00group-editor/index.ts +++ b/ts_web/elements/00group-editor/index.ts @@ -1,4 +1,4 @@ // Editor Components -export * from './dees-editor/index.js'; +export * from './dees-editor-bare/index.js'; export * from './dees-editor-markdown/index.js'; export * from './dees-editor-markdownoutlet/index.js'; diff --git a/ts_web/elements/00group-form/dees-form/dees-form.ts b/ts_web/elements/00group-form/dees-form/dees-form.ts index ad842d4..faef009 100644 --- a/ts_web/elements/00group-form/dees-form/dees-form.ts +++ b/ts_web/elements/00group-form/dees-form/dees-form.ts @@ -9,6 +9,7 @@ import { import * as domtools from '@design.estate/dees-domtools'; import { DeesInputCheckbox } from '../../00group-input/dees-input-checkbox/dees-input-checkbox.js'; +import { DeesInputCode } from '../../00group-input/dees-input-code/dees-input-code.js'; import { DeesInputDatepicker } from '../../00group-input/dees-input-datepicker/index.js'; import { DeesInputText } from '../../00group-input/dees-input-text/dees-input-text.js'; import { DeesInputQuantitySelector } from '../../00group-input/dees-input-quantityselector/dees-input-quantityselector.js'; @@ -26,6 +27,7 @@ import { demoFunc } from './dees-form.demo.js'; // Unified set for form input types const FORM_INPUT_TYPES = [ DeesInputCheckbox, + DeesInputCode, DeesInputDatepicker, DeesInputDropdown, DeesInputFileupload, @@ -41,6 +43,7 @@ const FORM_INPUT_TYPES = [ export type TFormInputElement = | DeesInputCheckbox + | DeesInputCode | DeesInputDatepicker | DeesInputDropdown | DeesInputFileupload diff --git a/ts_web/elements/00group-input/dees-input-code/dees-input-code.ts b/ts_web/elements/00group-input/dees-input-code/dees-input-code.ts new file mode 100644 index 0000000..8565462 --- /dev/null +++ b/ts_web/elements/00group-input/dees-input-code/dees-input-code.ts @@ -0,0 +1,719 @@ +import { DeesInputBase } from '../dees-input-base/dees-input-base.js'; +import { + customElement, + type TemplateResult, + property, + html, + cssManager, + css, + state, +} from '@design.estate/dees-element'; +import { themeDefaultStyles } from '../../00theme.js'; +import { DeesModal } from '../../dees-modal/dees-modal.js'; +import '../../dees-icon/dees-icon.js'; +import '../../dees-label/dees-label.js'; +import '../../00group-editor/dees-editor-bare/dees-editor-bare.js'; +import { DeesEditorBare } from '../../00group-editor/dees-editor-bare/dees-editor-bare.js'; + +declare global { + interface HTMLElementTagNameMap { + 'dees-input-code': DeesInputCode; + } +} + +// Common programming languages for the language selector +const LANGUAGES = [ + { key: 'typescript', label: 'TypeScript' }, + { key: 'javascript', label: 'JavaScript' }, + { key: 'json', label: 'JSON' }, + { key: 'html', label: 'HTML' }, + { key: 'css', label: 'CSS' }, + { key: 'scss', label: 'SCSS' }, + { key: 'markdown', label: 'Markdown' }, + { key: 'yaml', label: 'YAML' }, + { key: 'xml', label: 'XML' }, + { key: 'sql', label: 'SQL' }, + { key: 'python', label: 'Python' }, + { key: 'java', label: 'Java' }, + { key: 'csharp', label: 'C#' }, + { key: 'cpp', label: 'C++' }, + { key: 'go', label: 'Go' }, + { key: 'rust', label: 'Rust' }, + { key: 'shell', label: 'Shell' }, + { key: 'plaintext', label: 'Plain Text' }, +]; + +@customElement('dees-input-code') +export class DeesInputCode extends DeesInputBase { + public static demo = () => html` + + `; + + // INSTANCE + @property({ type: String }) + accessor value: string = ''; + + @property({ type: String }) + accessor language: string = 'typescript'; + + @property({ type: String }) + accessor height: string = '200px'; + + @property({ type: String }) + accessor wordWrap: 'on' | 'off' = 'off'; + + @property({ type: Boolean }) + accessor showLineNumbers: boolean = true; + + @state() + accessor isLanguageDropdownOpen: boolean = false; + + @state() + accessor copySuccess: boolean = false; + + private editorElement: DeesEditorBare | null = null; + + public static styles = [ + themeDefaultStyles, + ...DeesInputBase.baseStyles, + cssManager.defaultStyles, + css` + * { + box-sizing: border-box; + } + + :host { + display: block; + } + + .code-container { + border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + border-radius: 6px; + overflow: hidden; + background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 9%)')}; + } + + .toolbar { + 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%)')}; + gap: 8px; + } + + .toolbar-left { + display: flex; + align-items: center; + gap: 8px; + } + + .toolbar-right { + display: flex; + align-items: center; + gap: 4px; + } + + .language-selector { + position: relative; + } + + .language-button { + display: flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + font-size: 12px; + font-weight: 500; + background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 12%)')}; + border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 20%)')}; + border-radius: 4px; + cursor: pointer; + color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')}; + transition: all 0.15s ease; + } + + .language-button:hover { + background: ${cssManager.bdTheme('hsl(0 0% 95%)', 'hsl(0 0% 15%)')}; + } + + .language-dropdown { + position: absolute; + top: 100%; + left: 0; + margin-top: 4px; + background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 9%)')}; + border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 20%)')}; + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + z-index: 100; + max-height: 250px; + overflow-y: auto; + min-width: 140px; + } + + .language-option { + padding: 8px 12px; + font-size: 12px; + cursor: pointer; + color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')}; + transition: background 0.15s ease; + } + + .language-option:hover { + background: ${cssManager.bdTheme('hsl(0 0% 95%)', 'hsl(0 0% 15%)')}; + } + + .language-option.selected { + background: ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(0 0% 20%)')}; + } + + .toolbar-button { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + background: transparent; + border: none; + border-radius: 4px; + cursor: pointer; + color: ${cssManager.bdTheme('hsl(0 0% 45%)', 'hsl(0 0% 60%)')}; + transition: all 0.15s ease; + } + + .toolbar-button:hover { + background: ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(0 0% 15%)')}; + color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')}; + } + + .toolbar-button.active { + background: ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')}; + color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')}; + } + + .toolbar-button.success { + color: hsl(142.1 76.2% 36.3%); + } + + .editor-wrapper { + position: relative; + } + + dees-editor-bare { + display: block; + } + + .toolbar-divider { + width: 1px; + height: 20px; + background: ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')}; + margin: 0 4px; + } + + :host([disabled]) .code-container { + opacity: 0.5; + pointer-events: none; + } + `, + ]; + + public render(): TemplateResult { + const currentLanguage = LANGUAGES.find(l => l.key === this.language) || LANGUAGES[0]; + + return html` + +
+ +
+
+
+
+ + ${this.isLanguageDropdownOpen ? html` +
+ ${LANGUAGES.map(lang => html` +
this.selectLanguage(e, lang.key)} + > + ${lang.label} +
+ `)} +
+ ` : ''} +
+
+
+ + +
+ + +
+
+
+ +
+
+
+ `; + } + + async firstUpdated() { + this.editorElement = this.shadowRoot?.querySelector('dees-editor-bare') as DeesEditorBare; + if (this.editorElement) { + // Subscribe to content changes from the editor + this.editorElement.contentSubject.subscribe((newContent: string) => { + if (this.value !== newContent) { + this.value = newContent; + this.changeSubject.next(this as any); + } + }); + } + } + + private toggleLanguageDropdown() { + this.isLanguageDropdownOpen = !this.isLanguageDropdownOpen; + } + + private handleLanguageBlur() { + // Small delay to allow click events on dropdown items + setTimeout(() => { + this.isLanguageDropdownOpen = false; + }, 150); + } + + private async selectLanguage(e: Event, languageKey: string) { + e.preventDefault(); + this.language = languageKey; + this.isLanguageDropdownOpen = false; + + // Update the editor language + if (this.editorElement) { + this.editorElement.language = languageKey; + const editor = await this.editorElement.editorDeferred.promise; + const model = editor.getModel(); + if (model) { + (window as any).monaco.editor.setModelLanguage(model, languageKey); + } + } + } + + private toggleWordWrap() { + this.wordWrap = this.wordWrap === 'on' ? 'off' : 'on'; + this.updateEditorOption('wordWrap', this.wordWrap); + } + + private toggleLineNumbers() { + this.showLineNumbers = !this.showLineNumbers; + this.updateEditorOption('lineNumbers', this.showLineNumbers ? 'on' : 'off'); + } + + private async updateEditorOption(option: string, value: any) { + if (this.editorElement) { + const editor = await this.editorElement.editorDeferred.promise; + editor.updateOptions({ [option]: value }); + } + } + + private async copyCode() { + try { + await navigator.clipboard.writeText(this.value); + this.copySuccess = true; + setTimeout(() => { + this.copySuccess = false; + }, 2000); + } catch (err) { + console.error('Failed to copy code:', err); + } + } + + private handleContentChange(e: CustomEvent) { + const newContent = e.detail; + if (this.value !== newContent) { + this.value = newContent; + this.changeSubject.next(this as any); + } + } + + public async openFullscreen() { + const currentValue = this.value; + let modalEditorElement: DeesEditorBare | null = null; + + // Modal-specific state + let modalLanguage = this.language; + let modalWordWrap = this.wordWrap; + let modalShowLineNumbers = this.showLineNumbers; + let modalLanguageDropdownOpen = false; + let modalCopySuccess = false; + + // Helper to get current language label + const getLanguageLabel = () => { + const lang = LANGUAGES.find(l => l.key === modalLanguage); + return lang ? lang.label : 'TypeScript'; + }; + + // Helper to update toolbar UI + const updateToolbarUI = (modal: DeesModal) => { + 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'); + } + + // Update line numbers button + const linesBtn = toolbar.querySelector('.lines-btn') as HTMLElement; + 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) { + copyBtn.classList.toggle('success', modalCopySuccess); + 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'; + } + }; + + const modal = await DeesModal.createAndShow({ + heading: this.label || 'Code Editor', + width: 'fullscreen', + contentPadding: 0, + content: html` + + + + `, + menuOptions: [ + { + name: 'Cancel', + action: async (modalRef) => { + await modalRef.destroy(); + }, + }, + { + name: 'Save & Close', + action: async (modalRef) => { + // Get the editor content from the modal + modalEditorElement = modalRef.shadowRoot?.querySelector('dees-editor-bare') as DeesEditorBare; + if (modalEditorElement) { + const editor = await modalEditorElement.editorDeferred.promise; + const newValue = editor.getValue(); + this.setValue(newValue); + } + await modalRef.destroy(); + }, + }, + ], + }); + + // Wait for modal to render + await new Promise(resolve => setTimeout(resolve, 100)); + modalEditorElement = modal.shadowRoot?.querySelector('dees-editor-bare') as DeesEditorBare; + + // 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 () => { + const newLang = (option as HTMLElement).dataset.lang; + if (newLang && modalEditorElement) { + 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); + } + + // Update selected state + langOptions.forEach(opt => opt.classList.remove('selected')); + option.classList.add('selected'); + + updateToolbarUI(modal); + } + }); + }); + + // Word wrap button + const wrapBtn = toolbar.querySelector('.wrap-btn'); + wrapBtn?.addEventListener('click', async () => { + modalWordWrap = modalWordWrap === 'on' ? 'off' : 'on'; + if (modalEditorElement) { + const editor = await modalEditorElement.editorDeferred.promise; + editor.updateOptions({ wordWrap: modalWordWrap }); + } + updateToolbarUI(modal); + }); + + // Line numbers button + const linesBtn = toolbar.querySelector('.lines-btn'); + linesBtn?.addEventListener('click', async () => { + modalShowLineNumbers = !modalShowLineNumbers; + if (modalEditorElement) { + const editor = await modalEditorElement.editorDeferred.promise; + editor.updateOptions({ lineNumbers: modalShowLineNumbers ? 'on' : 'off' }); + } + updateToolbarUI(modal); + }); + + // Copy button + const copyBtn = toolbar.querySelector('.copy-btn'); + copyBtn?.addEventListener('click', async () => { + if (modalEditorElement) { + const editor = await modalEditorElement.editorDeferred.promise; + const content = editor.getValue(); + try { + await navigator.clipboard.writeText(content); + modalCopySuccess = true; + updateToolbarUI(modal); + setTimeout(() => { + modalCopySuccess = false; + updateToolbarUI(modal); + }, 2000); + } catch (err) { + console.error('Failed to copy code:', err); + } + } + }); + + // Close dropdown when clicking outside + document.addEventListener('click', (e) => { + if (modalLanguageDropdownOpen && !langBtn?.contains(e.target as Node)) { + modalLanguageDropdownOpen = false; + updateToolbarUI(modal); + } + }, { once: true }); + } + } + + public getValue(): string { + return this.value; + } + + public setValue(value: string): void { + this.value = value; + if (this.editorElement) { + this.editorElement.content = value; + // Also update the Monaco editor directly if it's already loaded + this.editorElement.editorDeferred.promise.then(editor => { + if (editor.getValue() !== value) { + editor.setValue(value); + } + }); + } + this.changeSubject.next(this as any); + } +} diff --git a/ts_web/elements/00group-input/dees-input-code/index.ts b/ts_web/elements/00group-input/dees-input-code/index.ts new file mode 100644 index 0000000..ca62f79 --- /dev/null +++ b/ts_web/elements/00group-input/dees-input-code/index.ts @@ -0,0 +1 @@ +export * from './dees-input-code.js'; diff --git a/ts_web/elements/00group-input/index.ts b/ts_web/elements/00group-input/index.ts index ab2eded..a4fcbdd 100644 --- a/ts_web/elements/00group-input/index.ts +++ b/ts_web/elements/00group-input/index.ts @@ -1,6 +1,7 @@ // Input Components export * from './dees-input-base/index.js'; export * from './dees-input-checkbox/index.js'; +export * from './dees-input-code/index.js'; export * from './dees-input-datepicker/index.js'; export * from './dees-input-dropdown/index.js'; export * from './dees-input-fileupload/index.js'; diff --git a/ts_web/elements/dees-modal/dees-modal.ts b/ts_web/elements/dees-modal/dees-modal.ts index 6d88d3d..8138a3a 100644 --- a/ts_web/elements/dees-modal/dees-modal.ts +++ b/ts_web/elements/dees-modal/dees-modal.ts @@ -45,6 +45,7 @@ export class DeesModal extends DeesElement { showHelpButton?: boolean; onHelp?: () => void | Promise; mobileFullscreen?: boolean; + contentPadding?: number; }) { const body = document.body; const modal = new DeesModal(); @@ -58,6 +59,7 @@ export class DeesModal extends DeesElement { if (optionsArg.showHelpButton !== undefined) modal.showHelpButton = optionsArg.showHelpButton; if (optionsArg.onHelp) modal.onHelp = optionsArg.onHelp; if (optionsArg.mobileFullscreen !== undefined) modal.mobileFullscreen = optionsArg.mobileFullscreen; + if (optionsArg.contentPadding !== undefined) modal.contentPadding = optionsArg.contentPadding; modal.windowLayer = await DeesWindowLayer.createAndShow({ blur: true, }); @@ -108,6 +110,9 @@ export class DeesModal extends DeesElement { @property({ type: Boolean }) accessor mobileFullscreen: boolean = false; + @property({ type: Number }) + accessor contentPadding: number = 16; + @state() accessor modalZIndex: number = 1000; @@ -272,7 +277,6 @@ export class DeesModal extends DeesElement { } .modal .content { - padding: 16px; flex: 1; overflow-y: auto; overflow-x: hidden; @@ -361,7 +365,7 @@ export class DeesModal extends DeesElement { ` : ''}
-
${this.content}
+
${this.content}
${this.menuOptions.length > 0 ? html`
${this.menuOptions.map(